NAME

FU::XMLWriter - Convenient and efficient XML and HTML generator.

EXPERIMENTAL

This module is still in development and there will likely be a few breaking API changes, see the main FU module for details.

SYNOPSIS

use FU::XMLWriter ':html5_';

my $html_string = html_ sub {
  head_ sub {
    title_ 'Document title!';
  };
  body_ sub {
    h1_ 'Main title!';
    p_ class => 'description', sub {
      txt_ 'Here we have <textual> data.';
      br_;
      a_ href => '/path', 'And a link.';
    };
  };
};

# Or XML:

use FU::XMLWriter ':xml_';

my $xml_string = xml_ sub {
  tag_ feed => xmlns => 'http://www.w3.org/2005/Atom',
      'xml:lang' => 'en', 'xml:base' => 'https://mywebsite/atom.feed', sub {
    tag_ title => 'My awesome Atom feed';
    # etc
  };
};

DESCRIPTION

This is a convenient XML writer that provides an imperative API to generating dynamic XML. It just so happens that XML syntax is also completely valid for HTML5, so this module is primarily abused for that purpose.

As a naming convention, all XML/HTML output functions are suffixed with an underscore (_) to make their functionality easy to identify and avoid potential naming collisions. You are encouraged to follow this convention in your own code. For example, if you have a function to convert some data into a nicely formatted table, you could name it info_table_() or something. It's like having composable custom HTML elements, but in the backend!

Generating HTML is something that website backends tend to do a lot, but calling tons of Perl functions is generally not very fast. For that reason, this is an XS module implemented in C. It compares favorably against a few other XML writing modules on CPAN that I tried, but whether this approach is faster than typical templating solutions... I've no idea. Check out FU::Benchmarks for some benchmarks.

Top-level functions

These functions all return a byte string with (UTF-8) encoded XML.

fragment($block)

Executes $block and captures the output of all /"Output functions" called within the same scope into a string. This function can be safely nested:

my $string = fragment {
  p_ 'Stuff here';

  my $subfragment = fragment {
    div_ 'More stuff here';
  };
  # $subfragment = '<div>More stuff here</div>'
};
# $string = '<p>Stuff here</p>'
xml_($block)

Like fragment() but adds a <?xml ..> declaration.

html_(@args)

Like fragment() but adds a suitable DOCTYPE of HTML5. The @args are passed to the tag_() call for the top-level <html> element.

Output functions

tag_($name, @attrs, $content)

This is the meat of this module. Output an XML element with the given $name. $content can either be undef to create a self-closing tag:

tag_ 'br', undef;
# <br />

Or a string:

tag_ 'title', 'My title & stuff';
# <title>My title &amp; stuff</title>

Or a subroutine:

tag_ 'div', sub {
  tag_ 'br', undef;
};
# <div><br /></div>

Attributes can be given as key/value pairs:

tag_ 'a', href => '/?f&c', title => 'Homepage', 'link';
# <a href="/f&amp;c" title="Homepage">link</a>

An undef value causes the attribute to be ignored:

tag_ 'option', selected => time % 2 == 0 ? 'selected' : undef, '';
# Depending on the time:
#   <option></option>
# Or
#   <option selected="selected"></option>

A '+' attribute name can be used to append a string to the previously given attribute:

tag_ 'div', class => $is_hidden ? 'hidden' : undef,
           '+'    => $is_warning ? 'warning' : undef, 'Text';
# Results in either:
#   <div>Text</div>
#   <div class="hidden">Text</div>
#   <div class="warning">Text</div>
#   <div class="hidden warning">Text</div>
txt_($string)

Takes a Unicode string and outputs it, escaping any special XML characters in the process.

lit_($string)

Takes a Unicode string and outputs it literally, i.e. without any XML escaping.

<html-tag>_(@attrs, $content)

This module provides a short-hand function for every HTML5 tag. Using these is less typing and also slightly more performant than calling tag_(). The following tag_()-like wrapper functions are provided:

a_ abbr_ address_ article_ aside_ audio_ b_ bb_ bdo_ blockquote_ body_
button_ canvas_ caption_ cite_ code_ colgroup_ datagrid_ datalist_ dd_ del_
details_ dfn_ dialog_ div_ dl_ dt_ em_ fieldset_ figure_ footer_ form_ h1_
h2_ h3_ h4_ h5_ h6_ head_ header_ i_ iframe_ ins_ kbd_ label_ legend_ li_
main_ map_ mark_ menu_ meter_ nav_ noscript_ object_ ol_ optgroup_ option_
output_ p_ pre_ progress_ q_ rp_ rt_ ruby_ samp_ script_ section_ select_
small_ span_ strong_ style_ sub_ summary_ sup_ table_ tbody_ td_ textarea_
tfoot_ th_ thead_ time_ title_ tr_ ul_ var_ video_

Additionally, the following self-closing-tag functions are provided:

area_ base_ br_ col_ command_ embed_ hr_ img_ input_ link_ meta_ param_
source_

The self-closing functions do not require a $content argument; if none is provided it defaults to undef.

Utility function

xml_escape($string)

Return the XML-escaped version of $string. The characters &, <, and " are replaced with their XML entity.

Import options

All of the functions mentioned in this document can be imported individually. There are also two import groups:

use FU::XMLWriter ':html_';

Exports tag_(), html_(), lit_(), txt_() and all of the <html-tag>_ functions mentioned above.

use FU::XMLWriter ':xml_';

Exports xml_(), tag_(), lit_() and txt_().

SEE ALSO

This module is part of the FU framework, although it can be used independently of it.

This module was based on TUWF::XML, which was in turn inspired by XML::Writer, which is more powerful but less convenient.

There's also DSL::HTML, a slightly more featureful, heavyweight and opinionated HTML-templating-inside-Perl module, based on HTML::Tree.

And there's HTML::Declare, which is conceptually simpler than both this and DSL::HTML, but its syntax isn't quite as nice.

And there's also HTML::FromArrayref, HTML::Tiny, HTML::Untidy and many more modules on CPAN. In fact I don't know why you should use this module instead of whatever is available on CPAN.

COPYRIGHT

MIT.

AUTHOR

Yorhel <projects@yorhel.nl>