Template-Pure

 view release on metacpan or  search on metacpan

README.mkdn  view on Meta::CPAN

just so you have a place to match, when you have complex and deeply nested data.

Additionally many UI  designers already are familiar with some basic templating systems and 
might really prefer to use that so that they can maintain more autonomy and avoid the additional
learning curve that [Template::Pure](https://metacpan.org/pod/Template::Pure) will requires (most people seem to find its a bit more
effort to learn off the top compared to more simple systems like Mustache or even [Template::Toolkit](https://metacpan.org/pod/Template::Toolkit).

Although inspired by pure.js [http://beebole.com/pure/](http://beebole.com/pure/) this module attempts to help mitigate some
of the listed possible downsides with additional features that are a superset of the original 
pure.js specification. For example you may include templates inside of templates as includes or even
overlays that provide much of the same benefit that template inheritance offers in many other
popular template frameworks.  These additional features are intended to make it more suitable as a general
purpose server side templating system.

# CREATING TEMPLATE OBJECTS

The first step is to create a [Template::Pure](https://metacpan.org/pod/Template::Pure) object:

    my $pure = Template::Pure->new(
      template=>$html,
      directives=> \@directives);

README.mkdn  view on Meta::CPAN

Results in:

    <div>
      <p class="headline">This is a test of the emergency broadcasting
      network... This is only a test</p>
    </div>

Lastly you can mimic a type of inheritance using data mapping and
node aliasing:

    my $overlay_html = q[
       <html>
         <head>
           <title>Example Title</title>
           <link rel="stylesheet" href="/css/pure-min.css"/>
             <link rel="stylesheet" href="/css/grids-responsive-min.css"/>
               <link rel="stylesheet" href="/css/common.css"/>
           <script src="/js/3rd-party/angular.min.js"></script>
             <script src="/js/3rd-party/angular.resource.min.js"></script>
         </head>
         <body>
           <section id="content">...</section>
           <p id="foot">Here's the footer</p>
         </body>
       </html>
     ];

     my $overlay = Template::Pure->new(
       template=>$overlay_html,
       directives=> [
         'title' => 'title',
         '^title+' => 'scripts',
         'body section#content' => 'content',
       ]);

     my $page_html = q[
       <html>
         <head>
           <title>The Real Page</title>

README.mkdn  view on Meta::CPAN

     my $page = Template::Pure->new(
       template=>$page_html,
       directives=> [
         'title' => 'meta.title',
         'html' => [
           {
             title => \'title',
             scripts => \'^head script',
             content => \'body',
           },
           '^.' => $overlay,
         ]
       ]);

     my $data = +{
       meta => {
         title => 'Inner Stuff',
       },
     };

Results in:

README.mkdn  view on Meta::CPAN

      template=>$base_html,
      directives=> [
        'title+' => 'meta.title',
        '#story' => 'story,
        '^#story => $story_section_wrapper,
      ]
    );

## Overlay

An overlay replaces the selected node with the results on another template.  Typically
you will pass selected nodes of the original template as directives to the new template.
This can be used to minic features like template inheritance, that exist in other templating
systems.  One example:

    my $overlay_html = q[
      <html>
        <head>
          <title>Example Title</title>
          <link rel="stylesheet" href="/css/pure-min.css"/>
            <link rel="stylesheet" href="/css/grids-responsive-min.css"/>
              <link rel="stylesheet" href="/css/common.css"/>
          <script src="/js/3rd-party/angular.min.js"></script>
            <script src="/js/3rd-party/angular.resource.min.js"></script>
        </head>
        <body>
        </body>
      </html>
    ];

    my $overlay = Template::Pure->new(
      template=>$overlay_html,
      directives=> [
        'title' => 'title',
        'head+' => 'scripts',
        'body' => 'content',
      ]);

    my $base_html = q[
      <?pure-overlay src='layout'
        title=\'title'
        scripts=\'^head script' 
        content=\'body'?>
      <html>
        <head>
          <title>Page Title:&nbsp;</title>
          <script>
          function foo(bar) {
            return baz;
          }

README.mkdn  view on Meta::CPAN


    my $base = Template::Pure->new(
      template=>$base_html,
      directives=> [
        'title+' => 'meta.title',
        '#story' => 'story,
      ]
    );

    print $base->render({
      layout => $overlay,
      story => 'Once Upon a Time...',
      meta => {
        title=>'Once',
        author=>'jnap',
      },
    });

Renders As:

    <html>

README.mkdn  view on Meta::CPAN

            }
            </script>
      </head>
      <body>
        <div id='story'>Once Upon a Time...</div>
      </body>
    </html>

The syntax of the processing instruction is:

    <?pure-overlay src='' @args ?>

Where 'src' is a data path to the template you want to use as the overlay, and @args is
a list of key values which populate the data context of the overlay when you process it.
Often these values will be references to existing nodes in the base template (as in the
examples \\'title' and \\'body' above) but they can also be used to map values from your
data context in the same way we do so for ["Include"](#include) and ["Wrapper"](#wrapper).

If you were to write this as 'directives only' it would look like:

    my $base = Template::Pure->new(
      template=>$base_html,
      directives=> [
        'title+' => 'meta.title',

README.mkdn  view on Meta::CPAN

          {
            title => \'title'
            script s=> \'^head script' 
            content => \'body'
          },
          '^.' => 'layout',
        ],
      ]
    );

Please note that although in this example the overlay wrapped over the entire template, it is
not limited to that, rather like the ["Wrapper"](#wrapper) processing instruction it just takes the next
tag node following as its overlay target.  So you could have more than one overlap in a document
and can overlay sections for those cases where a ["Wrapper"](#wrapper) is not sufficently complex.

## Filter

A Filter will process the following node on a [Template::Pure](https://metacpan.org/pod/Template::Pure) instance as if that node was the
source for its template.  This means that the target source template must be a coderef that builds
a <Template::Pure> object, and not an already instantiated one.  For Example:

    my $base_html = q[
      <html>
        <head>

lib/Template/Pure.pm  view on Meta::CPAN

      );
    } else {
      push @{$params{directives}}, (
        "^*[data-pure-filter-id=filter-$params{cnt}]", sub {
          my ($t, $dom, $data) = @_;
          $t->data_at_path($data, $src)->($dom);
        },
        "*[data-pure-filter-id=filter-$params{cnt}]\@data-pure-filter-id", sub { undef },
      );
    }
  } elsif($target eq 'pure-overlay') {
    $params{node}->following('*')->first->attr('data-pure-overlay-id'=>"overlay-$params{cnt}");
    $params{node}->remove;

    push @{$params{directives}}, (
      "^*[data-pure-overlay-id=overlay-$params{cnt}]", [ +{%attrs, src=>$src }, '^.' => 'src'],
      #  "*[data-pure-overlay-id=overlay-$params{cnt}]\@data-pure-overlay-id", sub { undef },
    );
  } else {
    warn "Encountering processing instruction $target that I can't process";
  }
  $params{cnt}++;
  return %params;
}

sub components { shift->{components} }
sub initialized_components { shift->{initialized_components} }

lib/Template/Pure.pm  view on Meta::CPAN

just so you have a place to match, when you have complex and deeply nested data.

Additionally many UI  designers already are familiar with some basic templating systems and 
might really prefer to use that so that they can maintain more autonomy and avoid the additional
learning curve that L<Template::Pure> will requires (most people seem to find its a bit more
effort to learn off the top compared to more simple systems like Mustache or even L<Template::Toolkit>.

Although inspired by pure.js L<http://beebole.com/pure/> this module attempts to help mitigate some
of the listed possible downsides with additional features that are a superset of the original 
pure.js specification. For example you may include templates inside of templates as includes or even
overlays that provide much of the same benefit that template inheritance offers in many other
popular template frameworks.  These additional features are intended to make it more suitable as a general
purpose server side templating system.

=head1 CREATING TEMPLATE OBJECTS

The first step is to create a L<Template::Pure> object:

    my $pure = Template::Pure->new(
      template=>$html,
      directives=> \@directives);

lib/Template/Pure.pm  view on Meta::CPAN

Results in:

    <div>
      <p class="headline">This is a test of the emergency broadcasting
      network... This is only a test</p>
    </div>

Lastly you can mimic a type of inheritance using data mapping and
node aliasing:

   my $overlay_html = q[
      <html>
        <head>
          <title>Example Title</title>
          <link rel="stylesheet" href="/css/pure-min.css"/>
            <link rel="stylesheet" href="/css/grids-responsive-min.css"/>
              <link rel="stylesheet" href="/css/common.css"/>
          <script src="/js/3rd-party/angular.min.js"></script>
            <script src="/js/3rd-party/angular.resource.min.js"></script>
        </head>
        <body>
          <section id="content">...</section>
          <p id="foot">Here's the footer</p>
        </body>
      </html>
    ];

    my $overlay = Template::Pure->new(
      template=>$overlay_html,
      directives=> [
        'title' => 'title',
        '^title+' => 'scripts',
        'body section#content' => 'content',
      ]);

    my $page_html = q[
      <html>
        <head>
          <title>The Real Page</title>

lib/Template/Pure.pm  view on Meta::CPAN

    my $page = Template::Pure->new(
      template=>$page_html,
      directives=> [
        'title' => 'meta.title',
        'html' => [
          {
            title => \'title',
            scripts => \'^head script',
            content => \'body',
          },
          '^.' => $overlay,
        ]
      ]);

    my $data = +{
      meta => {
        title => 'Inner Stuff',
      },
    };

Results in:

lib/Template/Pure.pm  view on Meta::CPAN

      template=>$base_html,
      directives=> [
        'title+' => 'meta.title',
        '#story' => 'story,
        '^#story => $story_section_wrapper,
      ]
    );

=head2 Overlay

An overlay replaces the selected node with the results on another template.  Typically
you will pass selected nodes of the original template as directives to the new template.
This can be used to minic features like template inheritance, that exist in other templating
systems.  One example:

    my $overlay_html = q[
      <html>
        <head>
          <title>Example Title</title>
          <link rel="stylesheet" href="/css/pure-min.css"/>
            <link rel="stylesheet" href="/css/grids-responsive-min.css"/>
              <link rel="stylesheet" href="/css/common.css"/>
          <script src="/js/3rd-party/angular.min.js"></script>
            <script src="/js/3rd-party/angular.resource.min.js"></script>
        </head>
        <body>
        </body>
      </html>
    ];

    my $overlay = Template::Pure->new(
      template=>$overlay_html,
      directives=> [
        'title' => 'title',
        'head+' => 'scripts',
        'body' => 'content',
      ]);

    my $base_html = q[
      <?pure-overlay src='layout'
        title=\'title'
        scripts=\'^head script' 
        content=\'body'?>
      <html>
        <head>
          <title>Page Title:&nbsp;</title>
          <script>
          function foo(bar) {
            return baz;
          }

lib/Template/Pure.pm  view on Meta::CPAN


    my $base = Template::Pure->new(
      template=>$base_html,
      directives=> [
        'title+' => 'meta.title',
        '#story' => 'story,
      ]
    );

    print $base->render({
      layout => $overlay,
      story => 'Once Upon a Time...',
      meta => {
        title=>'Once',
        author=>'jnap',
      },
    });

Renders As:

    <html>

lib/Template/Pure.pm  view on Meta::CPAN

            }
            </script>
      </head>
      <body>
        <div id='story'>Once Upon a Time...</div>
      </body>
    </html>

The syntax of the processing instruction is:

    <?pure-overlay src='' @args ?>

Where 'src' is a data path to the template you want to use as the overlay, and @args is
a list of key values which populate the data context of the overlay when you process it.
Often these values will be references to existing nodes in the base template (as in the
examples \'title' and \'body' above) but they can also be used to map values from your
data context in the same way we do so for L</Include> and L</Wrapper>.

If you were to write this as 'directives only' it would look like:

    my $base = Template::Pure->new(
      template=>$base_html,
      directives=> [
        'title+' => 'meta.title',

lib/Template/Pure.pm  view on Meta::CPAN

          {
            title => \'title'
            script s=> \'^head script' 
            content => \'body'
          },
          '^.' => 'layout',
        ],
      ]
    );

Please note that although in this example the overlay wrapped over the entire template, it is
not limited to that, rather like the L</Wrapper> processing instruction it just takes the next
tag node following as its overlay target.  So you could have more than one overlap in a document
and can overlay sections for those cases where a L</Wrapper> is not sufficently complex.

=head2 Filter

A Filter will process the following node on a L<Template::Pure> instance as if that node was the
source for its template.  This means that the target source template must be a coderef that builds
a <Template::Pure> object, and not an already instantiated one.  For Example:

    my $base_html = q[
      <html>
        <head>

t/processing-instructions.t  view on Meta::CPAN

use Test::Most;
use Template::Pure;

ok my $overlay_html = q[
  <html>
    <head>
      <title>Example Title</title>
      <link rel="stylesheet" href="/css/pure-min.css"/>
        <link rel="stylesheet" href="/css/grids-responsive-min.css"/>
          <link rel="stylesheet" href="/css/common.css"/>
      <script src="/js/3rd-party/angular.min.js"></script>
        <script src="/js/3rd-party/angular.resource.min.js"></script>
    </head>
    <body>
      <section id="content">...</section>
      <p id="foot">Here's the footer</p>
    </body>
  </html>
];

ok my $overlay = Template::Pure->new(
  template=>$overlay_html,
  directives=> [
    'title' => 'title',
    'head+' => 'scripts',
    'body section#content' => 'content',
  ]);

ok my $story_html = qq[
  <section>
    <h1>story title</h1>
    <p>By </p>

t/processing-instructions.t  view on Meta::CPAN

ok my $foot_html = qq[
  <span class="time">current time: </span>];

ok my $foot = Template::Pure->new(
  template=>$foot_html,
  directives=> [
    '.time+' => 'time',
  ]);

ok my $base_html = q[
  <?pure-overlay src='lib.overlay'
    title=\'title'
    scripts=\'^head script' 
    content=\'body'?>
  <html>
    <head>
      <title>Page Title: </title>
      <script>
      function foo(bar) {
        return baz;
      }

t/processing-instructions.t  view on Meta::CPAN

  meta => {
    title=>'My Title',
    author=>'jnap',
    time => scalar(localtime),
    class => 'blue',
  },
  class => 'red',
  lib => {
    foot => $foot,
    story => $story,
    overlay => $overlay,
    filter => sub {
      my $template = shift;
      return Template::Pure->new(
        template => $template,
        directives => [
        'a@class' => 'class',
        ]
      );
    },
  }



( run in 1.368 second using v1.01-cache-2.11-cpan-49f99fa48dc )