The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

PAB3 - Perl Application Builder / Version 3

SYNOPSIS

  use PAB3;

DESCRIPTION

PAB3 provides a framework for building rapid applications with Perl. It includes a template handler for producing output. This part is defined here.

Examples

Following example loads a template from template1.tpx, does a loop over the %ENV variable and prints the output to STDOUT.

- perl script -

  #!/usr/bin/perl -w
  
  use PAB3;
  
  my $pab = PAB3->new();
  
  $pab->make_script_and_run( 'template1.tpx' );

- template1.tpx -

  main script:
  
  <*= $0 *>
  
  show the environment:
  
  <* LOOP HASH %ENV *>
  <* PRINT "[$_] => " . $ENV{$_} . "\n" *>
  <* END LOOP *>
  
  # or - with loop directive
  
  <* loop foreach( keys %ENV ) *>
  <* = "[$_] => " . $ENV{$_} . "\n" *>
  <* end loop *>
  
  # or - perl like
  
  <* foreach( keys %ENV ) { *>
  <* print "[$_] => " . $ENV{$_} . "\n" *>
  <* } *>
  
  # or - with internal function
  
  <* &PAB3::print_r( \%ENV ) *>

METHODS

setenv ()

Set some useful variables to the interpreters environment

these variables are:

  $ENV{'SCRIPT_PATH'}   : path to the main script
  $ENV{'SCRIPT'}        : name of the main script
new ( [%arg] )

Create a new instance of the PAB3 (template handler) class.

Posible arguments are:

  path_cache     => path to save parsed templates
  path_template  => path to the template files
  auto_cache     => create cache files automatically. 'path_cache' is required
  prg_start      => begin of program sequence, default is '<*'
  prg_end        => end of program sequence, default is '*>'
  cmd_sep        => command separator, to define more directives in one program
                    sequence, default is ';;'
  record_name    => name of default record in loops, default is '$_'
  logger         => reference to a PAB3::Logger class
  warn           => warn on error, default is OFF
  die            => die on error, default is ON
  class_name     => name of the variable for this class. eg '$pab'
                    It is needed when templates including templates. If its
                    undefined, a variable $PAB3::_CURRENT will
                    be used as a reference to the current PAB3 class.

Example:

  $pab = PAB3->new(
      'path_cache'    => '/path/to/cache',
      'path_template' => '/path/to/template-files',
  );
parse_template ( $template )

Parse the template given at $template and return Perl code. If $template points to an existing file, the content of the file will be parsed. In the other case the content of the variable will be used as template.

Example:

  $code = $pab->parse_template( '<*= $0 *>' );
  eval( $code );
make_script_and_run ( $template )
make_script_and_run ( $template, $cache )
make_script_and_run ( $template, $cache, $package )

Parse the template given at $template, generate Perl code and execute it. If $cache is set to a filename or "auto_cache" is enabled, the Perl code will be saved into a file. If the cache file already exists and the template has not been changed, the template will not be parsed again. Third parameter $package defines the package where the Perl code should be executed. If $package has not been specified the package from caller(0) is used.

Returns TRUE on success or FALSE on error.

Example:

  # parse the template and execute it
  $pab->make_script_and_run( 'template.htm' );
  
  # parse the template, cache it into file and execute it
  $pab->make_script_and_run( 'template.htm', 'template.pl' );
register_loop ( $id, $source, $s_type )
register_loop ( $id, $source, $s_type, $record, $r_type )
register_loop ( $id, $source, $s_type, $record, $r_type, $object )
register_loop ( $id, $source, $s_type, $record, $r_type, $object, $arg )
register_loop ( $id, $source, $s_type, $record, $r_type, $object, $arg, $fixed )

Registers a loop to be used inside templates.

Loops need not registered here. It also can be declared in the template.

Arguments

$id

Loop identifier

$source

the source of the loop.

$s_type

