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{ <input type=image
src
="$$args{go_button}" border=0>}:qq{ <input type=submit value="Go">};
print << "EOF";
<table border=0 cellpadding=1 cellspacing=0>
<form method=GET action="/$storeid/$script">
<tr>
$caption
<td $class>
<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{ <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