summaryrefslogtreecommitdiff
path: root/lib/TUWF
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2017-12-16 09:45:03 +0100
committerYorhel <git@yorhel.nl>2017-12-16 09:45:03 +0100
commit8fefd16c2b503e09730951258944c73ef14a2264 (patch)
treec1ee0a8c4959a5d573da1394931a90b5d0268f43 /lib/TUWF
parentfa4380844e137905016f5e6cf4a664aae9624a8e (diff)
TUWF::XML: Add support for a more functional alternative to end()
Diffstat (limited to 'lib/TUWF')
-rw-r--r--lib/TUWF/XML.pm14
-rw-r--r--lib/TUWF/XML.pod49
2 files changed, 53 insertions, 10 deletions
diff --git a/lib/TUWF/XML.pm b/lib/TUWF/XML.pm
index 51d9b2a..d15b8e9 100644
--- a/lib/TUWF/XML.pm
+++ b/lib/TUWF/XML.pm
@@ -119,7 +119,9 @@ sub txt {
# 'tagname' <tagname>
# 'tagname', id => "main" <tagname id="main">
# 'tagname', '<bar>' <tagname>&lt;bar&gt;</tagname>
+# 'tagname', sub { .. } <tagname>..</tagname>
# 'tagname', id => 'main', '<bar>' <tagname id="main">&lt;bar&gt;</tagname>
+# 'tagname', id => 'main', sub { .. } <tagname id="main">..</tagname>
# 'tagname', id => 'main', undef <tagname id="main" />
# 'tagname', undef <tagname />
sub tag {
@@ -136,11 +138,14 @@ sub tag {
}
if(!@_) {
- $t .= '>';
- $s->lit($t);
+ $s->lit($t.'>');
push @{$s->{stack}}, $name;
} elsif(!defined $_[0]) {
$s->lit($t.' />');
+ } elsif(ref $_[0] eq 'CODE') {
+ $s->lit($t.'>');
+ $_[0]->();
+ $s->lit('</'.$name.'>');
} else {
$s->lit($t.'>'.xml_escape(shift).'</'.$name.'>');
}
@@ -161,6 +166,8 @@ sub end {
sub html {
my $s = ref($_[0]) eq __PACKAGE__ ? shift : $OBJ;
+ my $hascontent = @_ % 2 == 1;
+ my $c = $hascontent && pop;
my %o = @_;
$s->lit($doctypes{ delete($o{doctype}) || 'xhtml1-strict' }."\n");
@@ -168,7 +175,8 @@ sub html {
$s->tag('html',
xmlns => 'http://www.w3.org/1999/xhtml',
$lang ? ('xml:lang' => $lang, lang => $lang) : (),
- %o
+ %o,
+ $hascontent ? ($c) : ()
);
}
diff --git a/lib/TUWF/XML.pod b/lib/TUWF/XML.pod
index 82b83ab..2382723 100644
--- a/lib/TUWF/XML.pod
+++ b/lib/TUWF/XML.pod
@@ -33,6 +33,13 @@ The functional interface looks like this:
use TUWF ':html';
TUWF::XML->new(default => 1);
+ html sub {
+ head sub {
+ title 'Document title!';
+ };
+ };
+
+ # -- or, in more imperative style:
html;
head;
title 'Document title!';
@@ -45,6 +52,13 @@ And the equivalent, using the object interface:
use TUWF::XML;
my $xml = TUWF::XML->new();
+ $xml->html(sub {
+ $xml->head(sub {
+ $xml->title('Document title!');
+ });
+ });
+
+ # -- or, again in more imperative style:
$xml->html;
$xml->head;
$xml->title('Document title!');
@@ -151,13 +165,12 @@ C<lang> and C<xml:lang> attribute for the html open tag.
=item I<anything else>
-Any option besides I<doctype> and I<lang> is added as attribute to html open
-tag.
+All other arguments are passed to C<tag()>.
=back
-Since this opens an C<E<lt>htmlE<gt>> tag, it should be closed with an
-C<end()>.
+If you don't pass a I<contents> argument to this function, you should take care
+to close the C<< <html> >> tag with an C<end()>.
=head2 tag(name, attribute => value, .., contents)
@@ -166,9 +179,11 @@ attributes can be specified after that with key/value pairs and finally the
contents can be specified. If the I<contents> argument is not present, an open
tag will be generated, which should be closed later on using C<end()>. If
I<contents> is present but undef, the generated tag will be self-closing, i.e.
-it will end with a C</E<gt>> instead of a regular C<E<gt>>. If I<contents> is
-present and not undef, it will be used as the contents of the tag, after which
-the tag will be closed with a closing tag (C<E<lt>/tagnameE<gt>>).
+it will end with a C</E<gt>> instead of a regular C<E<gt>>. If I<contents> is a
+scalar, it will be used as the contents of the tag, after which the tag will be
+closed with a closing tag (C<E<lt>/tagnameE<gt>>). If I<contents> is a CODE
+reference, the subroutine will be called in between the start tag and the
+closing tag.
The tag name and attribute names are outputted as-is, after some very basic
validation. The attribute values and contents are passed through
@@ -197,12 +212,32 @@ Some example function calls and their output:
lit '</content>';
# except tag() can do pretty-printing when requested
+ tag 'div', sub {
+ tag 'a', href => '/', 'Home';
+ };
+ # <div><a href="/">Home</a></div>
+
=head2 end(name)
Closes the last tag opened by C<tag()> or C<html()>. The I<name> argument is
optional, when given, it will be used as validation. If the given I<name> does
not equal the last opened tag, an error is thrown.
+Usage of this function is discouraged, as it may not be easy keep track of
+which C<end()> belongs to which C<tag()>. An easier and more functional
+approach is to not use C<end()> at all, and instead give a CODE reference to
+C<tag()>. For example:
+
+ tag 'body';
+ tag 'b', 'text';
+ end;
+
+Is more safely written as:
+
+ tag 'body', sub {
+ tag 'b', 'text';
+ };
+
=head2 <xhtml-tag>(attribute => value, .., contents)
For convenience, all XHTML 1.0 tags have their own function that acts as a