the type of the source. One of these constants: PAB_ARRAY, PAB_HASH or PAB_FUNC

$record

the record in the loop.

$r_type

the type of the record. One of these constants: PAB_SCALAR, PAB_FUNC, PAB_ARRAY or PAB_HASH

$object

a object for $source or $record functions.

$arg

arguments passed to the source if it is a function, as an array reference

$fixed

installes the loop as fixed. it can not be overwritten

Combinations

Following combinations are possible:

   --------------------------------------
  |   Source   |   Record   |   Object   |
   --------------------------------------
  | PAB_ARRAY  | PAB_SCALAR |     -      |
  | PAB_ARRAY  | PAB_FUNC   |    yes     |
  | PAB_HASH   | PAB_SCALAR |     -      |
  | PAB_HASH   | PAB_FUNC   |    yes     |
  | PAB_FUNC   | PAB_SCALAR |    yes     |
  | PAB_FUNC   | PAB_ARRAY  |    yes     |
  | PAB_FUNC   | PAB_HASH   |    yes     |
   --------------------------------------

The constants can be accessed in three ways, by the object like $pab->SCALAR, by the module like PAB3::SCALAR and by export like PAB_SCALAR. See more at EXPORTS

Source as Array, Record as Scalar

  # definition
  register_loop( 'id', 'source' => PAB_ARRAY, 'record' => PAB_SCALAR )
  
  # result
  foreach $record( @source ) {
  }

Source as Array, Record as Function

  # definition
  register_loop( 'id', 'source' => PAB_ARRAY, 'record' => PAB_FUNC )
  
  # result
  foreach $<iv>( @source ) {
       &record( $<iv> [, <arg>] );
  }

Source as Array, Record as Function, Object

  # definition
  register_loop( 'id', 'source' => PAB_ARRAY, 'record' => PAB_FUNC, 'object' )
  
  # result
  foreach $<iv>( @source ) {
       $object->record( $<iv> [, <arg>] );
  }

Source as Hash, Record as Scalar

  # definition
  register_loop( 'id', 'source' => PAB_HASH, 'record' => PAB_SCALAR )
  
  # result
  foreach $record( keys %source ) {
  }

Source as Hash, Record as Function

  # definition
  register_loop( 'id', 'source' => PAB_HASH, 'record' => PAB_FUNC )
  
  # result
  foreach $<iv>( keys %source ) {
      &record( $<iv> [, <arg>] );
  }

Source as Hash, Record as Function, Object

  # definition
  register_loop( 'id', 'source' => PAB_HASH, 'record' => PAB_FUNC, 'object' )
  
  # result
  foreach $<iv>( keys %source ) {
      $object->record( $<iv> [, <arg>] );
  }

Source as Function, Record as Scalar

  # definition
  register_loop( 'id', 'source' => PAB_FUNC, 'record' => PAB_SCALAR )
  
  # result
  while( $record = &source( [<arg>] ) ) {
  }

Source as Function, Record as Scalar, Object

  # definition
  register_loop( 'id', 'source' => PAB_FUNC, 'record' => PAB_SCALAR, 'object' )
  
  # result
  while( $record = $object->source( [<arg>] ) ) {
  }

Source as Function, Record as Array

  # definition
  register_loop( 'id', 'source' => PAB_FUNC, 'record' => PAB_ARRAY )
  
  # result
  while( @record = &source( [<arg>] ) ) {
  }

Source as Function, Record as Hash

  # definition
  register_loop( 'id', 'source' => PAB_FUNC, 'record' => PAB_HASH )
  
  # result
  while( %record = &source( [<arg>] ) ) {
  }

Source as Function, Record as Function

  # definition
  register_loop( 'id', 'source' => PAB_FUNC, 'record' => PAB_FUNC )
  
  # result
  while( $<iv> = &source( [<arg>] ) ) {
      &record( $<iv> [, <arg>] );
  }

