[Petal] [PATCH] Options via constructor rather than package globals

Grant McLean grant at mclean.net.nz
Mon Jun 2 22:49:38 BST 2003


Hi All

A couple of weeks back, I proposed a patch to allow Petal options
to be set via arguments to the constructor rather than via global
variables in the Petal package namespace.  A patch implementing
that change is attached.

Two small changes to the test scripts were required.  In both
cases a global variable was changed _after_ the template object
was created but the tests assumed the change _would_ be reflected
in the template output - precisely the type of situation this
patch is intended to protect against.  Aside from those two cases,
all other tests still pass.

The patch also adds six test scripts to exercise the functionality
of the new constructor arguments.

I bumped the version number up to 0.93_01 to indicate a developer
release.

In case the patch attachment doesn't make it through the mailing
list software, both the patch file and a complete patched
distribution are available here:

   http://www.web.co.nz/~grantm/petal/petal_options.patch
   http://www.web.co.nz/~grantm/petal/Petal-0.93_01.tar.gz

I'd appreciate any feedback people might have.

Regards
Grant

-------------- next part --------------
Index: MANIFEST
===================================================================
RCS file: /home/grantm/.cvsrepos/petal-for-patch/MANIFEST,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- MANIFEST	20 May 2003 18:03:18 -0000	1.1.1.1
+++ MANIFEST	2 Jun 2003 09:03:58 -0000	1.2
@@ -50,6 +50,12 @@
 t/033_Hash.t
 t/034_Plugin.t
 t/035_Petal_Language.t
+t/050_Opt_base_dir.t
+t/051_Opt_in_out.t
+t/052_Opt_language.t
+t/053_Opt_taint.t
+t/054_Opt_cache.t
+t/055_Opt_maxinc.t
 t/data/attributes_andquot.xml
 t/data/autoload.xml
 t/data/canonical_error.html
@@ -107,4 +113,4 @@
 t/data/test_ns_attributes/test_ns_attributes7.xml
 t/data/test_ns_attributes/test_rightWayOfDoing.xml
 t/data/xhtml.html
-t/data/xhtml_omit_tag.html
\ No newline at end of file
+t/data/xhtml_omit_tag.html
Index: lib/Petal.pm
===================================================================
RCS file: /home/grantm/.cvsrepos/petal-for-patch/lib/Petal.pm,v
retrieving revision 1.1.1.1
retrieving revision 1.14
diff -u -r1.1.1.1 -r1.14
--- lib/Petal.pm	20 May 2003 18:03:18 -0000	1.1.1.1
+++ lib/Petal.pm	2 Jun 2003 09:08:10 -0000	1.14
@@ -26,6 +26,12 @@
 use vars qw /@tokens @nodeStack/;
 
 
+# Which options should the constructor accept?
+our @KNOWN_OPTS = qw(
+  file base_dir language default_language input output taint disk_cache
+  memory_cache max_includes
+);
+
 # What do we use to parse input?
 our $INPUT  = 'XML';
 our $INPUTS = {
@@ -63,11 +69,10 @@
 
 # prevents infinites includes...
 our $MAX_INCLUDES = 30;
-our $CURRENT_INCLUDES = 0;
 
 
 # that's for CPAN
-our $VERSION = '0.92';
+our $VERSION = '0.93_01';
 
 
 # The CodeGenerator class backend to use.
@@ -125,44 +130,88 @@
     print Petal->new ($file)->_code_with_line_numbers;
 }
 
-
-# Instanciates a new Petal object.
+# Instantiates a new Petal object.
 sub new
 {
     my $class = shift;
-    $class = ref $class || $class;
+
     unshift (@_, 'file') if (@_ == 1);
-    my $self = bless { @_ }, $class;
-    $self->_initialize();
+    my $self = bless {}, $class;
+
+    $self->_initialize(@_);
     
     return $self;
 }
 
-
-# (multi language mode)
-# if the language has been specified, let's try to
-# find which template we can use.
+# Initialize object properties from constructor args or package globals
 sub _initialize
 {
     my $self = shift;
+    my %o    = @_;
+
+    $self->{_taint}        = exists $o{taint}        ? $o{taint}        : $Petal::TAINT;
+    $self->{_disk_cache}   = exists $o{disk_cache}   ? $o{disk_cache}   : $Petal::DISK_CACHE;
+    $self->{_memory_cache} = exists $o{memory_cache} ? $o{memory_cache} : $Petal::MEMORY_CACHE;
+    $self->{_max_includes} = exists $o{max_includes} ? $o{max_includes} : $Petal::MAX_INCLUDES;
+
+    $self->{_file}             = $o{file};
+    $self->{_language}         = $o{language}         || $o{lang};
+    $self->{_input}            = $o{input}            || $Petal::INPUT;
+    $self->{_output}           = $o{output}           || $Petal::OUTPUT;
+    $self->{_default_language} = $o{default_language} || $Petal::LANGUAGE;
+
+    $self->{_base_dir} = $o{base_dir} || [ @Petal::BASE_DIR ];
+    $self->{_base_dir} = [ $self->{_base_dir} ] unless ref($self->{_base_dir});
+    unshift(@{$self->{_base_dir}}, $BASE_DIR) if (defined $BASE_DIR);
+
+    delete @o{@KNOWN_OPTS, 'lang'};
+    croak 'Unknown option(s): ' . (join ' ', keys %o) if(%o);
+
+
+    # (multi language mode)
+    # if the language has been specified, let's try to
+    # find which template we can use.
+
     my $lang = $self->language() || return;
 
-    my @dirs = @BASE_DIR;
-    unshift (@dirs, $BASE_DIR) if (defined $BASE_DIR);
-    @dirs = map { File::Spec->canonpath ("$_/$self->{file}") } @dirs;
+    my @dirs = $self->base_dir();
+    @dirs = map { File::Spec->canonpath ("$_/$self->{_file}") } @dirs;
+
+    $self->{_file} =~ s/\/$//;
+    
+    local($Petal::LANGUAGE) = $self->default_language;
 
-    $self->{file} =~ s/\/$//;
     my $filename = Petal::Functions::find_filename ($lang, @dirs);
-    $self->{file} .= "/$filename" if ($filename); 
+    $self->{_file} .= "/$filename" if ($filename); 
 }
 
 
