You are viewing a plain text version of this content. The canonical link for it is here.
Posted to asp@perl.apache.org by Brat Wizard <br...@naxs.com> on 2002/09/19 09:55:53 UTC

Yet more code (Was: Re: A nifty code sample to share)

On Wednesday 18 September 2002 01:10 pm, Josh Chamas spewed into the ether:
> Assuming that the data is available when the my:sub is starting
> to get processed, then you can use more XMLSubs to do your dirty work like:
>
> <% my $data_ref = \%data_hash; %>
> <my:sub data=$data_ref>
>     <my:value name='COST' fmt='%.2f' />
>     <my:value name='STATE' fmt='%uc2' />
> </my:sub>

I've been playing around with this some more and have this observation to make 
(among some others)-- what you have started here with XMLSubs is very nifty 
and offers the designer a large degree of abstraction ability (a la 
templating) in crufting up pages-- however, the one big weakness that I see 
is that all of of the ASP assisted bits-- nested subs, <% %> substitution, 
etc-- all happen in the PRE processing stage and not DURING the processing 
stage. A lot of what one wants to do doesn't come into scope until _during_ 
the processing.

For example, one use of this ability (and enabling a big win) would be in 
interpreting/rendering row data selected from a database. None of the nifty 
ASP-assist functions are at all useful in this context because it isn't until 
inside the iteration loop which is inside the my:sub function that this 
information first becomes available. 

Here is an (actual) example of the problem:

<table width="100%">
<my:pgloop sth=$sth>
  <fmt>PRICE:%.2f</fmt>
  <tr>
    <td><my:pimage pg="__PG__" sku="__SKU__" width="32"/></td>
    <td><a href="catalog/__PG__/__SKU__">__SKU__</a></td>
    <td>__USER1__</td>
    <td>__NAME__</td>
    <td align=right>__QTY_ONHAND__</td>
    <td align=right>__AMT_PRICE__</td>
  </tr>
</my:pgloop>
</table>

In this case, the 'my:pimage' sub is nested inside the 'my:pgloop' sub-- the 
statement handle '$sth' contains the selected DBI table rows, and the pgloop 
sub iterates the rows and subs the template for each row. The 'my:pimage' sub 
retrieves an image based on the product group and sku id. But in this case, 
the sku is not known until inside the pg:loop func. Intuitively (to me at 
least) the above is the logical way (and perhaps the only way) to construct 
the template. However, in real life, when I do this, ASP attempts to resolve 
the my:pimage sub before handing it off to the my:pgloop sub-- which is 
before any token-data is available to resolve the __TOKENS__. So processing 
(as written) works from the inside-out instead of outside-in, which imo would 
be the more intuitive and useful approach.

There might also be a compromise approach that's possible too- so that <% %> 
substitutions happen immediately before the sub, while __TOKEN__ values and 
nested xmlsubs would be processed after (during) being passed (ie. be delayed 
in resolution by one level of nesting so that any external references ie. 
__TOKENS__ or whatever could get resolved first).


> If you can end up with your document written entirely in XML,
> then you are on your way to having your data written entirely
> separately from your style, which is a good goal.  This is
> the XML/XSLT paradigm, but can be implemented with XHTML,
 XMLSubs, XSLT, you pick your favorite.

Yes, I'm doing my best to push my application in that direction. My current 
app involves an e-commerce platform which has to support a wide variety of 
styles and configurations. I am hoping to get to the point where the customer 
can supply the basic page with all the business logic accessible via special 
tags. So they get the control over design and layout while maintaining (but 
not being responsible for) the actual workings of the system.


> > B) The second issue is harder though and I still haven't thunk up a good
> > solution for it yet... and that is how to handle combined or calculated
> > post-processed fields-- ie
> >
> > <my:sub ...>
> > __VAR1__ times __VAR2__ = [product here somehow??]
> > </my:sub>
>
> Be careful... you are creating your own mini language here.

Heh- I hope not. I'm trying to stay away from that on purpose. But what I am 
discovering is that there needs to be formatting information, and also I had 
another idea before I went to sleep last night-- and that was the idea of 
tying a token to a well-known (somewhere-else predefined) rendering style...

<my:sub>
  <type>USER_TYPE:userlookup</type>
</my:sub>

And 'userlookup' would be a specific reference (either implicit or 
interpreted) to a renderer-- meaning it would tie the USER_TYPE token/data 
pair to a bit of code (function/whatever) that would render it in a way 
appropriate to a user lookup function-- however that happens to be defined.

A couple of years ago, I was working on a different (non-ASP) app where I 
needed to display a list of "who's on" along with related information about 
the user, where they were from, what they were doing, the time, and 
idle-time, and other information. In addition, there were several _very_ 
similar variations on this theme depending on who was asking, why they were 
asking, and what the general gist of the question was-- after writing a bunch 
of who's-on scripts, all slighly different but basically the same, I decided 
to create a more flexible approach and designed a single who's-on system that 
allowed predefined "fields", "renderers", and "reports" (stored in config 
files) that defined what a field was (where it came from, how it was used, 
etc) and this allowed me to create artificial fields through some transform 
of existing fields, and defined renderers that could be tied to a particular 
field (in the field definition), and finally reports which were collections 
of fields and rules regarding who could access the report and meta 
information such as report title and other "pretty" information.

When I first started, it seemed pretty daunting. But in the end, it turned out 
that it was an extremely workable system-- there were a few wrinkles that 
needed to be ironed-out and areas extended in order to ultimately make it all 
work the way it should. The code was easy to follow and most importantly made 
designing a new report a 10-minute process. Once the first few reports had 
been created, 99% of the field & renderers had already been defined so 
building a new report consisted mostly of choosing predefined fields and the 
context in which the report could be viewed.

While that bit of code was specific to the application I was working on at the 
time, the _idea_ of that code seems germain and relevant to what we're 
discussing here-- and is very similar to this whole templating thing we're 
talking about.