Source as Function, Record as Function, Object

  # definition
  register_loop( 'id', 'source' => PAB_FUNC, 'record' => PAB_FUNC, 'object' )
  
  # result
  while( $<iv> = $object->source( [<arg>] ) ) {
      $object->record( $<iv> [, <arg>] );
  }

Examples

Example of a loop over an array with record as subroutine:

  use PAB3 qw(:const);
  
  my @Array1 = ( 1, 2, 3 );
  
  $pab->register_loop(
      'MYLOOP', 'Array1' => PAB_ARRAY , 'init_record' => PAB_FUNC
  );
  
  sub init_record {
      $Record = shift;
      ...
  }

Example of an enumeration loop:

  $pab->register_loop(
      'MYLOOP', 'enum' => PAB_FUNC, 'Record' => PAB_SCALAR
  );
  
  $Counter = 10;
  
  sub enum {
       if( $Counter == 0 ) {
           $Counter = 10;
           return 0;
       }
       return $Counter --;
  }

--- inside the template ---

  <* LOOP MYLOOP *>
  <* PRINT $Record . "\n" *>
  <* END LOOP *>
  

See also

directive LOOP.

add_hashmap ( $loop_id, $hashname, $fieldmap )
add_hashmap ( $loop_id, $hashname, $fieldmap, $fm_save )

Add a hashmap to the parser. Hashmaps are designed to translate hashes in templates into arrays in the parsed script. For example: you use $var->{'Key'} in your template. With a hashmap you can convert it into an array like $var->[0] without taking care of the indices. This can noticable make the execution time faster.

Parameters

$loop_id

If it is defined only the program sequences inside the loop will be converted, otherwise the complete template is used.

$hashname

Specifies the name of the hash to be translated.

$fieldmap

Can be a reference to an array of fieldnames or a reference to a hash containing fieldnames as keys and the assiocated indices as values.

$fm_save

If $fieldmap is an arrayref, the new generated hashmap can be saved in this parameter.

Return Values

Returns TRUE on success or FALSE if it fails.

Example

  @data = (
      [ 'Smith', 'John', 33 ],
      [ 'Thomson', 'Peggy', 45 ],
      [ 'Johanson', 'Gustav', 27 ],
  );
  @fields = ( 'Name', 'Prename', 'Age' );
  
  # map all $per items in loop "Person" from hash to array
  $pab->add_hashmap( 'Person', 'per', \@fields );
  
  $pab->make_script_and_run( 'template' );

--- template ---

  <* LOOP Person foreach $per(@data) *>
  <* = $per->{'Prename'} . ' ' . $per->{'Name'} *> is <* = $per->{'Age'} *> years old
  <* END LOOP *>

Warning

If an empty result from a db query is returned, no hashmap can be created. If your template needs to be compiled and uses hashmaps, which are empty, you will get an error. You should recompile affected templates once by running them with valid hashmaps. Or you can use a hashmap cache handler. See more at PAB3::HashmapCache.

reset ()

Clears loop definitions and hashmaps in the PAB3 class.

require ( $filename )

Loads the required file and compiles it into a package and runs it once it has been changed.

Example:

  &PAB3::require( 'config.inc.pl' );
  
  - or -
  
  $pab->require( 'foo.pl' );
require_and_run ( $filename )

Loads the required file and compiles it into a package once it has been changed. Runs it on every call.

Example:

  &PAB3::require_and_run( 'dosomething.pl' );
  
  - or -
  
  $pab->require_and_run( 'foo.pl' );

Prints human-readable information about one or more variables

Example:

  &PAB3::print_r( \%ENV );

PAB3 TEMPLATE LANGUAGE SYNTAX

The little extended language is needed to extract the PAB3 and Perl elements from the rest of the template. By default program sequences are included in <* ... *> and directives are separated by ;; . These parameters can be overwritten by new().