-# (multi language mode)
-# returns the current preferred language.
-sub language
-{
+# accessor methods for options
+
+sub language         { return $_[0]->{_language};         }
+sub default_language { return $_[0]->{_default_language}; }
+sub input            { return $_[0]->{_input};            }
+sub output           { return $_[0]->{_output};           }
+sub taint            { return $_[0]->{_taint};            }
+sub disk_cache       { return $_[0]->{_disk_cache};       }
+sub memory_cache     { return $_[0]->{_memory_cache};     }
+sub max_includes     { return $_[0]->{_max_includes};     }
+sub base_dir         { return @{$_[0]->{_base_dir}};      }
+
+# Return option values as a string for use in generated code
+sub include_opts {
     my $self = shift;
-    return $self->{language} || $self->{lang};
+
+    my $lang = '';
+    if($self->language) {
+        $lang = ", language => '" . $self->language . "'";
+    }
+    return 
+        "base_dir => [" . join(', ', map {"'$_'"} $self->base_dir) . "], " .
+        "input => '" . $self->input . "', " .
+        "output => '" . $self->output . "', " .
+        "max_includes => " . ($self->max_includes - 1) .
+        $lang;
 }
 
 
@@ -176,7 +225,7 @@
     my $file  = shift;
     return $file unless ($file =~ /^\./);
     
-    my $path = $self->{file};
+    my $path = $self->_file;
     ($path)  = $path =~ /(.*)\/.*/;
     $path  ||= '.';
     $path .= '/';
@@ -219,12 +268,11 @@
 # print $data_out;
 sub process
 {
+    my $self = shift;
+
     # prevent infinite includes from happening...
-    my $current_includes = $CURRENT_INCLUDES;
-    local $CURRENT_INCLUDES = $current_includes + 1;
-    return "ERROR: MAX_INCLUDES : $CURRENT_INCLUDES" if ($CURRENT_INCLUDES >= $MAX_INCLUDES);
+    return "ERROR: MAX_INCLUDES" if ($self->max_includes < 0);
     
-    my $self = shift;
     my $hash = undef;
     if (ref $_[0] eq 'Petal::Hash') { $hash = shift }
     elsif (ref $_[0] eq 'HASH')     { $hash = new Petal::Hash (%{shift()}) }
@@ -274,9 +322,9 @@
 sub _file
 {
     my $self = shift;
-    $self->{file} = shift if (@_);
-    $self->{file} =~ s/^\///;
-    return $self->{file};
+    $self->{_file} = shift if (@_);
+    $self->{_file} =~ s/^\///;
+    return $self->{_file};
 }
 
 
@@ -288,8 +336,7 @@
 {
     my $self = shift;
     my $file = $self->_file;
-    my @dirs = @BASE_DIR;
-    unshift (@dirs, $BASE_DIR) if (defined $BASE_DIR);
+    my @dirs = $self->base_dir();
     
     foreach my $dir (@dirs)
     {
@@ -300,7 +347,7 @@
 	return $file_path if (-e $file_path and -r $file_path);
     }
     
-    confess ("Cannot find $file in @BASE_DIR. (typo? permission problem?)");
+    confess ("Cannot find $file in @dirs. (typo? permission problem?)");
 }
 
 
@@ -331,13 +378,13 @@
 {
     my $self = shift;
     my $file = $self->_file_path;
-    my $code = (defined $DISK_CACHE and $DISK_CACHE) ? Petal::Cache::Disk->get ($file) : undef;
+    my $code = $self->disk_cache ? Petal::Cache::Disk->get ($file, $self) : undef;
     unless (defined $code)
     {
 	my $data_ref = $self->_file_data_ref;
 	$data_ref    = $self->_canonicalize;
 	$code = $CodeGenerator->process ($data_ref, $self);
-	Petal::Cache::Disk->set ($file, $code) if (defined $DISK_CACHE and $DISK_CACHE);
+	Petal::Cache::Disk->set ($file, $code, $self) if ($self->disk_cache);
     }
     return $code;
 }
@@ -351,13 +398,13 @@
 {
     my $self = shift;
     my $file = $self->_file_path;
-    my $code = (defined $MEMORY_CACHE and $MEMORY_CACHE) ? Petal::Cache::Memory->get ($file) : undef;
+    my $code = $self->memory_cache ? Petal::Cache::Memory->get ($file, $self) : undef;
     unless (defined $code)
     {
 	my $code_perl = $self->_code_disk_cached;
         my $VAR1 = undef;
 	
-	if ($TAINT)
+	if ($self->taint)
 	{
 	    # important line, don't remove
 	    ($code_perl) = $code_perl =~ m/^(.+)$/s;
@@ -379,7 +426,7 @@
 	    $code = $VAR1;
 	}
 	
-        Petal::Cache::Memory->set ($file, $code) if (defined $MEMORY_CACHE and $MEMORY_CACHE);
+        Petal::Cache::Memory->set ($file, $code, $self) if ($self->memory_cache);
     }
     return $code;
 }
@@ -403,8 +450,11 @@
 sub _canonicalize
 {
     my $self = shift;
-    my $parser_type        = $INPUTS->{$INPUT}   || confess "unknown \$Petal::INPUT = $INPUT";
-    my $canonicalizer_type = $OUTPUTS->{$OUTPUT} || confess "unknown \$Petal::OUTPUT = $OUTPUT";
+    my $parser_type = $INPUTS->{$self->input} ||
+        confess "unknown input format = " . $self->input;
+
+    my $canonicalizer_type = $OUTPUTS->{$self->output} ||
+        confess "unknown output format = " . $self->output;
     
     my $data_ref = $self->_file_data_ref;
     my $parser = $parser_type->new;
@@ -605,7 +655,7 @@
     use Petal;
     local $Petal::OUTPUT = 'XHTML';
 
-    my $template = new Petal ( file => 'hello_world', lang => 'fr-CA' );
+    my $template = new Petal ( file => 'hello_world', language => 'fr-CA' );
     print $template->process ( my_var => my_var() );
 
 What will happen is that the C<$template> object will try to find a file named
@@ -615,10 +665,14 @@
 
 TIP:
 
-If you feel that 'en' should not be the default language, you can change the
-C<$Petal::LANGUAGE> variable to whatever you want.
+If you feel that 'en' should not be the default language, you can specify a
+different default:
 
-    local $Petal::LANGUAGE = 'fr'; # vive la France!
+    my $template = new Petal (
+        file             => 'hello_world',
+        language         => 'zh',
+        default_language => 'fr' # vive la France!
+    );
 
 
 TRAP:
@@ -626,79 +680,109 @@
 If you do specify the C<lang> option, you MUST use a path to a template
 directory, not a file directory.
 
-Reversely, if you do not specify a C<lang> option, you MUST use a path to a
+Conversely, if you do not specify a C<lang> option, you MUST use a path to a
 template file, not a directory.
 
 
 =head1 OPTIONS
 
-=head2 C<$Petal::INPUT> - default: 'XML'
+When you create a Petal template object you can specify various options using
+name => value pairs as arguments to the constructor.  For example:
 
-Acceptable values are
+  my $template = Petal->new(
+    file     => 'gerbils.html',
+    base_dir => '/var/www/petshop',
+    input    => 'HTML',
+    output   => 'HTML',
+  );
 
-  'HTML'  - Petal will use HTML::TreeBuilder to parse the template
-  'XHTML' - Alias for 'HTML'
-  'XML'   - Petal will use XML::Parser to parse the template
+The recognised options are:
 
-Example:
 
-  local $Petal::INPUT = 'XHTML';
+=head2 file => I<filename>
 
+The template filename.  This option is mandatory and has no default.
 
-=head2 C<$Petal::OUTPUT> - default: 'XML'
+Note: If you also use 'language' this option should point to a directory.
 
-Currently acceptable values are
 
-  'HTML'  - Petal will output XHTML, self-closing certain tags
+=head2 base_dir => I<pathname> | [ I<pathname list> ] (default: '.')
+
+The directories listed in this option will be searched in turn to locate the
+template file.  A single directory can be specified as a scalar.  For a
+directory list use an arrayref.
+
+
+=head2 input => 'HTML' | 'XHTML' | 'XML' (default: 'XML')
+
+Defines the format of the template files.  Recognised values are:
+
+  'HTML'  - Petal will use HTML::TreeBuilder to parse the template
   'XHTML' - Alias for 'HTML'
-  'XML'   - Petal will output generic XML 
+  'XML'   - Petal will use XML::Parser to parse the template
 
-Example:
 
-  local $Petal::OUTPUT = 'XHTML';
+=head2 output => 'HTML' | 'XHTML' | 'XML' (default: 'XML')
 
+Defines the format of the data generated as a result of processing the template
+files.  Recognised values are:
 
-=head2 @Petal::BASE_DIR - Default: ('.')
+  'HTML'  - Petal will output XHTML, self-closing certain tags
+  'XHTML' - Alias for 'HTML'
+  'XML'   - Petal will output generic XML 
 
-Sets the base directories in which Petal looks for templates.
 
-Petal will try to fetch the template file starting from the beginning of the
-list until it finds one base directory which has the requested file.
+=head2 language => I<language code>
 
-Example:
+For internationalized applications, you can use the 'file' option to point to a
+I<directory> and select a language-specific template within that directory
+using the 'language' option.  Languages are selected using a two letter code
+(eg: 'fr') optionally followed by a hyphen and a two letter country code (eg:
+'fr-CA').
 
-  local @Petal::BASE_DIR = ('.', 'templates', '/www/templates');
 
+=head2 default_language => I<language code> (default: 'en')
 
-=head2 C<$Petal::TAINT> - default: I<FALSE>
+This language code will be used if no template matches the selected
+language-country or language.
 
-If set to C<TRUE>, makes perl taint mode happy.
 
+=head2 taint => I<true> | I<false> (default: I<false>)
 
-=head2 C<$Petal::DISK_CACHE> - Default: I<TRUE>
+If set to C<true>, makes perl taint mode happy.
 
-If set to C<FALSE>, Petal will not use the C<Petal::Cache::Disk> module.
 
+=head2 disk_cache => I<true> | I<false> (default: I<true>)
 
-=head2 C<$Petal::MEMORY_CACHE> - Default: I<TRUE>
+If set to C<false>, Petal will not use the C<Petal::Cache::Disk> module.
 
-If set to C<FALSE>, Petal will not use the C<Petal::Cache::Memory> module.
 
+=head2 memory_cache => I<true> | I<false> (default: I<true>)
 
-=head2 C<$Petal::MAX_INCLUDES> - Default: I<30>
+If set to C<false>, Petal will not use the C<Petal::Cache::Memory> module.
 
-C<$MAX_INCLUDES> - The maximum number of recursive includes before Petal stops
-processing.  This is to prevent from accidental infinite recursions.
 
+=head2 max_includes => I<number> (default: 30)
 
-=head2 C<$Petal::LANGUAGE> - Default: I<en>
+The maximum number of recursive includes before Petal stops processing.  This
+is to guard against accidental infinite recursions.
 
-Fallback template language when using Petal in multi-language mode.
 
+=head2 Global Variables
 
-Example:
+Earlier versions of Petal used global variables rather than constructor
+arguments to set the options described above.  That method of setting options
+is now deprecated, but you may encounter older code which uses the following
+variables:
 
-    local $Petal::LANGUAGE = 'fr';
+  $Petal::BASE_DIR          (use base_dir option)
+  $Petal::INPUT             (use input option)
+  $Petal::OUTPUT            (use output option)
+  $Petal::TAINT             (use taint option)
+  $Petal::DISK_CACHE        (use disk_cache option)
+  $Petal::MEMORY_CACHE      (use memory_cache option)
+  $Petal::MAX_INCLUDES      (use max_includes option)
+  $Petal::LANGUAGE          (use default_language option)
 
 
 =head1 TAL SYNTAX
Index: lib/Petal/CodeGenerator.pm
===================================================================
RCS file: /home/grantm/.cvsrepos/petal-for-patch/lib/Petal/CodeGenerator.pm,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -r1.1.1.1 -r1.3
--- lib/Petal/CodeGenerator.pm	20 May 2003 18:03:18 -0000	1.1.1.1
+++ lib/Petal/CodeGenerator.pm	26 May 2003 10:10:56 -0000	1.3
@@ -139,15 +139,16 @@
     my $class = shift;
     my $file  = $token_hash{file};
     my $path  = $petal_object->_include_compute_path ($file);
-    my $lang  = $petal_object->language();
-    if (defined $lang and $lang)
-    {
-        push @code, ("    " x $indent . $class->_add_res("Petal->new (file => '$path', lang => '$lang')->process (\$hash->new());"));
-    }
-    else
-    {
-        push @code, ("    " x $indent . $class->_add_res("Petal->new ('$path')->process (\$hash->new());"));
-    }
+
+    push @code, (
+        "    " x $indent . 
+        $class->_add_res(
+            "Petal->new (file => '$path', " .
+            $petal_object->include_opts .  
+            ")->process (\$hash->new());"
+        )
+    );
+
 }
 
 
Index: lib/Petal/Cache/Disk.pm
===================================================================
RCS file: /home/grantm/.cvsrepos/petal-for-patch/lib/Petal/Cache/Disk.pm,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- lib/Petal/Cache/Disk.pm	20 May 2003 18:03:18 -0000	1.1.1.1
+++ lib/Petal/Cache/Disk.pm	25 May 2003 10:13:43 -0000	1.2
@@ -13,14 +13,6 @@
 use Carp;
 
 
-# kill silly warnings
-sub sillyness
-{
-    + $Petal::INPUT &&
-    + $Petal::OUTPUT;
-}
-
-
 # local $Petal::Cache::Disk::TMP_DIR = <some_dir>
 # defaults to File::Spec->tmpdir;
 our $TMP_DIR = File::Spec->tmpdir;
@@ -40,8 +32,9 @@
 {
     my $class = shift;
     my $file  = shift;
-    my $key   = $class->compute_key ($file);
-    return $class->cached ($key) if ($class->is_ok ($file));
+    my $tmpl  = shift;
+    my $key   = $class->compute_key ($file, $tmpl);
+    return $class->cached ($key) if ($class->is_ok ($file, $tmpl));
     return;
 }
 
@@ -54,7 +47,8 @@
     my $class = shift;
     my $file  = shift;
     my $data  = shift;
-    my $key   = $class->compute_key ($file);
+    my $tmpl  = shift;
+    my $key   = $class->compute_key ($file, $tmpl);
     my $tmp   = $class->tmp;
     open FP, ">$tmp/$key" or
         ( Carp::cluck "Cannot write-open $tmp/$key" and return );
@@ -70,13 +64,14 @@
 {
     my $class = shift;
     my $file  = shift;
+    my $tmpl  = shift;
     
-    my $key = $class->compute_key ($file);
+    my $key = $class->compute_key ($file, $tmpl);
     my $tmp = $class->tmp;    
     my $tmp_file = "$tmp/$key";
     return unless (-e $tmp_file);
     
-    my $cached_mtime = $class->cached_mtime ($file);
+    my $cached_mtime = $class->cached_mtime ($file, $tmpl);
     my $current_mtime = $class->current_mtime ($file);
     return $cached_mtime >= $current_mtime;
 }
@@ -91,8 +86,9 @@
 {
     my $class = shift;
     my $file = shift;
+    my $tmpl = shift;
     
-    my $key = md5_hex ($file . ";INPUT=" . $Petal::INPUT . ";OUTPUT=" . $Petal::OUTPUT);
+    my $key = md5_hex ($file . ";INPUT=" . $tmpl->input . ";OUTPUT=" . $tmpl->output);
     $key = $PREFIX . "_" . $Petal::VERSION . "_" . $key if (defined $PREFIX);
     return $key;
 }
@@ -106,7 +102,8 @@
 {
     my $class = shift;
     my $file = shift;
-    my $key = $class->compute_key ($file);
+    my $tmpl = shift;
+    my $key = $class->compute_key ($file, $tmpl);
     my $tmp = $class->tmp;
     
     my $tmp_file = "$tmp/$key";
Index: lib/Petal/Cache/Memory.pm
===================================================================
RCS file: /home/grantm/.cvsrepos/petal-for-patch/lib/Petal/Cache/Memory.pm,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- lib/Petal/Cache/Memory.pm	20 May 2003 18:03:18 -0000	1.1.1.1
+++ lib/Petal/Cache/Memory.pm	25 May 2003 10:30:26 -0000	1.2
@@ -15,12 +15,6 @@
 our $FILE_TO_MTIME = {};
 
 
-sub sillyness
-{
-    + $Petal::INPUT && $Petal::OUTPUT;
-}
-
-
 # $class->get ($file);
 # --------------------
 # Returns the cached subroutine if its last modification time
@@ -30,9 +24,9 @@
 {
     my $class = shift;
     my $file  = shift;
-    my $key = $class->compute_key ($file);
-    my $data  = shift;
-    return $FILE_TO_SUBS->{$key} if ($class->is_ok ($file));
+    my $tmpl  = shift;
+    my $key = $class->compute_key ($file, $tmpl);
+    return $FILE_TO_SUBS->{$key} if ($class->is_ok ($file, $tmpl));
     return;
 }
 
@@ -44,8 +38,9 @@
 {
     my $class = shift;
     my $file  = shift;
-    my $key = $class->compute_key ($file);
     my $code  = shift;
+    my $tmpl  = shift;
+    my $key = $class->compute_key ($file, $tmpl);
     $FILE_TO_SUBS->{$key} = $code;
     $FILE_TO_MTIME->{$key} = $class->current_mtime ($file);
 }
@@ -58,10 +53,11 @@
 {
     my $class = shift;
     my $file  = shift;
-    my $key = $class->compute_key ($file);
+    my $tmpl  = shift;
+    my $key = $class->compute_key ($file, $tmpl);
     return unless (defined $FILE_TO_SUBS->{$key});
     
-    my $cached_mtime = $class->cached_mtime ($file);
+    my $cached_mtime = $class->cached_mtime ($file, $tmpl);
     my $current_mtime = $class->current_mtime ($file);
     return $cached_mtime >= $current_mtime;
 }
@@ -75,7 +71,8 @@
 {
     my $class = shift;
     my $file = shift;
-    my $key = $class->compute_key ($file);
+    my $tmpl = shift;
+    my $key = $class->compute_key ($file, $tmpl);
     return $FILE_TO_MTIME->{$key};
 }
 
@@ -101,8 +98,9 @@
 {
     my $class = shift;
     my $file = shift;
+    my $tmpl = shift;
     
-    my $key = $file . ";INPUT=" . $Petal::INPUT . ";OUTPUT=" . $Petal::OUTPUT;
+    my $key = $file . ";INPUT=" . $tmpl->input . ";OUTPUT=" . $tmpl->output;
     return $key;
 }
 
Index: t/003_More_Include.t
===================================================================
RCS file: /home/grantm/.cvsrepos/petal-for-patch/t/003_More_Include.t,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- t/003_More_Include.t	20 May 2003 18:03:18 -0000	1.1.1.1
+++ t/003_More_Include.t	25 May 2003 09:11:22 -0000	1.2
@@ -12,12 +12,12 @@
 $Petal::DISK_CACHE = 0;
 $Petal::MEMORY_CACHE = 0;
 $Petal::TAINT = 1;
+$Petal::INPUT = 'HTML';
 
 my $template_file = 'register_form.tmpl';
 my $template = new Petal ($template_file);
 
 # $template::PARSER does not work, but $Petal::PARSER does!
-$Petal::INPUT = 'HTML';
 ($Petal::INPUT eq 'HTML') ? print "ok 2\n" : print "not ok 2\n";
 
 my $data_ref = $template->_file_data_ref;
Index: t/004_Misc_Tests.t
===================================================================
RCS file: /home/grantm/.cvsrepos/petal-for-patch/t/004_Misc_Tests.t,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- t/004_Misc_Tests.t	20 May 2003 18:03:18 -0000	1.1.1.1
+++ t/004_Misc_Tests.t	25 May 2003 09:12:42 -0000	1.2
@@ -13,11 +13,10 @@
 $Petal::DISK_CACHE = 0;
 $Petal::MEMORY_CACHE = 0;
 $Petal::TAINT = 1;
+$Petal::INPUT = 'HTML';
 
 my $template_file = 'test.tmpl';
 my $template = new Petal ($template_file);
-
-$Petal::INPUT = 'HTML';
 
 my $hash = {
 	first_name => "William",
Index: t/050_Opt_base_dir.t
===================================================================
RCS file: t/050_Opt_base_dir.t
diff -N t/050_Opt_base_dir.t
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ t/050_Opt_base_dir.t	27 May 2003 10:29:07 -0000	1.7
@@ -0,0 +1,151 @@
+#!/usr/bin/perl
+##############################################################################
+# Tests the 'data_dir' option to Petal->new and related functionality.
+# Uses t/data/namespaces.xml and t/data/include/index_xinclude.xml
+#
+
+use Test::More tests => 16;
+
+use warnings;
+use lib 'lib';
+
+use Petal;
+use File::Spec;
+
+$Petal::DISK_CACHE   = 0;
+$Petal::MEMORY_CACHE = 0;
+
+my $data_dir = File::Spec->catdir('t', 'data');
+my $file     = 'namespaces.xml';
+
+my @args = (
+  replace   => 'Replacement Text',
+  content   => 'Content Here',
+  attribute => 'An attribute',
+  elements  => [ 'one', 'two' ],
+);
+
+
+# Test template not found if path not specified
+
+my $template = new Petal (file => $file);
+
+is_deeply([ $template->base_dir ], [ '.' ],
+          "\@Petal::BASE_DIR is used as default base_dir");
+
+$@ = '';
+my $output = eval {
+  $template->process(@args);
+};
+
+ok($@, "Template not found (as expected)");
+like($@, qr{
+  Cannot\sfind\snamespaces\.xml\sin\s\.\.
+}x, "Error message is correct");
+
+
+# Confirm $Petal::BASE_DIR used as default if defined
+
+{
+  local($Petal::BASE_DIR) = 'dummy_dir';
+
+  $template = new Petal (file => $file);
+
+  is_deeply([ $template->base_dir ], [ 'dummy_dir', '.' ], 
+            "\$Petal::BASE_DIR is in default base_dir");
+
+  $@ = '';
+  $output = eval {
+    $template->process(@args);
+  };
+
+  ok($@, "Template still not found (as expected)");
+  like($@, qr{
+    Cannot\sfind\snamespaces\.xml\sin\sdummy_dir\s\.\.
+  }x, "Error message is correct");
+}
+
+
+# Confirm base_dir option as arrayref works
+
+$template = new Petal (file => $file, base_dir => ['dummy1', 'dummy2']);
+
+is_deeply([ $template->base_dir ], [ 'dummy1', 'dummy2' ], 
+          "base_dir option specified as arrayref works");
+
+$@ = '';
+$output = eval {
+  $template->process(@args);
+};
+
+ok($@, "Template still not found (as expected)");
+like($@, qr{
+  Cannot\sfind\snamespaces\.xml\sin\sdummy1\sdummy2\.
+}x, "Error message is correct");
+
+
+# Confirm base_dir option as scalar works
+
+$template = new Petal (file => $file, base_dir => 'dummy');
+
+is_deeply([ $template->base_dir ], [ 'dummy' ], 
+          "base_dir option specified as scalar works");
+
+$@ = '';
+$output = eval {
+  $template->process(@args);
+};
+
+ok($@, "Template still not found (as expected)");
+like($@, qr{
+  Cannot\sfind\snamespaces\.xml\sin\sdummy\.
+}x, "Error message is correct");
+
+
+# Now specify a valid base_dir option and process a template
+
+$template = new Petal (file => $file, base_dir => $data_dir);
+
+$@ = '';
+$output = eval {
+  $template->process(@args);
+};
+
+ok(!$@, "Template was found");
+like($output, qr{^\s*
+  <body\s*>\s+
+  Replacement\sText\s+
+  <p\s*>Content\sHere</p>\s+
+  <p\s+attribute=(['"])An\sattribute\1\s*>yo</p>\s+
+  <ul>\s*
+  <li>one</li>\s*
+  <li>two</li>\s*
+  </ul>\s+
+  </body>
+}x, "Output is correct");
+
+
+# Try processing a template with an include (data_dir is required for included
+# template too).
+
+$data_dir = File::Spec->catdir('t', 'data', 'include');
+$file     = 'index_xinclude.xml';
+
+$template = new Petal (file => $file, base_dir => $data_dir);
+
+$@ = '';
+$output = eval {
+  $template->process(@args);
+};
+
+ok(!$@, "Template with includes successfully processed");
+like($output, qr{^\s*
+  <xml>\s+
+  <div>\s+
+  <p>__INCLUDED__</p>\s+
+  <p>__INCLUDED__</p>\s+
+  <p></p>\s+
+  </div>\s+
+  </xml>
+}x, "Output is correct");
+
Index: t/051_Opt_in_out.t
===================================================================
RCS file: t/051_Opt_in_out.t
diff -N t/051_Opt_in_out.t
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ t/051_Opt_in_out.t	27 May 2003 10:29:07 -0000	1.2
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+##############################################################################
+# Tests the 'input' and 'output' options to Petal->new and related
+# functionality.  Uses t/data/if.html and t/data/if.xml for templates.
+# 'if.html' is assumed to not be well formed.
+#
+
+use Test::More tests => 5;
+
+use warnings;
+use lib 'lib';
+
+use Petal;
+use File::Spec;
+
+$Petal::DISK_CACHE = 0;
+
+my $data_dir = File::Spec->catdir('t', 'data');
+my $file     = 'if.html';
+
+my @args = (
+  error => 'Altitude too low!',
+);
+
+
+# Confirm input format defaults to XML
+
+my $template = new Petal (file => $file, base_dir => $data_dir);
+
+is($template->input, 'XML', "input format defaults to 'XML'");
+#          "\$Petal::INPUT is used as default input format");
+
+$@ = '';
+my $output = eval {
+  $template->process(@args);
+};
+
+ok($@, "Template processing fails (as expected)");
+
+
+# Confirm input option can be used to change format
+
+$template = new Petal (file => $file, base_dir => $data_dir, input => 'HTML');
+
+is($template->input, 'HTML',
+          "input option overrides default");
+
+$@ = '';
+$output = eval {
+  $template->process(@args);
+};
+
+ok(!$@, "Template processed successfully");
+like($output, qr{^\s*
+  <div>\s*
+  <p\s+class=.error.\s*>Altitude\stoo\slow!</p>\s*
+  </div>
+}x, "Output is correct");
+
+
+
Index: t/052_Opt_language.t
===================================================================
RCS file: t/052_Opt_language.t
diff -N t/052_Opt_language.t
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ t/052_Opt_language.t	31 May 2003 09:20:14 -0000	1.3
@@ -0,0 +1,81 @@
+#!/usr/bin/perl
+##############################################################################
+# Tests the 'language' option (and 'lang' alias) to Petal->new.
+# Uses t/data/language/*
+#
+
+use Test::More tests => 9;
+
+use warnings;
+use lib 'lib';
+
+use Petal;
+use File::Spec;
+
+$Petal::DISK_CACHE   = 0;
+$Petal::MEMORY_CACHE = 0;
+
+my $data_dir = File::Spec->catdir('t', 'data');
+my $file     = 'language';
+
+my @args = (
+  replace   => 'Replacement Text',
+  content   => 'Content Here',
+  attribute => 'An attribute',
+  elements  => [ 'one', 'two' ],
+);
+
+
+# Try processing a template with a language and an include
+
+my $template = new Petal (file => $file, base_dir => $data_dir, language => 'fr');
+
+is($template->language, 'fr', 'correct language requested (fr)');
+
+$@ = '';
+$output = eval { $template->process(@args) };
+
+ok(!$@, "template with language and includes successfully processed");
+like($output, qr{^\s*
+  <p>\s+
+  <span>Bonjour,\sMonde\s\(fr\)</span>\s+
+  </p>
+}x, "output is correct");
+
+
+# Same again but using 'lang' option alias
+
+$template = new Petal (file => $file, base_dir => $data_dir, lang => 'fr-CA');
+
+is($template->language, 'fr-CA', 'correct language requested (fr-CA)');
+
+$@ = '';
+$output = eval { $template->process(@args) };
+
+ok(!$@, "template with lang and includes successfully processed");
+like($output, qr{^\s*
+  <p>\s+
+  <span>Bonjour,\sMonde\s\(fr-CA\)</span>\s+
+  </p>
+}x, "output is correct");
+
+
+# Change default language and try requesting a non-existant language
+
+$Petal::LANGUAGE = 'fr';
+
+$template = new Petal (file => $file, base_dir => $data_dir, lang => 'zh');
+
+is($template->language, 'zh', 'correct language requested (zh)');
+
+$@ = '';
+$output = eval { $template->process(@args) };
+
+ok(!$@, "default language successfully used to select template");
+like($output, qr{^\s*
+  <p>\s+
+  <span>Bonjour,\sMonde\s\(fr\)</span>\s+
+  </p>
+}x, "output is correct");
+
+
Index: t/053_Opt_taint.t
===================================================================
RCS file: t/053_Opt_taint.t
diff -N t/053_Opt_taint.t
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ t/053_Opt_taint.t	27 May 2003 10:21:21 -0000	1.1
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+##############################################################################
+# Tests the 'taint' option to Petal->new.
+#
+
+use Test::More tests => 4;
+
+use warnings;
+use lib 'lib';
+
+use Petal;
+use File::Spec;
+
+my $data_dir = File::Spec->catdir('t', 'data');
+my $file     = 'if.html';
+
+# Confirm taint mode defaults to off
+
+my $template = new Petal (file => $file, base_dir => $data_dir);
+
+ok(!$template->taint, "taint mode defaults to off");
+
+
+# Confirm option can enable it
+
+$template = new Petal (file => $file, base_dir => $data_dir, taint => 1);
+
+ok($template->taint, "taint option turns it on");
+
+
+# Confirm global can enable it
+
+$Petal::TAINT = 1;
+$template = new Petal (file => $file, base_dir => $data_dir);
+
+ok($template->taint, "\$Petal::TAINT turns it on");
+
+
+# Confirm option can disable it
+
+$template = new Petal (file => $file, base_dir => $data_dir, taint => 0);
+
+ok(!$template->taint, "taint option turns it off again");
+
+
Index: t/054_Opt_cache.t
===================================================================
RCS file: t/054_Opt_cache.t
diff -N t/054_Opt_cache.t
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ t/054_Opt_cache.t	31 May 2003 09:09:40 -0000	1.1
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+##############################################################################
+# Tests the 'disk_cache' and 'memory_cache' options to Petal->new.
+#
+
+use Test::More tests => 8;
+
+use warnings;
+use lib 'lib';
+
+use Petal;
+use File::Spec;
+
+my $data_dir = File::Spec->catdir('t', 'data');
+my $file     = 'if.html';
+
+# Confirm disk cache defaults to on
+
+my $template = new Petal (file => $file, base_dir => $data_dir);
+
+ok($template->disk_cache, "disk_cache defaults to on");
+
+
+# Confirm option can disable it
+
+$template = new Petal (file => $file, base_dir => $data_dir, disk_cache => 0);
+
+ok(!$template->disk_cache, "disk_cache option turns it off");
+
+
+# Confirm global can disable it
+
+$Petal::DISK_CACHE = 0;
+$template = new Petal (file => $file, base_dir => $data_dir);
+
+ok(!$template->disk_cache, "\$Petal::DISK_CACHE turns it off");
+
+
+# Confirm option can enable it
+
+$template = new Petal (file => $file, base_dir => $data_dir, disk_cache => 1);
+
+ok($template->disk_cache, "disk_cache option turns it on again");
+
+
+
+# Confirm memory cache defaults to on
+
+$template = new Petal (file => $file, base_dir => $data_dir);
+
+ok($template->memory_cache, "memory_cache defaults to on");
+
+
+# Confirm option can disable it
+
+$template = new Petal (file => $file, base_dir => $data_dir, memory_cache => 0);
+
+ok(!$template->memory_cache, "memory_cache option turns it off");
+
+
+# Confirm global can disable it
+
+$Petal::MEMORY_CACHE = 0;
+$template = new Petal (file => $file, base_dir => $data_dir);
+
+ok(!$template->memory_cache, "\$Petal::MEMORY_CACHE turns it off");
+
+
+# Confirm option can enable it
+
+$template = new Petal (file => $file, base_dir => $data_dir, memory_cache => 1);
+
+ok($template->memory_cache, "memory_cache option turns it on again");
+
+
Index: t/055_Opt_maxinc.t
===================================================================
RCS file: t/055_Opt_maxinc.t
diff -N t/055_Opt_maxinc.t
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ t/055_Opt_maxinc.t	31 May 2003 10:42:39 -0000	1.1
@@ -0,0 +1,115 @@
+#!/usr/bin/perl
+##############################################################################
+# Tests the 'max_includes' option to Petal->new.
+#
+
+use Test::More tests => 12;
+
+use warnings;
+use lib 'lib';
+
+use Petal;
+use File::Spec;
+
+$Petal::DISK_CACHE = 0;
+$Petal::MEMORY_CACHE = 0;
+
+my $data_dir = File::Spec->catdir('t', 'data');
+my $file     = 'if.xml';
+my @args     = (
+  error => 'Altitude too low!',
+);
+
+
+# Confirm max_includes defaults to 30
+
+my $template = new Petal (file => $file, base_dir => $data_dir);
+
+is($template->max_includes, 30, "max_includes defaults to 30");
+
+
+# Confirm option can change it
+
+$template = new Petal (file => $file, base_dir => $data_dir, max_includes => 5);
+
+is($template->max_includes, 5, "max_includes option changes it");
+
+
+# Confirm global also changes it
+
+$Petal::MAX_INCLUDES = 20;
+$template = new Petal (file => $file, base_dir => $data_dir);
+
+is($template->max_includes, 20, "\$Petal::MAX_INCLUDES changes it too");
+
+
+# Confirm option can override changed global
+
+$template = new Petal (file => $file, base_dir => $data_dir, max_includes => 0);
+
+is($template->max_includes, 0, "max_includes option overrides changed global");
+
+
+# Check the value passed to included templates
+
+my $options = $template->include_opts;
+my $ref = eval "my \$hashref = { $options }";
+ok(!$@, "extracted params for included template");
+
+is($ref->{max_includes}, -1, "correct value for max_includes option");
+
+
+# Confirm that template with no includes is processed
+
+$@ = '';
+$output = eval {
+  $template->process(@args);
+};
+
+is($@, '', "template with no includes processed successfully");
+like($output, qr{^\s*
+  <div>\s*
+  <p\s+class=.error.\s*>Altitude\stoo\slow!</p>\s*
+  </div>
+}x, "output is correct");
+
+
+# Process a template with includes
+
+$data_dir = File::Spec->catdir('t', 'data', 'include');
+$file     = 'index_xinclude.xml';
+ at args     = ();
+
+$template = new Petal (file => $file, base_dir => $data_dir, max_includes => 1,
+input => 'XML', output => 'XML');
+
+$@ = '';
+$output = eval { $template->process(@args) };
+
+ok(!$@, "Template with includes successfully processed");
+like($output, qr{^\s*
+  <xml>\s+
+  <div>\s+
+  <p>__INCLUDED__</p>\s+
+  <p>__INCLUDED__</p>\s+
+  <p></p>\s+
+  </div>\s+
+  </xml>
+}x, "Output is correct");
+
+
+# Try again but with reduced max_includes setting
+
+$template = new Petal (file => $file, base_dir => $data_dir, max_includes => 0,
+input => 'XML', output => 'XML');
+
+$@ = '';
+$output = eval { $template->process(@args) };
+
+ok(!$@, "Template with includes successfully processed");
+like($output, qr{^\s*
+  <xml>\s+
+  ERROR:\s+MAX_INCLUDES\s+
+  </xml>
+}x, "Output is correct");
+


More information about the Petal mailing list