AxKit2
view release on metacpan or search on metacpan
lib/AxKit2/Transformer/XSP.pm view on Meta::CPAN
$document->setDocumentElement($elem);
}
return $elem;
}
sub __mk_comment_node {
my ($document, $parent, $text) = @_;
my $node = $document->createComment($text);
$parent->appendChild($node);
}
1;
__END__
=pod
=head1 NAME
AxKit2::Transformer::XSP - eXtensible Server Pages
=head1 SYNOPSIS
<xsp:page
xmlns:xsp="http://apache.org/xsp/core/v1">
<xsp:structure>
<xsp:import>Time::Piece</xsp:import>
</xsp:structure>
<page>
<title>XSP Test</title>
<para>
Hello World!
</para>
<para>
Good
<xsp:logic>
if (localtime->hour >= 12) {
<xsp:content>Afternoon</xsp:content>
}
else {
<xsp:content>Morning</xsp:content>
}
</xsp:logic>
</para>
</page>
</xsp:page>
=head1 DESCRIPTION
XSP implements a tag-based dynamic language that allows you to develop
your own tags, examples include sendmail and sql taglibs. It is AxKit's
way of providing an environment for dynamic pages. XSP is originally part
of the Apache Cocoon project, and so you will see some Apache namespaces
used in XSP.
Also, use only one XSP processor in a pipeline. XSP is powerful enough
that you should only need one stage, and this implementation allows only
one stage. If you have two XSP processors, perhaps in a pipeline that
looks like:
... => XSP => XSLT => XSLT => XSP => ...
it is pretty likely that the functionality of the intermediate XSLT
stages can be factored in to either upstream or downstream XSLT:
... => XSLT => XSP => XSLT => ...
This design is likely to lead to a clearer and more maintainable
implementation, if only because generating code, especially embedded
Perl code, in one XSP processor and consuming it in another is often
confusing and even more often a symptom of misdesign.
Likewise, you may want to lean towards using Perl taglib modules instead
of upstream XSLT "LogicSheets". Upstream XSLT LogicSheets work fine,
mind you, but using Perl taglib modules results in a simpler pipeline,
simpler configuration (just load the taglib modules in httpd.conf, no
need to have the correct LogicSheet XSLT page included whereever you
need that taglib), a more flexible coding environment, the ability to
pretest your taglibs before installing them on a server, and better
isolation of interface (the taglib API) and implementation (the Perl
module behind it). LogicSheets work, and can be useful, but are often
the long way home. That said, people used to the Cocoon environment may
prefer them.
=head2 Result Code
You can specify the result code of the request in two ways. Both actions
go inside a <xsp:logic> tag.
If you want to completely abort the current request, throw an exception:
throw Apache::AxKit::Exception::Retval(return_code => FORBIDDEN);
If you want to send your page but have a custom result code, return it:
return FORBIDDEN;
In that case, only the part of the document that was processed so far gets
sent/processed further.
=head2 Debugging
If you have PerlTidy installed (get it from L<http://perltidy.sourceforge.net>),
the compiled XSP scripts can be formatted nicely to spot errors easier. Enable
AxDebugTidy for this, but be warned that reformatting is quite slow, it can
take 20 seconds or more I<on each XSP run> for large scripts.
If you enable AxTraceIntermediate, your script will be dumped alongside the
other intermediate files, with an extension of ".XSP". These are unnumbered,
thus only get one dump per request. If you have more than one XSP run in a
single request, the last one will overwrite the dumps of earlier runs.
=head1 Tag Reference
=head2 C<< <xsp:page> >>
This is the top level element, although it does not have to be. AxKit's
XSP implementation can process XSP pages even if the top level element
is not there, provided you use one of the standard AxKit ways to turn
lib/AxKit2/Transformer/XSP.pm view on Meta::CPAN
my ($e, $text) = @_;
$text =~ s/^\s*//;
$text =~ s/\s*$//;
return '' unless $text;
$text = AxKit2::Transformer::XSP::makeSingleQuoted($text);
return ". $text";
}
Note there's no semi-colon at the end of all this, so we add that:
in parse_end:
if ($tag eq 'to') {
return ';';
}
All of this black magic allows other taglibs to set the thing in that
variable using expressions.
=head2 3. You want your tag to return a scalar (string) that does the right thing
depending on context.
For example, generates a Text node in one place or generates a scalar in another
context.
Solution:
use $e->start_expr(), $e->append_to_script(), $e->end_expr().
Example:
<example:get-datetime format="%Y-%m-%d %H:%M:%S"/>
in parse_start:
if ($tag eq 'get-datetime') {
$e->start_expr($tag); # creates a new { ... } block
my $local_format = lc($attribs{format}) || '%a, %d %b %Y %H:%M:%S %z';
return 'my ($format); $format = q|' . $local_format . '|;';
}
in parse_end:
if ($tag eq 'get-datetime') {
$e->append_to_script('use Time::Piece; localtime->strftime($format);');
$e->end_expr();
return '';
}
Explanation:
This is more complex than the first 2 examples, so it warrants some
explanation. I'll go through it step by step.
$e->start_expr($tag)
This tells XSP that this really generates a <xsp:expr> tag. Now we don't
really generate that tag, we just execute the handler for it. So what
happens is the <xsp:expr> handler gets called, and it looks to see what
the current calling context is. If its supposed to generate a text node,
it generates some code to do that. If its supposed to generate a scalar, it
does that too. Ultimately both generate a do {} block, so we'll summarise
that by saying the code now becomes:
do {
(the end of the block is generated by end_expr()).
Now the next step (ignoring the simple gathering of the format variable), is
a return, which appends more code onto the generated perl script, so we
get:
do {
my ($format); $format = q|%a, %d %b %Y %H:%M:%S %z|;
Now we immediately receive an end_expr, because this is an empty element
(we'll see why we formatted it this way in #5 below). The first thing we
get is:
$e->append_to_script('use Time::Piece; localtime->strftime($format);');
This does exactly what it says, and the script becomes:
do {
my ($format); $format = q|%a, %d %b %Y %H:%M:%S %z|;
use Time::Piece; localtime->strftime($format);
Finally, we call:
$e->end_expr();
which closes the do {} block, leaving us with:
do {
my ($format); $format = q|%a, %d %b %Y %H:%M:%S %z|;
use Time::Piece; localtime->strftime($format);
}
Now if you execute that in Perl, you'll see the do {} returns the last
statement executed, which is the C<localtime->strftime()> bit there,
thus doing exactly what we wanted.
=head2 4. Your tag can take as an option either an attribute, or a child tag.
Example:
<util:include-uri uri="http://server/foo"/>
or
<util:include-uri>
<util:uri><xsp:expr>$some_uri</xsp:expr></util:uri>
</util:include-uri>
Solution:
There are several parts to this. The simplest is to ensure that whitespace
is ignored. We have that dealt with in the example parse_char above. Next
we need to handle that variable. Do this by starting a new block with the
( run in 0.485 second using v1.01-cache-2.11-cpan-39bf76dae61 )