X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FTemplate%2FSimple.pm;h=20d2a4683e72b05a7d79c02f1b7ff6cbaa907dc9;hb=f12977deb12d8e9e3f2555f77905ba78ed609046;hp=10970d19f62471c00c8a3ce59229168c0f01a1bf;hpb=544b9c25915917ee9a3adc016851b59ab1088de4;p=urisagit%2FTemplate-Simple.git diff --git a/lib/Template/Simple.pm b/lib/Template/Simple.pm index 10970d1..20d2a46 100644 --- a/lib/Template/Simple.pm +++ b/lib/Template/Simple.pm @@ -1,24 +1,24 @@ -package Template::Simple; +package Template::Simple ; -use warnings; -use strict; +use warnings ; +use strict ; use Carp ; use Data::Dumper ; -use Scalar::Util qw( reftype ) ; +use Scalar::Util qw( reftype blessed ) ; use File::Slurp ; - -our $VERSION = '0.03'; +our $VERSION = '0.06'; my %opt_defaults = ( pre_delim => qr/\[%/, post_delim => qr/%\]/, + token_id => qr/\w+?/, greedy_chunk => 0, # upper_case => 0, # lower_case => 0, - include_paths => [ qw( templates ) ], + search_dirs => [ qw( templates ) ], ) ; sub new { @@ -29,12 +29,19 @@ sub new { # get all the options or defaults into the object +# support the old name 'include_paths' ; + + $opts{search_dirs} ||= delete $opts{include_paths} ; + while( my( $name, $default ) = each %opt_defaults ) { $self->{$name} = defined( $opts{$name} ) ? $opts{$name} : $default ; } + croak "search_dirs is not an ARRAY reference" unless + ref $self->{search_dirs} eq 'ARRAY' ; + # make up the regexes to parse the markup from templates # this matches scalar markups and grabs the name @@ -42,7 +49,7 @@ sub new { $self->{scalar_re} = qr{ $self->{pre_delim} \s* # optional leading whitespace - (\w+?) # grab scalar name + ($self->{token_id}) # grab scalar name \s* # optional trailing whitespace $self->{post_delim} }xi ; # case insensitive @@ -60,7 +67,7 @@ sub new { \s* # optional leading whitespace START # required START token \s+ # required whitespace - (\w+?) # grab the chunk name + ($self->{token_id}) # grab the chunk name \s* # optional trailing whitespace $self->{post_delim} ($chunk_body) # grab the chunk body @@ -82,7 +89,7 @@ sub new { \s* # optional leading whitespace INCLUDE # required INCLUDE token \s+ # required whitespace - (\w+?) # grab the included template name + ($self->{token_id}) # grab the included template name \s* # optional trailing whitespace $self->{post_delim} }xi ; # case insensitive @@ -102,11 +109,15 @@ sub compile { $self->_get_template( $template_name ) ; } ; +#print Dumper $self ; + croak "Template::Simple $@" if $@ ; + my $included = $self->_render_includes( $tmpl_ref ) ; + # compile a copy of the template as it will be destroyed - my $code_body = $self->_compile_chunk( '', "${$tmpl_ref}", "\t" ) ; + my $code_body = $self->_compile_chunk( '', "${$included}", "\t" ) ; my $source = <{compiled_cache}{$template_name} = $code_ref ; $self->{source_cache}{$template_name} = $source ; @@ -147,7 +160,7 @@ ${indent}my \@data = $data_init ; ${indent}while( \@data ) { ${indent} my \$data = shift \@data ; -${indent} if ( ref \$data eq 'ARRAY' ) { +${indent} if ( reftype \$data eq 'ARRAY' ) { ${indent} push \@data, \@{\$data} ; ${indent} next ; ${indent} } @@ -216,7 +229,7 @@ sub _compile_scalars { # access the scalar push( @parts, - dump_text( substr( $template, 0, $-[0] ) ), + _dump_text( substr( $template, 0, $-[0] ) ), "\$data->{$1}" ) ; @@ -227,20 +240,24 @@ sub _compile_scalars { # keep any trailing text part - push @parts, dump_text( $template ) ; + push @parts, _dump_text( $template ) ; my $parts_code = join( "\n$indent.\n$indent", @parts ) ; return <_get_template($template_name) } || + eval{ $self->_get_template( $template_name ) } || \$template_name ; } @@ -310,20 +327,19 @@ sub _render_includes { # loop until we can render no more include markups 1 while $rendered =~ - s{$self->{include_re}} - { ${ $self->_get_template($1) } - }e ; + s{$self->{include_re}}{ ${ $self->_get_template($1) }}e ; return \$rendered ; } my %renderers = ( + SCALAR => sub { return $_[2] }, + '' => sub { return \$_[2] }, HASH => \&_render_hash, ARRAY => \&_render_array, CODE => \&_render_code, # if no ref then data is a scalar so replace the template with just the data - '' => sub { \$_[2] }, ) ; @@ -338,13 +354,31 @@ sub _render_chunk { return \'' unless defined $data ; +# get the type of this data. handle blessed types + + my $reftype = blessed( $data ) ; + +#print "REF $reftype\n" ; + +# handle the case of a qr// which blessed returns as Regexp + + if ( $reftype ) { + + $reftype = reftype $data unless $reftype eq 'Regexp' ; + } + else { + $reftype = ref $data ; + } + +#print "REF2 $reftype\n" ; + # now render this chunk based on the type of data - my $renderer = $renderers{reftype $data || ''} ; + my $renderer = $renderers{ $reftype || ''} ; -#print "EXP $renderer\nREF ", reftype $data, "\n" ; +#print "EXP $renderer\nREF $reftype\n" ; - die "unknown template data type '$data'\n" unless defined $renderer ; + croak "unknown template data type '$data'\n" unless defined $renderer ; return $self->$renderer( $tmpl_ref, $data ) ; } @@ -359,7 +393,6 @@ sub _render_hash { my $rendered = ${$tmpl_ref} ; - # recursively render all top level chunks in this chunk $rendered =~ s{$self->{chunk_re}} @@ -406,7 +439,7 @@ sub _render_code { my $rendered = $cref->( $tmpl_ref ) ; - die <{tmpl_cache}}{ keys %{$tmpls} } = - map ref $_ eq 'SCALAR' ? \"${$_}" : \"$_", values %{$tmpls} ; + + while( my( $name, $tmpl ) = each %{$tmpls} ) { + + defined $tmpl or croak "undefined template value for '$name'" ; + +# cache the a scalar ref of the template + + $self->{tmpl_cache}{$name} = ref $tmpl eq 'SCALAR' ? + \"${$tmpl}" : \"$tmpl" + } #print Dumper $self->{tmpl_cache} ; @@ -441,6 +481,7 @@ sub delete_templates { @names = keys %{$self->{tmpl_cache}} unless @names ; +#print "NAMES @names\n" ; # clear out all the caches # TODO: reorg these into a hash per name @@ -480,11 +521,14 @@ sub _find_template { my( $self, $tmpl_name ) = @_ ; - foreach my $dir ( @{$self->{include_paths}} ) { +#print "FIND $tmpl_name\n" ; + foreach my $dir ( @{$self->{search_dirs}} ) { my $tmpl_path = "$dir/$tmpl_name.tmpl" ; #print "PATH: $tmpl_path\n" ; + + next if $tmpl_path =~ /\n/ ; next unless -r $tmpl_path ; # cache the path to this template @@ -493,11 +537,15 @@ sub _find_template { # slurp in the template file and return it as a scalar ref - return scalar read_file( $tmpl_path, scalar_ref => 1 ) ; +#print "FOUND $tmpl_name\n" ; + + return read_file( $tmpl_path, scalar_ref => 1 ) ; } - die <{include_paths}}' +#print "CAN'T FIND $tmpl_name\n" ; + + croak <{search_dirs}}' DIE } @@ -508,7 +556,7 @@ __END__ =head1 NAME -Template::Simple - A simple and fast template module +Template::Simple - A simple and very fast template module =head1 VERSION @@ -520,7 +568,10 @@ Version 0.03 my $tmpl = Template::Simple->new(); - my $template = < { date => 'Jan 1, 2008', @@ -548,65 +605,40 @@ TMPL }, } ; - my $rendered = $tmpl->render( $template, $data ) ; - -=head1 DESCRIPTION - -Template::Simple has these goals: + # this call renders the template with the data tree -=over 4 - -=item * Support most common template operations - -It can recursively include other templates, replace tokens (scalars), -recursively render nested chunks of text and render lists. By using -simple idioms you can get conditional renderings. - -=item * Complete isolation of template from program code - -This is very important as template design can be done by different -people than the program logic. It is rare that one person is well -skilled in both template design and also programming. - -=item * Very simple template markup (only 4 markups) - -The only markups are C, C, C and C. See -MARKUP for more. + my $rendered = $tmpl->render( \$template_text, $data ) ; -=item * Easy to follow rendering rules + # here we add the template to the cache and give it a name -Rendering of templates and chunks is driven from a data tree. The type -of the data element used in an rendering controls how the rendering -happens. The data element can be a scalar or scalar reference or an -array, hash or code reference. + $tmpl->add_templates( { demo => $template_text } ) ; -=item * Efficient template rendering + # this compiles and then renders that template with the same data + # but is much faster -Rendering is very simple and uses Perl's regular expressions -efficiently. Because the markup is so simple less processing is needed -than many other templaters. Precompiling templates is not supported -yet but that optimization is on the TODO list. + $tmpl->compile( 'demo' ) ; + my $rendered = $tmpl->render( 'demo', $data ) ; -=item * Easy user extensions -User code can be called during an rendering so you can do custom -renderings and plugins. Closures can be used so the code can have its -own private data for use in rendering its template chunk. +=head1 DESCRIPTION -=back +Template::Simple is a very fast template rendering module with a +simple markup. It can do almost any templating task and is extendable +with user callbacks. It can render templates directly or compile them +for more speed. -=head2 new() +=head1 CONSTRUCTOR +=head2 new + You create a Template::Simple by calling the class method new: my $tmpl = Template::Simple->new() ; All the arguments to C are key/value options that change how -the object will do renderings. - -=over 4 +the object will render templates. -=item pre_delim +=head2 pre_delim This option sets the string or regex that is the starting delimiter for all markups. You can use a plain string or a qr// but you need to @@ -619,7 +651,7 @@ chars. The default is qr/\[%/. my $rendered = $tmpl->render( '<%FOO%]', 'bar' ) ; -=item post_delim +=head2 post_delim This option sets the string or regex that is the ending delimiter for all markups. You can use a plain string or a qr// but you need to @@ -632,26 +664,35 @@ chars. The default is qr/%]/. my $rendered = $tmpl->render( '[%FOO%>', 'bar' ) ; -=item greedy_chunk +=head2 token_re + +This option overrides the regular expression that is used match a +token or name in the markup. It should be a qr// and you may need to +escape (with \Q or \) any regex metachars if you want them to be plain +chars. The default is qr/\w+?/. + + my $tmpl = Template::Simple->new( + token_re => qr/[\w-]+?/, + ); + + my $rendered = $tmpl->render( + '[% id-with-hyphens %]', + { 'id-with-hyphens' => 'bar' } + ) ; + +=head2 greedy_chunk This boolean option will cause the regex that grabs a chunk of text between the C markups to become greedy (.+). The default is a not-greedy grab of the chunk text. (UNTESTED) -=item templates +=head2 templates This option lets you load templates directly into the cache of the -Template::Simple object. This cache will be searched by the C -markup which will be replaced by the template if found. The option -value is a hash reference which has template names (the name in the -C markup) for keys and their template text as their -values. You can delete or clear templates from the object cache with -the C method. - +Template::Simple object. See