The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Text::Macros.pm - a class implementing text macros.

SYNOPSIS

use Text::Macros;

 # poetic:
 my $macro_expander = new Text::Macros qw( {{ }} );
 $text = expand_macros $macro_expander $data_object, $text;

 # noisy:
 $macro_expander = Text::Macros->new( "\Q[[", "\Q]]", 1 );
 print $macro_expander->expand_macros( $data_object, $text );

DESCRIPTION

Typical usage might look like this:

     my $template = <<EOF;
       To: [[ RecipientEmail ]]
       From: [[ SenderEmail ]]
       Subject: Payment Past Due on Account # [[ AccountNum ]]
    
       Dear [[ RecipientName ]]:
       Your payment of [[ PaymentAmount ]] is [[ DaysPastDue ]] days past due.
     EOF
    
     # get a data object from somewhere, e.g.:
     my $data_object = $database->get_record_object( 'acctnum' => $account_num );
    
     # make a macro expander:
     my $macro_expander = Text::Macros->new( "\Q[[", "\Q]]" );
    
     # expand the macros in the template:
     my $email_text = $macro_expander->expand_macros( $data_object, $template );

To support this, a "data object" would need to exist which would need to define methods which will be used as macro names, e.g. like this:

     package RecordObject;
     sub RecipientEmail { $_[0]->{'RecipientEmail'} }
     sub SenderEmail    { $_[0]->{'SenderEmail'}    }
     sub AccountNum     { $_[0]->{'AccountNum'}     }
     sub RecipientName  { $_[0]->{'RecipientName'}  }
     sub PaymentAmount  { $_[0]->{'PaymentAmount'}  }
     sub DaysPastDue    { $_[0]->{'DaysPastDue'}    }

Alternatively, the data object class might have AUTOLOAD defined, for example like this:

     package RecordObject;
     sub AUTOLOAD {
      my $self = shift;
      my $name = $AUTOLOAD;
      $name =~ s/.*:://;
      $self->{$name}
     }

If this is the case, then the macro expander should be instructed not to assert that the macro names encountered are valid for the object -- since CAN might fail, even though the calls will be handled by AUTOLOAD. To do this, pass a true value for the third value to the constructor:

     my $macro_expander = Text::Macros->new( "\Q[[", "\Q]]", 1 );

Macros can take arguments. Any strings which occur inside the macro text after the macro name will be passed as arguments to the macro method call. In the current implementation, such strings must be separated by newlines (thus allowing whitespace, but not newlines, in the argument values). In a future implementation, the rules for determining how the macro text will be split up into arguments will be user-defined, with perhaps the current style as the default.

Example:

     $macro_expander = new Macros qw( {{ }} );
    
     print $macro_expander->expand_macros( $cgi_query, 
       "You entered {{
          param
             Name
       }} as your name."
     );

This will replace the substring "macro( param Name )" with the result of calling

     $cgi_query->param("Name")

(Obviously this example is a little contrived.)

METHODS

The Constructor

     Text::Macros->new( $open_delim, $close_delim, $no_CAN_check );

The delimiters are regular expressions; this gives you the greatest power in determining how macros are to be detected in the text. But it means that if you simply want them to be considered literal strings, then you must quotemeta them.

Since the macro expander will be calling object methods, you have an option: do you want any encountered macro names to be required to be valid for the given object? Or do you have some kind of autoloading in effect, which will handle undefined methods?

If you have some kind of autoloading, pass a true value for the third argument to new(). If you want the expander to assert CAN for each method, pass false (the default).

The Main Method: Expand Macros

     $text = $macro_expander->expand_macros( $data_object, $text );

The $data_object argument is not an object of the Macros package. Rather, this is the object upon which the macro will be called as a method.

expand_macros() returns the result of replacing all the macros it finds with their appropriate expansions. Note that recursion can occur; that is, if the expansion of a macro results in text which also contains a valid macro, that new macro will also be expanded. The text will be scanned for macros, and those macros will be expanded, until none are found.

A Utility Method: Call Macro

     $macro_expander->call_macro( $data_object, $macro_name, @arguments );

This is used internally by expand_macros(), but you can call it directly if you wish.

Essentially all this does is the following:

     $macro_expander->call_macro( $data_object, $macro_name, @arguments );

results in the call:

     $data_object->$macro_name( @arguments );

All the macros supported by the data object can be predefined, or you might have some kind of autoloading mechanism in place for it. If you have autoloading in effect, you should have passed a true value as the third argument to new(). If you pass false (the default), the call_macro() will check to see that the object CAN do the method; and if it can't an exception will be thrown.

Note: data objects' macro methods must return a string. They can take any number of arguments, which will all be strings.

AUTHOR

jdporter@min.net (John Porter)

This program is free software; you may redistribute it and/or modify it under the same terms as Perl itself.