Examples

  <p><* PRINT localtime *></p>
  
  <*
      my $pos = int( rand( 3 ) );
      my @text =
          (
              'I wish you a nice day.',
              'Was happy to see you.',
              'Would be nice to see you back.'
          )
  *>
  
  <* if $pos == 0 *>
  <p>I wish you a nice day.</p>
  <* elsif $pos == 1 *>
  <p>Was happy to see you.</p>
  <* else *>
  <p>Would be nice to see you back.</p>
  <* end if *>
  
  <!-- or shortly -->
  
  <p><* = $text[$pos] *></p>

Directives

The following list explains the directives available within the PAB3 template system. All directives are case insensitive. The description is using the default program and command separators.

= <expression>

Prints the output returned from <expression>.

  <* PRINT <expression> *>

or shortly

  <* = <expression>     *>

Example:

  <* print $0, "\n" *>
  <* PRINT 'Now: ' . time . "\n" *>
  <* = 'Or formated: ' . &PAB3::Utils::strftime( '%c', time ) *>

Performance notice: Combining multiple expressions into one string can speed up the print process. For example:

   faster:
   <* print $x . ' some data: ' . $str *>
   
   slower:
   <* print $x, ' some data: ', $str *>

Joining several PRINT directives into one directive does not realy affect to the speed, because the optimizer will do it automatically.

: <expression>
<expression>

Executes <expression> wihout printing the output. This is also the default action if no directive has been specified.

  <* : <expression> *>

or

  <* <expression>     *>

Example:

  <* : $my_var = 1 *>
  <* : &my_sub( $my_var ) *>
  <* $x = $y *>
IF <condition>
ELSIF <condition>
ELSE
END IF

Enclosed block is processed if the <condition> is true.

  <* IF <condition>    *>
  ...
  <* ELSIF <condition> *>
  ...
  <* ELSE              *>
  ...
  <* END IF            *>

Example:

  <* if ( $rowpos % 2 ) == 0 *>
  <tr class="even">
  <* else *>
  <tr class="odd">
  <* end if *>
INCLUDE <template>

Process another template file. Please note to "class_name" at new().

  <* INCLUDE <expression> *>

Example:

  <* include banner.tpl *>
  
  - or -
  
  <* $banner = int( rand( 3 ) ) + 1 *>
  <* include banner${banner}.tpl *>
LOOP
LOOP <id>
LOOP <id> <exp1>
LOOP <id> <exp1> <exp2>
LOOP <exp1>
END LOOP

Performs a predefined loop, a loop registered by register_loop or an unregistered loop. Predefined loops are ARRAY and HASH. These are using <exp1> as source and <exp2> as record. In registered loops <exp1> optionally can be used as record and <exp2> as an argument. Unregistered loops can be used with or without <id>. The loop must be defined in <exp1> without brackets. The difference between registered and unregistered loops is that registered loops are defined in the perl script and unregistered loops are defined in the template.

  <* LOOP <id> [<exp1> [<exp2>]] *>
  ...
  <* END LOOP                    *>

Example of a predefined ARRAY loop

  <* LOOP ARRAY @INC $_ *>
  <*   PRINT $_ . "\n" *>
  <* END LOOP *>

Example of a predefined HASH loop

  <* LOOP HASH %ENV $_ *>
  <*   PRINT $_ . ' = ' . $ENV{$_} . "\n" *>
  <* END LOOP *>

Example of a registered loop

--- perl script ---

  use PAB3 qw(:const);
  
  $pab = PAB3->new( ... );
  
  @data = (
      { 'Name' => 'Smith',    'Prename' => 'John',   'Age' => 33 },
      { 'Name' => 'Thomson',  'Prename' => 'Peggy',  'Age' => 45 },
      { 'Name' => 'Johanson', 'Prename' => 'Gustav', 'Age' => 27 },
  );
  
  $pab->register_loop(
       'PERSON', 'data' => PAB_ARRAY, 'per' => PAB_HASH
  );
  
  $pab->make_script_and_run( 'template' );

