diff options
author | Yorhel <git@yorhel.nl> | 2017-12-16 09:45:03 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2017-12-16 09:45:03 +0100 |
commit | 8fefd16c2b503e09730951258944c73ef14a2264 (patch) | |
tree | c1ee0a8c4959a5d573da1394931a90b5d0268f43 /lib/TUWF | |
parent | fa4380844e137905016f5e6cf4a664aae9624a8e (diff) |
TUWF::XML: Add support for a more functional alternative to end()
Diffstat (limited to 'lib/TUWF')
-rw-r--r-- | lib/TUWF/XML.pm | 14 | ||||
-rw-r--r-- | lib/TUWF/XML.pod | 49 |
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><bar></tagname> +# 'tagname', sub { .. } <tagname>..</tagname> # 'tagname', id => 'main', '<bar>' <tagname id="main"><bar></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 |