Another useful thing might be a <def> field (define) field that could 
construct artificial tokens based on expression-manipulation of existing data 
available in the dataset at time of rendering. That way you could have a cost 
field and apply a markup value to it and come up with an artificial, but 
usable price token.

<my:sub>
  <define>PRICE:COST*1.2</define>
 The cost of this item is __COST__ and it is currently selling for __PRICE__.
</my:sub>

Finally there is a need for some sort of simple if/then logic. This is where 
everything gets tricky. If you go to far with this concept then you cross the 
threshold of simple formatting and get into procedural aspects which should 
probably be avoided. There probably should never be any sort of iterative 
aspect. That seems definately over the line, and can be handled anyway by 
simply creating a my:sub that performs the iteration. But consider this 
situation:

Suppose you have a product table and some of the products in the table are on 
sale. When you retrieve the list of the items, you would like to apply one 
style if onsale, and another if not.

I think there oughta be some sort of facility for handling this type of 
formatting situation at least for the simplistic case-- though I quickly 
admit that if you add too much flexibility that it gets out of hand very 
quickly, and further I am at a loss to suggest a suitable style for such a 
framework.

Besides, at some point, if you alter enough of the basic template, you're not 
really saving yourself any work, which was the original objective, and 
arguably could be adding to your workload by adding additional crap to  
remember.



> You could do this instead:
>
> <% my $data_ref = \%$data_hash; %>
> <my:sub>
>    <%= $data_ref->{VAR1} * $data_ref->{VAR2} %>
> </my:sub>
>
> I caution against going down the create your own mini language
> path because it will end up being a language that only you know.

Definatelyt agreed. Trying to stick to formatting issues only.



> I would recommend more that if you want to inline a mini language,
> that you look into rendering inline a bit of a template toolkit
> or HTML::Template into your Apache::ASP

Actually I have looked at them and they seem overblown and needlessly 
cumbersome for what I want to do. I think you could get away with some very 
simple formatting rules, and perhaps some method to generate artificial 
tokens. Being able to define a renderer is a nice thing too but an extra. All 
of these things are issues the designer/webmaster needs to have access to in 
order to have the control over the creative aspect of the site. They do not 
need to concern themselves with the inner workings of the XMLSubs they use. 
They need only know that if you supply "these parameters" and in "this way" 
then "this will happen"-- very black box-ish, and html-like.


>> I guess perhaps there is a limit to how much one can cram
>> into this XMLSubs thing... ;)
>
> There is a limit, but I would like to break it.  I believe I can add
> a lot of power to XMLSubs, if I allow them to also be called at
> compile time as an extension, so that the ASP template being compiled
> could be modified instead of the output.  The runtime model has more
> limits, and the compile time model would only be added as an extension.
> I have been mulling these kinds of extensions to XMLSubs for a while,
> so do not hold your breath.

I don't hold my breath much anymore. I tend to faint a lot that way ;)


> If you really want your site to be fast, then precompile your scripts
> in the parent httpd.  The real performance hit your are saving yourself
> from it having to compile the scripts in every child httpd separately.
> Read here for more:
>
>    http://www.apache-asp.org/tuning.html#Precompile%20S36827838

Yup, I already do that and it works well. Also moving the .htaccess stuff into 
the apache config helps a lot too.


BTW- here is a better and improved version of the rendering function I 
submitted last time. It is more efficient because it cranks through the 
tokens it finds instead of all the ones available in the hash. The render 
function itself though is a drop-in replacement of the one I presented 
before. This snippet is capable of processing the my:sub examples I presented 
above (though you would have to finish wrapping the my:sub setup around it).


my %fmt = (); # for format view

## extract format (if any) - what's left becomes template string
$body =~ s/<fmt>(.*?)<\/fmt>//i;
$fmt = $1;

## scoop-up formats into a convenient hash
foreach (split(/,/, $fmt)) {
        my ($key, $val) = split(/:/, $_);
        $fmt{uc(eatwhite($key))} = eatwhite($val);
        }

foreach (@hash_list) { render($body, $_, \%fmt) }

## more efficient render func
sub render {
        my ($tmpl, $hash, $fmt) = @_;
        while ($tmpl =~ /__(.*?)__/) {
                my $TOKEN = $1; my $token = lc($TOKEN);
                if ((my $fmt = $$fmt{$TOKEN}) ne undef) {
                        # to-uppercase
                        if ($fmt =~ /^%uc/i) { $fmt =~ s/[^0-9]//gm; 
$$hash{$token} = uc(substr($$hash{$token}, 0, $fmt || 
length($$hash{$token}))); }
                        # to-lowercase
                        elsif ($fmt =~ /^%lc/i) { $fmt =~ s/[^0-9]//gm; 
$$hash{$token} = lc(substr($$hash{$token}, 0, $fmt || 
length($$hash{$token}))); }
                        # picture
                        else { $$hash{$token} = sprintf($fmt, $$hash{$token}); 
}
                        }
                $tmpl =~ s/__${TOKEN}__/$$hash{$token}/gme;
                }
        print $tmpl;
        }


__END__

BTW-- additional predefined formats can be added as desired. Some useful ones 
to add might include a telephone-number style renderer, a ssn style, a 
zipcode style (zip+4), an email style, a url style, etc. They could all be 
added in the if/then ladder where it is deciding what format to apply to the 
data. Also, if enough renderers are defined, or additional 
functionality/flexibility/or config options is desired, the whole formatting 
bit can be removed to its own subsystem in a style similar to the "who's-on" 
model I described above.


Here is a snippet of code from an actual my:sub (my:pgloop, used above) that 
makes use of this particular renderer:

sub my::pgloop { # iterate thru results set and process body block
        my ($args, $body) = @_;

        ## extract format (if any) - what's left becomes template string
        my %fmt = (); # hash for format view
        $body =~ s/<fmt>(.*?)<\/fmt>//i;
        my $fmt = $1;

        ## scoop-up formats into a convenient hash
        foreach (split(/,/, $fmt)) {
                my ($key, $val) = split(/:/, $_);
                $fmt{uc(eatwhite($key))} = eatwhite($val);
                }

        ## iterate data rows and render accordingly
        while (my $row = $$args{sth}->fetchrow_hashref('NAME_lc')) {
                ## render hash using template string and format hash
                render($body, $row, \%fmt);
                }
        }

__END__

This illustrates how a complete XMLSub function can be constructed using the 
snippets of code I've presented thus far. Simply substitute the guts of this 
function with the code snippets and make the minor adjustments so that the 
variables match up.


Finally I would like to present another snippet of code that I use in my 
applications. This time it is an embeddable search form that makes it very 
easy to stick a search widget just about anywhere.

sub my::search { # display a small, embeddable search form
        my ($args, $body) = @_;
        my $script = $$args{script} || 'catalog';
        my $class = qq{class="$$args{class}"} if ($$args{class} ne '');
        my $caption = qq{<td align=right $class>$body</td>} if ($body ne '');
        my $size = $$args{size} || 10;
        my $go_button = ($$args{go_button} ne '')?qq{&nbsp;<input type=image 
src
="$$args{go_button}" border=0>}:qq{ &nbsp;<input type=submit value="Go">};
        print << "EOF";
<table border=0 cellpadding=1 cellspacing=0>
<form method=GET action="/$storeid/$script">
<tr>
        $caption
        <td $class>
        &nbsp;<input type="text" name="search" value="-Search-" size=$size 
maxle
ngth=80 onfocus="if(this.value=='-Search-') {this.value='';}" 
onblur="if(this.va
lue=='') {this.value='-Search-';}">
        $go_button
        </font>
        </td>
</tr>
</form>
</table>
EOF
        }

__END__

To use this in an html/asp script, just do something like:

<my:search go_button="$themeurl/go_button.gif"/>

-or-

<my:search go_button="$themeurl/go_button.gif"><b>Search</b></my:search>

(Anything placed inside the tags becomes the caption for the search). Notice 
also that this includes some simple javascript to make the word 'search' 
appear inside the text box whenever the user has not typed anything in it. 
This makes it possible to use it in a very "small" footprint.


As always, I hope I'm helping someone. There's a lot of neat stuff you can do 
with Apache::ASP. Part of getting it widely-used is helping people to see how 
it can be used to solve real-world problems and offering real world examples 
to "steal" or get inspired by.



John


--------------------------------------------------------------------------------
Check out http://www.Wizard.Org for great deals on Electronic Parts
*NEW* Computer Parts & Accessories - Drives - LCD - Systems - Linux
--------------------------------------------------------------------------------
** Affordable Online Store w/Merchant Card Processing & Paypal **
Write to us: sales@wizard.org  --  Get your Store Online Today!
--------------------------------------------------------------------------------



---------------------------------------------------------------------
To unsubscribe, e-mail: asp-unsubscribe@perl.apache.org
For additional commands, e-mail: asp-help@perl.apache.org


Re: Yet more code (Was: Re: A nifty code sample to share)

Posted by Brat Wizard <br...@naxs.com>.
Yes, but extrordinarily easy-to-do and it didn't break nuthin ;)


On Friday 20 September 2002 02:27 pm, you muttered:
> Brat Wizard wrote:
> >                         # picture
> >                         else { $$hash{$token} = sprintf($fmt,
> > $$hash{$token}); }
> >                         }
> >                 $tmpl =~ s/__${TOKEN}__/$$hash{$token}/gme;
> >                 }
> >         $Response->Include(\$tmpl);
> >         }
>
> Now that is interesting.  You grabbed all the raw output, preprocessed
> it, and then ran executed it again, effectively creating a preprocessing
> model.  Tricky.  This gives something to think about...
>
> --Josh
> ________________________________________________________________
> Josh Chamas, Founder                   phone:925-552-0128
> Chamas Enterprises Inc.                http://www.chamas.com
> NodeWorks Link Checking                http://www.nodeworks.com

-- 

--------------------------------------------------------------------------------
Check out http://www.Wizard.Org for great deals on Electronic Parts
*NEW* Computer Parts & Accessories - Drives - LCD - Systems - Linux
--------------------------------------------------------------------------------
** Affordable Online Store w/Merchant Card Processing & Paypal **
Write to us: sales@wizard.org  --  Get your Store Online Today!
--------------------------------------------------------------------------------



---------------------------------------------------------------------
To unsubscribe, e-mail: asp-unsubscribe@perl.apache.org
For additional commands, e-mail: asp-help@perl.apache.org


Re: Yet more code (Was: Re: A nifty code sample to share)

Posted by Josh Chamas <jo...@chamas.com>.
Brat Wizard wrote:

>                         # picture
>                         else { $$hash{$token} = sprintf($fmt, $$hash{$token}); 
> }
>                         }
>                 $tmpl =~ s/__${TOKEN}__/$$hash{$token}/gme;
>                 }
>         $Response->Include(\$tmpl);
>         }
> 

Now that is interesting.  You grabbed all the raw output, preprocessed
it, and then ran executed it again, effectively creating a preprocessing
model.  Tricky.  This gives something to think about...

--Josh
________________________________________________________________
Josh Chamas, Founder                   phone:925-552-0128
Chamas Enterprises Inc.                http://www.chamas.com
NodeWorks Link Checking                http://www.nodeworks.com


---------------------------------------------------------------------
To unsubscribe, e-mail: asp-unsubscribe@perl.apache.org
For additional commands, e-mail: asp-help@perl.apache.org


Re: Yet more code (Was: Re: A nifty code sample to share)