--- template ---

  <* LOOP PERSON *>
  <* = $per->{'Prename'} . ' ' . $per->{'Name'} *> is <* = $per->{'Age'} *> years old
  <* END LOOP PERSON *>

Example of a registered loop with hashmap

--- perl script ---

  use PAB3 qw(:const);
  
  $pab = PAB3->new( ... );
  
  @data = (
      [ 'Smith', 'John', 33 ],
      [ 'Thomson', 'Peggy', 45 ],
      [ 'Johanson', 'Gustav', 27 ],
  );
  
  @fields = ( 'Name', 'Prename', 'Age' );
  
  $pab->register_loop(
       'PERSON', 'data' => PAB_ARRAY, 'per' => PAB_ARRAY
  );
  $pab->add_hashmap( 'PERSON', 'per', \@fields );
  
  $pab->make_script_and_run( 'template' );

--- template ---

  <* LOOP PERSON *>
  <* = $per->{'Prename'} . ' ' . $per->{'Name'} *> is <* = $per->{'Age'} *> years old
  <* END LOOP PERSON *>

Example of unregistered loops

--- perl script ---

  use PAB3 qw(:const);
  
  $pab = PAB3->new( ... );
  
  @h_data = (
      { 'Name' => 'Smith',    'Prename' => 'John',   'Age' => 33 },
      { 'Name' => 'Thomson',  'Prename' => 'Peggy',  'Age' => 45 },
      { 'Name' => 'Johanson', 'Prename' => 'Gustav', 'Age' => 27 },
  );
  @a_data = (
      [ 'Smith',    'John',   33 ],
      [ 'Thomson',  'Peggy',  45 ],
      [ 'Johanson', 'Gustav', 27 ],
  );
  @fields = ( 'Name', 'Prename', 'Age' );
  
  $pab->add_hashmap( 'PERSON_MAPPED', 'per', \@fields );
  
  $pab->make_script_and_run( 'template' );

--- template ---

  # without id
  <* LOOP foreach $per( @h_data ) *>
  <* = $per->{'Prename'} . ' ' . $per->{'Name'} *> is <* = $per->{'Age'} *> years old
  <* END LOOP *>
  
  # with id
  <* LOOP PERSON foreach $per( @h_data ) *>
  <* = $per->{'Prename'} . ' ' . $per->{'Name'} *> is <* = $per->{'Age'} *> years old
  <* END LOOP PERSON *>
  
  # with hashmap
  <* LOOP PERSON_MAPPED foreach $per (@a_data) *>
  <* = $per->{'Prename'} . ' ' . $per->{'Name'} *> is <* = $per->{'Age'} *> years old
  <* END LOOP *>

See also register_loop(), add_hashmap()

SUB <expression>
END SUB

Defines a subroutine in the style local <expression> = sub { ... };.

  <* SUB <expression> *>
  ...
  <* END SUB          *>

Example:

  <* SUB *action *>
  <* PRINT $ENV{'SCRIPT'} . '?do=' . ( $_[0] || '' ) *>
  <* END SUB *>
  
  <a href="<* &action( 'open' ) *>">Open</a>
COMMENTS

Comments are copied.

  <* #... *>

Example:

  # comment out directives.
  <* #print $foo *>
!X <directive>

This special directive prints the content in <directive> as a new directive. It can be useful to generate templates from templates.

  <* !X <directive> *>

Example:

  <* $foo = '$bar' *>
  
  <* !X print $foo *>
  
  produces: <* print $bar *>
  
  <* !X print \$foo *>
  
  produces: <* print $foo *>

EXPORTS

By default nothing is exported. To export constants like PAB_SCALAR, etc use the export tag ":const". To export functions and constants use the export tag ":default".

AUTHORS

Christian Mueller <christian_at_hbr1.com>

COPYRIGHT

The PAB3 module is free software. You may distribute under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file.