Posted by Brat Wizard <br...@naxs.com>.
> Here is an (actual) example of the problem:
>
> <table width="100%">
> <my:pgloop sth=$sth>
>   <fmt>PRICE:%.2f</fmt>
>   <tr>
>     <td><my:pimage pg="__PG__" sku="__SKU__" width="32"/></td>
>     <td><a href="catalog/__PG__/__SKU__">__SKU__</a></td>
>     <td>__USER1__</td>
>     <td>__NAME__</td>
>     <td align=right>__QTY_ONHAND__</td>
>     <td align=right>__AMT_PRICE__</td>
>   </tr>
> </my:pgloop>
> </table>


Ta-Da! Here is the nifty solution... By slightly changing the template-- 
notice the 'my:pimage' xmlsub now has a different format using __[ ... ]__ to 
distinguish it instead (and the syntax is consistent with the __TOKEN__ 
style). This keeps it from getting processed before it gets passed to the 
first xmlsub (my:pgloop).
Then, in the render() func, add one more statement to change the __[ ]__ 
nomenclature back to < > and then pass it through $Response->Include() 
instead of printing it directly. This has the effect of substituting the 
parameters, fixing up the embedded xmlsubs, and then re-processing everything 
with the now-current data. By using $Response->Include to do it, the now 
correct xmlsubs will get processed with the substituted data.

<table width="100%">
<my:pgloop sth=$sth>
<fmt>PRICE:%.2f</fmt>
<tr>
        <td>__[my:pimage pg="__PG__" sku="__SKU__" width="32"/]__</td>
        <td><a href="catalog/__PG__/__SKU__"><u>__SKU__</u></a></td>
        <td>__USER1__</td>
        <td>__NAME__</td>
        <td align=right>__QTY_ONHAND__</td>
        <td align=right>__AMT_PRICE__</td>
</tr>
</my:pgloop>
</table>


The completed render() function looks like this:

sub render { ## process a body-template string and render appropriately
        my ($tmpl, $hash, $fmt) = @_;
        $tmpl =~ s/__\[(.*?)\]__/<$1>/gm; # fixup delayed-embedded xmlsubs
        while ($tmpl =~ /__(.*?)__/) {
                my $TOKEN = $1; my $token = lc($TOKEN);
                if ((my $fmt = $$fmt{$TOKEN}) ne undef) {
                        # to-uppercase
                        if ($fmt =~ /^%uc/i) { $fmt =~ s/[^0-9]//gm; 
$$hash{$token} = uc(substr($$hash{$token}, 0, $fmt || 
length($$hash{$token}))); }
                        # to-lowercase
                        elsif ($fmt =~ /^%lc/i) { $fmt =~ s/[^0-9]//gm; 
$$hash{$token} = lc(substr($$hash{$token}, 0, $fmt || 
length($$hash{$token}))); }
                        # picture
                        else { $$hash{$token} = sprintf($fmt, $$hash{$token}); 
}
                        }
                $tmpl =~ s/__${TOKEN}__/$$hash{$token}/gme;
                }
        $Response->Include(\$tmpl);
        }

__END__



--------------------------------------------------------------------------------
Check out http://www.Wizard.Org for great deals on Electronic Parts
*NEW* Computer Parts & Accessories - Drives - LCD - Systems - Linux
--------------------------------------------------------------------------------
** Affordable Online Store w/Merchant Card Processing & Paypal **
Write to us: sales@wizard.org  --  Get your Store Online Today!
--------------------------------------------------------------------------------



---------------------------------------------------------------------
To unsubscribe, e-mail: asp-unsubscribe@perl.apache.org
For additional commands, e-mail: asp-help@perl.apache.org


Re: Yet more code (Was: Re: A nifty code sample to share)

Posted by Brat Wizard <br...@naxs.com>.
A follow-up to my last post... adding this line to the render func:

$tmpl =~ s/__\((.*?)\)__/<%=($1)%>/gm; # fixup embedded tests

Allows this construct to be used:

__(__F_ONSALE__?'YES':'NO')__

These constructs also work as expected:

Item on sale? Yes or No:
__(__F_ONSALE__?'YES':'NO')__

Item NOT on sale? Yes or No:
__(!__F_ONSALE__?'YES':'NO')__

Price > 5? Yes or No
__(__AMT_PRICE__>5?'YES':'NO')__

Item on Sale? Sale Price or Reg Price:
__(__F_ONSALE__?__AMT_SALE__:__AMT_PRICE__)__

Here is the complete render func to date:

sub render { ## process a body-template string and render appropriately
        my ($tmpl, $hash, $fmt) = @_;
        $tmpl =~ s/__\[(.*?)\]__/<$1>/gm; # fixup delayed-embedded xmlsubs
        $tmpl =~ s/__\((.*?)\)__/<%=($1)%>/gm; # fixup embedded tests
        while ($tmpl =~ /__(.*?)__/) {
                my $TOKEN = $1; my $token = lc($TOKEN);
                if ((my $fmt = $$fmt{$TOKEN}) ne undef) {
                        # to-uppercase
                        if ($fmt =~ /^%uc/i) { $fmt =~ s/[^0-9]//gm; 
$$hash{$tok
en} = uc(substr($$hash{$token}, 0, $fmt || length($$hash{$token}))); }
                        # to-lowercase
                        elsif ($fmt =~ /^%lc/i) { $fmt =~ s/[^0-9]//gm; 
$$hash{$
token} = lc(substr($$hash{$token}, 0, $fmt || length($$hash{$token}))); }
                        # picture
                        else { $$hash{$token} = sprintf($fmt, $$hash{$token}); 
}
                        }
                $tmpl =~ s/__${TOKEN}__/$$hash{$token}/gme;
                }
        $Response->Include(\$tmpl);
        }

__END__


This is getting cool :) 

John





-- 

--------------------------------------------------------------------------------
Check out http://www.Wizard.Org for great deals on Electronic Parts
*NEW* Computer Parts & Accessories - Drives - LCD - Systems - Linux
--------------------------------------------------------------------------------
** Affordable Online Store w/Merchant Card Processing & Paypal **
Write to us: sales@wizard.org  --  Get your Store Online Today!
--------------------------------------------------------------------------------





---------------------------------------------------------------------
To unsubscribe, e-mail: asp-unsubscribe@perl.apache.org
For additional commands, e-mail: asp-help@perl.apache.org


Re: Yet more code (Was: Re: A nifty code sample to share)

Posted by Brat Wizard <br...@naxs.com>.
Of course, you could also put just the info to be included into a separate 
file and allow it to be included at a judicious moment... (nah, that would be 
too easy ;)

But the one thing that _doesn't_ allow for, and the reason why I myself have 
been working on the problem-- is putting together something you can let the 
end-user (the buyer of the account) create/customize/or otherwise abuse w/o 
worrying about compromising the sanctity of the system in the process. They 
can only supply tokens to be substituted and simple formatting directives, 
and not actualy code (although the ()?: structure I presented does allow 
actual code to be executed, something I would have to deal with somehow).



> I see the power of the preprocessing + execute output during post
> processing model.  The speed issue is the only one I have to get
> over here.

-- 

--------------------------------------------------------------------------------
Check out http://www.Wizard.Org for great deals on Electronic Parts
*NEW* Computer Parts & Accessories - Drives - LCD - Systems - Linux
--------------------------------------------------------------------------------
** Affordable Online Store w/Merchant Card Processing & Paypal **
Write to us: sales@wizard.org  --  Get your Store Online Today!
--------------------------------------------------------------------------------



---------------------------------------------------------------------
To unsubscribe, e-mail: asp-unsubscribe@perl.apache.org
For additional commands, e-mail: asp-help@perl.apache.org


Re: Yet more code (Was: Re: A nifty code sample to share)

Posted by Josh Chamas <jo...@chamas.com>.
Brat Wizard wrote:
 >
> Josh- why not make it a simple directive, like a pragma-- then it could be 
> specified inline by the end-user whenever (and if-ever) they need it one way 
> or the other.
> 
> Perhaps something like:
> 
> $Apache::ASP::XMLSubsPostProc = 1
>

Right.  I would never take away the current style/method of XMLSubs,
just add to it like you suggest.

> <my:sub>
>   <my:nestedsub><%=$blech%> __TOKEN1__</my:nestedsub>
>   <%=$foobar%> __TOKEN2__
> </my:sub>
> 
> You could wait until after the first my:sub had been called to interpret the 
> rest-- just process everything post. In my thinking __TOKEN1__ and __TOKEN2__ 
> would get resolved by the renderer function on the first pass and 
> <my:nestedsub>, $blech, and $foobar, would get processed immediately 
> afterwards-- after the tokens had been substituted.
> 
> How would that work out do you think?
>

So then the parent XMLSubs output would be then evaluated
at runtime as ASP script automatically by the system.  That
is an interesting way of looking at doing the pre-processing.

The only problem with this model is that runtime parsing/eval
of the code would be expensive.  The other preprocessing model
that I was considering would not do ANY inner code substitution
on its own, leaving this to the XMLSubs definition to do if
it wanted to.

I am not sure which one I would lean to more, but they are both
interesting models.

>>my $pgimage = My::PgImage->new(
>>	ASP => $asp,
>>	Attrib => { pg=>"__PG__" sku=>"__SKU__" },
>>	Parent => $pgloop
>>};
>>$pgimage->execute(\'');
>>$pgloop->execute(\$final_data_output_within);
> 
> 
> 
>>This pre/post processing model would allow pgimage to
>>work with $self->{Parent}{sth}, or pgloop could populate
>>its own data in this way.  Output from pgimage could be
>>trapped and inlined, passed into the execute() function
>>for pgloop.
> 
> 
> Hmm- sounds a little complicated. Not to mention you are exposing a lot of the 
> $asp guts to the programmer/user that way and the goal should be the exact 
> opposite-- to abstract that away from the programmer and toward the 
> web-monkey/end-user...?
> 
>

This was just an example of how the preprocessing model would
get executed.  This would be more for the XMLSubs definer to
worry about.  I ideally to the end user, things would just work.
I believe that XMLSubs will have to grow in complexity to be
able to do really doo things that other platform taglib functionality
offers.  I will be looking hard as JSP & AxKit before I do any
next generation XMLSubs for sure.

> 
> Actually I was thinking something very similar but for another reason-- if you 
> use delayed processing, like I describe above, conditionals actually get 
> pretty easy I think. You just build them into the my:sub "$body" text and let 
> the post-processing pick them up.
>

I see the power of the preprocessing + execute output during post
processing model.  The speed issue is the only one I have to get
over here.  I'll look at some taglib libraries that I would like
to implement in Apache::ASP & see what kind of power we need here.
It may be that some things are only possible with the kind of
power that you suggest, and its a very interesting idea giving
the XMLSubs programmer the ability to rewrite the code on the fly.

> All you need to do is make the one adjustment to handle <% %> subs post 
> instead of pre and I think the render function can handle the inbetween 
> stuff. The __TOKEN__ format is very comfortable and intuitive to me in this 
> instance. It seems very obvious that they are serving as placeholders for 
> yet-to-be-in-scope data. And by delaying the <% %> processing, you allow 
> substitution to occur _before_ conditional blocks take place.
> 
> 
> What is your take on all this? 
> 
> I think we've just descibed a very functional, useable processing paradigm 
> that fits with the current "feel" of the asp system, and with the single 
> exception of moving <% %> substitution to post-processing, you don't have to 
> make any other radical changes to the Apache::ASP framework or api (at least 
> I don't think you do).
> 
> Your thoughts?
> 

Pretty good stuff.  I'll definately be following up on this later
when I go to implement, so stay tuned.

Regards,

Josh
________________________________________________________________
Josh Chamas, Founder                   phone:925-552-0128
Chamas Enterprises Inc.                http://www.chamas.com
NodeWorks Link Checking                http://www.nodeworks.com


---------------------------------------------------------------------
To unsubscribe, e-mail: asp-unsubscribe@perl.apache.org
For additional commands, e-mail: asp-help@perl.apache.org


Re: Yet more code (Was: Re: A nifty code sample to share)

Posted by Brat Wizard <br...@naxs.com>.

> Right, its almost as if one would want a pre-processing stage of not the
> output, but of the content.  The current model was not built to be easily
> extendable to this as there is only one sub per XMLSubs that gets called,
> but what you would really want is something like:
>
> sub my::sub_pre()
> sub my::sub_post()

Josh- why not make it a simple directive, like a pragma-- then it could be 
specified inline by the end-user whenever (and if-ever) they need it one way 
or the other.

Perhaps something like:

$Apache::ASP::XMLSubsPostProc = 1

(or whatever)

And that would indicate to the system that it should enter post-processing 
mode. That way you would stay compatible with current code but allow the 
ability to be turned on/off as/if needed.

However, in thinking it through-- generally-speaking (while maybe not true in 
every case, for most cases it would be)-- things that would get substituted a 
la <% %> prior to calling the first my:sub are still available to be 
substituted afterwards as long as you get back to the original scope-context. 
I'm trying to think of a situation where it would be disadvantageous to delay 
substitution... nothing springs to mind right off but I don't want to say 
never either.

So if you had, say:

<my:sub>
  <my:nestedsub><%=$blech%> __TOKEN1__</my:nestedsub>
  <%=$foobar%> __TOKEN2__
</my:sub>

You could wait until after the first my:sub had been called to interpret the 
rest-- just process everything post. In my thinking __TOKEN1__ and __TOKEN2__ 
would get resolved by the renderer function on the first pass and 
<my:nestedsub>, $blech, and $foobar, would get processed immediately 
afterwards-- after the tokens had been substituted.

How would that work out do you think?


> Unfortunately, any parsing of this script is quite likely to thrown lines
> numbers off from the original source.

Yeah well, life sucks don't it ;)

If it really bothered you, you could always make a note in the output that the 
line numbering might be off.


> Another mechanism for might be good to use for this is to use ASPish
> global.asa events that can be called every XMLSubs execution, both
> before & after, like:
>
> sub XMLSubs_OnStart {
>     my($xml_tag_name, $args, $html_input) = @_;
> }
>
> sub XMLSubs_OnEnd {
>     my($xml_tag_name, $args, $html_output) = @_;
> }

This doesn't seem like a very clean solution to me, a kludge at best. If you 
don't mind my saying so, I think my solution is better using the rendering 
function I put forth. Its also easier and works out-of-the-box with no 
additional modification. 

Just delay the <% %> substitution until after the initial rendering stage and 
let post-processing pick it up-- the only minor hitch is that you need to 
make sure you're back in the original scope before doing the substitution. 
__TOKENS__ get replaced inside the my:sub function, by the my:sub function-- 
whether with the render function or directly by the programmer-- and <% %> 
get replaced immediately after its return.


> In an OO model, I think we could achieve something like this as in:
>
> my $pgloop = My::Pgloop->new(
> 	ASP => $asp,
> 	Attrib => { sth => $sth },
> 	Content => \$data_within,
> };
> my $pgimage = My::PgImage->new(
> 	ASP => $asp,
> 	Attrib => { pg=>"__PG__" sku=>"__SKU__" },
> 	Parent => $pgloop
> };
> $pgimage->execute(\'');
> $pgloop->execute(\$final_data_output_within);


> This pre/post processing model would allow pgimage to
> work with $self->{Parent}{sth}, or pgloop could populate
> its own data in this way.  Output from pgimage could be
> trapped and inlined, passed into the execute() function
> for pgloop.

Hmm- sounds a little complicated. Not to mention you are exposing a lot of the 
$asp guts to the programmer/user that way and the goal should be the exact 
opposite-- to abstract that away from the programmer and toward the 
web-monkey/end-user...?


> > Another useful thing might be a <def> field (define) field that could
> > construct artificial tokens based on expression-manipulation of existing
> > data available in the dataset at time of rendering. That way you could
> > have a cost field and apply a markup value to it and come up with an
> > artificial, but usable price token.
> >
> > <my:sub>
> >   <define>PRICE:COST*1.2</define>
> >  The cost of this item is __COST__ and it is currently selling for
> > __PRICE__. </my:sub>

> If we have a preprocessing stage like above, 

That is really a weak example actually. And after thinking about it awhile, I 
think I agree with you that having <define> blocks is a bad idea. While it 
might be useful to have that sort of flexibility, in reality it would be a 
bad idea to depend on a number hard-coded into the html body to define the 
markup value of the price (to use the example)-- and might encourage 
programmers (who oughta know better) to do silly things.



In my own project that I'm currently working on-- I have two major issues (at 
least as bounded by the context of this discussion and in trying to resolve 
it using tools developed herein)-- and they are:

1) Choosing between a Regular-Price and a Sale-Price (which are both retrieved 
during the record fetch) based upon truth of the 'on-sale' flag.

2a) Wanting to have a slightly different rendering-- conditional if the item 
is on-sale vs. not on-sale.

2b) Conditional blocks of code-- eg. if the item has a full-description, I 
want to show it (and the header that says "Full Description" or whatever) or 
nothing at all if not.


In thinking through some stuff-- token rendering is less important and so are 
format strings than I was making them out to be before-- though the simple 
format strings are still quite useful, imo, and eliminate more complicated 
coding because.. you can always define my:subs to handle token-rendering, 
formatting, and (I think) eliminating the need for explicit token definitions 
(defines). So in the earlier example where it went to define a __PRICE__ 
token based on a __COST__ token times a multiplier, you could instead do 
something like:

<my:markup cost="__COST__" mult="1.2"/>

or

<my:markup cost="__COST__" mult="<%=$mult%>"/>

if post-processing were available-- actually this particular example works 
either way, doesn't it...

I think you should keep the  <fmt>PRICE:%.2f</fmt>  stuff available. That is 
definately a handy short-cut for oft-used and simple field formatting, and 
happens in an easy-to-understand way. And it doesn't get in the way of, or 
preclude the more powerful xmlsub method I just illustrated.

So the earlier example could be processed like this instead:

<my:sub>
  <fmt>COST:%.2f</fmt>
 The cost of this item is __COST__ and it is currently selling for <my:markup 
cost="__COST__" mult="1.2"/>.
</my:sub>

or using post-processing:

<my:sub>
  <fmt>COST:%.2f</fmt>
 The cost of this item is __COST__ and it is currently selling for <my:markup 
cost="__COST__" mult="<%=$mult%>"/>.
</my:sub>


This seems like a much easier way to handle it and it fits into the current 
framework much easier and with almost no modification to the current setup. 
In fact, if you didn't change a thing you could still do 99% of this with the 
render function-- you just couldn't deal with the post-processing bit (which 
I already pointed out isn't really an issue in this particular example 
anyway).



So the only real problem left comes down to handling the 
"(blech>blah)?foo:bar" type logic and full-fledged conditional blocks.


> In a pre-process model, the if/else children could be evaluated by the
> parent at parent->new() time, or could look at the parent data at their
> if->new() time.  If ASP <% if() { } %> will still be supported, within,
> then that could be evaluated at parent->new() time by simply doing:
>
> $output = $Response->TrapInclude(\$content);

Actually I was thinking something very similar but for another reason-- if you 
use delayed processing, like I describe above, conditionals actually get 
pretty easy I think. You just build them into the my:sub "$body" text and let 
the post-processing pick them up.

I think you could handle the ()?: construct with a my:sub too actually-- do 
something like:

<my:price reg="__REG_PRICE__" sale="__SALE_PRICE__" onsale="__ONSALE__"/>

Though it might be prettier if there were a special construct for it:

__(__ONSALE__?__SALE_PRICE__:__REG_PRICE__)__

I think would do it-- and actually, that could simply be handled by the 
renderer func, and would internally get translated into:

 <%=($$hash{onsale})?$$hash{sale_price}:$$hash{reg_price}%>

Which would work just fine when post-processed. Or, seeing as how you already 
have the __( )__ nomenclature to distinguish it, you could even shorten the 
construct to:

__(ONSALE?SALE_PRICE:REG_PRICE)__

since its going to be handled special anyway-- and that gets intuitive again.


> where that will execute the content without needing a post processing model
> at all.  Hmmm, I kind of like where this is going. :)  Of course its
> totally different that what we have today.

Heh- not anymore ;)

All you need to do is make the one adjustment to handle <% %> subs post 
instead of pre and I think the render function can handle the inbetween 
stuff. The __TOKEN__ format is very comfortable and intuitive to me in this 
instance. It seems very obvious that they are serving as placeholders for 
yet-to-be-in-scope data. And by delaying the <% %> processing, you allow 
substitution to occur _before_ conditional blocks take place.


What is your take on all this? 

I think we've just descibed a very functional, useable processing paradigm 
that fits with the current "feel" of the asp system, and with the single 
exception of moving <% %> substitution to post-processing, you don't have to 
make any other radical changes to the Apache::ASP framework or api (at least 
I don't think you do).

Your thoughts?


John


--------------------------------------------------------------------------------
Check out http://www.Wizard.Org for great deals on Electronic Parts
*NEW* Computer Parts & Accessories - Drives - LCD - Systems - Linux
--------------------------------------------------------------------------------
** Affordable Online Store w/Merchant Card Processing & Paypal **
Write to us: sales@wizard.org  --  Get your Store Online Today!
--------------------------------------------------------------------------------



---------------------------------------------------------------------
To unsubscribe, e-mail: asp-unsubscribe@perl.apache.org
For additional commands, e-mail: asp-help@perl.apache.org


Re: Yet more code (Was: Re: A nifty code sample to share)

Posted by Josh Chamas <jo...@chamas.com>.
Brat Wizard wrote:
> On Wednesday 18 September 2002 01:10 pm, Josh Chamas spewed into the ether:
> 
>>Assuming that the data is available when the my:sub is starting
>>to get processed, then you can use more XMLSubs to do your dirty work like:
>>
>><% my $data_ref = \%data_hash; %>
>><my:sub data=$data_ref>
>>    <my:value name='COST' fmt='%.2f' />
>>    <my:value name='STATE' fmt='%uc2' />
>></my:sub>
> 
> 
> I've been playing around with this some more and have this observation to make 
> (among some others)-- what you have started here with XMLSubs is very nifty 
> and offers the designer a large degree of abstraction ability (a la 
> templating) in crufting up pages-- however, the one big weakness that I see 
> is that all of of the ASP assisted bits-- nested subs, <% %> substitution, 
> etc-- all happen in the PRE processing stage and not DURING the processing 
> stage. A lot of what one wants to do doesn't come into scope until _during_ 
> the processing.

Right, its almost as if one would want a pre-processing stage of not the
output, but of the content.  The current model was not built to be easily
extendable to this as there is only one sub per XMLSubs that gets called,
but what you would really want is something like:

sub my::sub_pre()
sub my::sub_post()

Unfortunately, any parsing of this script is quite likely to thrown lines
numbers off from the original source.

Another mechanism for might be good to use for this is to use ASPish
global.asa events that can be called every XMLSubs execution, both
before & after, like:

sub XMLSubs_OnStart {
    my($xml_tag_name, $args, $html_input) = @_;
}

sub XMLSubs_OnEnd {
    my($xml_tag_name, $args, $html_output) = @_;
}

The problem that I see with this is that it makes your XMLSubs
necessarily wired to the application, when ideally it should
be easily repurposable to other web applications.  JSP taglibs
seem to solve this problem by having an XML description file
for each tag lib.

I think I would like to solve this by having the ability to
map a module namespace to an XML namespace, and then have
a certain OO specification that if the module implements
it, then the XMLSubs can have its preprocessing stage et al.

> 
> For example, one use of this ability (and enabling a big win) would be in 
> interpreting/rendering row data selected from a database. None of the nifty 
> ASP-assist functions are at all useful in this context because it isn't until 
> inside the iteration loop which is inside the my:sub function that this 
> information first becomes available. 
> 
> Here is an (actual) example of the problem:
> 
> <table width="100%">
> <my:pgloop sth=$sth>
>   <fmt>PRICE:%.2f</fmt>
>   <tr>
>     <td><my:pimage pg="__PG__" sku="__SKU__" width="32"/></td>
>     <td><a href="catalog/__PG__/__SKU__">__SKU__</a></td>
>     <td>__USER1__</td>
>     <td>__NAME__</td>
>     <td align=right>__QTY_ONHAND__</td>
>     <td align=right>__AMT_PRICE__</td>
>   </tr>
> </my:pgloop>
> </table>
> 
> In this case, the 'my:pimage' sub is nested inside the 'my:pgloop' sub-- the 
> statement handle '$sth' contains the selected DBI table rows, and the pgloop 
> sub iterates the rows and subs the template for each row. The 'my:pimage' sub 
> retrieves an image based on the product group and sku id. But in this case, 
> the sku is not known until inside the pg:loop func. Intuitively (to me at 

In an OO model, I think we could achieve something like this as in:

my $pgloop = My::Pgloop->new(
	ASP => $asp,
	Attrib => { sth => $sth },
	Content => \$data_within,
};
my $pgimage = My::PgImage->new(
	ASP => $asp,
	Attrib => { pg=>"__PG__" sku=>"__SKU__" },
	Parent => $pgloop
};
$pgimage->execute(\'');
$pgloop->execute(\$final_data_output_within);

This pre/post processing model would allow pgimage to
work with $self->{Parent}{sth}, or pgloop could populate
its own data in this way.  Output from pgimage could be
trapped and inlined, passed into the execute() function
for pgloop.

Now, I'm not saying I could deliver on all of this necessarily,
but this seems like where the model should go, or some such
directtion.

> Another useful thing might be a <def> field (define) field that could 
> construct artificial tokens based on expression-manipulation of existing data 
> available in the dataset at time of rendering. That way you could have a cost 
> field and apply a markup value to it and come up with an artificial, but 
> usable price token.
> 
> <my:sub>
>   <define>PRICE:COST*1.2</define>
>  The cost of this item is __COST__ and it is currently selling for __PRICE__.
> </my:sub>
> 

If we have a preprocessing stage like above, then you could probably
take the inner content and read through it with XML::Simple, or HTML::Parser
and pick out your internal definitions that way.  It may be that I am unable
to achieve a fully post processed model where I can trap the output of
the inner tokens too, so that we would need to put the rest of the
data into another tag that you could read easily as well at input time like:

  <my:sub>
   <define>PRICE:COST*1.2</define>
   <output>The cost of this item is __COST__ and it is currently selling for __PRICE__.</output>
  </my:sub>

This kind of tag could probably be entirely handled by My::Sub->new() without
the need for a post process stage.

> Finally there is a need for some sort of simple if/then logic. This is where 
> everything gets tricky. If you go to far with this concept then you cross the 
> threshold of simple formatting and get into procedural aspects which should 
> probably be avoided. There probably should never be any sort of iterative 
> aspect. That seems definately over the line, and can be handled anyway by 
> simply creating a my:sub that performs the iteration. But consider this 
> situation:

In a pre-process model, the if/else children could be evaluated by the
parent at parent->new() time, or could look at the parent data at their
if->new() time.  If ASP <% if() { } %> will still be supported, within,
then that could be evaluated at parent->new() time by simply doing:

$output = $Response->TrapInclude(\$content);

where that will execute the content without needing a post processing model
at all.  Hmmm, I kind of like where this is going. :)  Of course its totally
different that what we have today.

> sub render {
>         my ($tmpl, $hash, $fmt) = @_;
>         while ($tmpl =~ /__(.*?)__/) {
>                 my $TOKEN = $1; my $token = lc($TOKEN);
>                 if ((my $fmt = $$fmt{$TOKEN}) ne undef) {
>                         # to-uppercase

Seeing where you are going with this, I feel like an object per
XMLSubs approach has the greatest potential.  With well well
defined reserved interface, mixed in with Apache::ASP::Share
system templates, I think this can go a long way towards
building reusable components.

> ="$$args{go_button}" border=0>}:qq{ &nbsp;<input type=submit value="Go">};
>         print << "EOF";
> <table border=0 cellpadding=1 cellspacing=0>
> <form method=GET action="/$storeid/$script">
> <tr>
>         $caption

I am hoping that this form processing could be done from a
reusable XMlSubs by something like $ASP->Response->Include('Share::My/Subs/form.inc');
I believe the way I constructed the Share:: mechanism is that
some could overload that template at their $GLOBAL/My/Subs/form.inc
to further customize what you have done, but there is a whole
lot of extension that can be done there too to make it really powerful.

Regards,

Josh
________________________________________________________________
Josh Chamas, Founder                   phone:925-552-0128
Chamas Enterprises Inc.                http://www.chamas.com
NodeWorks Link Checking                http://www.nodeworks.com


---------------------------------------------------------------------
To unsubscribe, e-mail: asp-unsubscribe@perl.apache.org
For additional commands, e-mail: asp-help@perl.apache.org