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

NAME

YASF - Yet Another String Formatter

VERSION

version 0.005

SYNOPSIS

    use YASF;
    use LWP::Simple;
    
    my $str = YASF->new('https://google.com/?q={search}');
    for my $term qw(<search terms>) {
        $results{$term} = get($str % { search => $term });
    }

DESCRIPTION

NOTE: This is an early release, and should be considered alpha-quality. The interface is subject to change in future versions.

YASF is a string-formatting module with functionality inspired by the % string operator and format method of the string class from Python.

YASF is not a direct port of these features, so they are not strictly identical in nature. Instead, YASF provides a handful of methods and an overload of some operators. This allows you to create your template string and interpolate it either with a direct call to format, or using % as an operator similar in syntax to Python.

Interpolation Syntax

The syntax for interpolating the pattern string is fairly simple:

    "some text {key} some more text"

When interpolated, the string {key} will be replaced with the value of key in the bindings for the interpolation.

Because the bindings can be almost any arbitrary Perl data structure, the keys may be multi-part, in a hierarchy denoted by dots (.):

    {key1.key2.key3}

The above will first look up key1 in the bindings, which it will expect to be a hash reference. The value that key1 yields will also be expected to be a hash reference, and key2 will be looked up in that table, and so on.

Keys may also be numeric, in which case it is expected that the corresponding binding being indexed is an array reference:

    {3}

Numeric and string keys may be interspersed, if the underlying data structure follows the same pattern:

    {key.0.name}

When a key expression is being evaluated into a value, an exception is thrown if the key-type is not appropriate for the node at that position in the data structure.

Keys may also be nested:

    {key.{subkey}}

In such a case, subkey is evaluated first, and the value from it is used to construct the full key.

The value from a nested key is not evaluated recursively, as this could lead to endless recursion. That is, if subkey evaluated to {key2}, it would not result in key2 being interpolated. Instead, a literal key of {key2} would be looked up on the hash reference that key yields.

Because curly braces are used to delimit keys, if you want a curly brace (of either orientation) in your string you will need to escape it with a backslash character:

    $str = YASF->new('\{{key}\}');

or

    $str = YASF->new("\\{{key}\\}");

The escaped characters will be converted when the template is compiled internally.

A key may be made up of any characters, though for readability it is recommended that you stay with alphanumerics. The only character that cannot be used in a key is the colon (:), as this is used to delimit a key from a formatting specification (see "Formatting Syntax").

Using Objects in the Bindings

If an element within the bindings data structure is an object, the key for that node will be used as the name of a method and called on the object. The method will be called with no parameters, and is expected to return a scalar value (be that an ordinary value or a reference).

For example:

    require HTTP::Daemon;
    
    my $str = YASF->new("{d.product_tokens} listening at {d.url}\n");
    my $d = HTTP::Daemon->new;
    print $str % { d => $d };

However, in this case there's no reason that the object itself cannot be the binding:

    require HTTP::Daemon;
    
    my $str = YASF->new("{product_tokens} listening at {url}\n");
    my $d = HTTP::Daemon->new;
    print $str % $d;

Formatting Syntax

Python's format also supports an extensive syntax for formatting the data that gets substituted. A format specification is given in the key, as a string sequence separated from the key itself by a colon:

    {key1.key2:6.2f}

This is not yet implemented in YASF, but will be added in a future release. For now, if a formatting string is detected it will be ignored.

OVERLOADED OPERATORS

The YASF class overrides a number of operators. Any operators not explicitly listed here will not fall back to any Perl defaults, they will instead trigger a run-time error.

%

The % operator causes the interpolation of a YASF template against the bindings that are passed following the operator:

    print $str % $data;
    # or
    print $str % { ... };
    # or
    print $str % [ ... ];

If the object has been bound to a data structure already (see the bind method, below), the explicitly-provided bindings take precedence over the object-level binding.

"" (stringification)

When a YASF object is stringified, one of two things happens:

  1. If the object is bound to a data structure via bind (or from a bindings argument in the constructor), it is interpolated against these bindings and the resulting string is used.

  2. If the object has no object-level bindings, then the uninterpolated template string will be used.

You do not need to explicitly use double-quotes to trigger this; anywhere the object would be used as a string (printing, hash keys, etc.), this will be the behavior.

String Comparison (cmp, eq, etc.)

The string comparison operators (cmp, eq, ne, lt, le, gt, ge) are all overloaded to stringify the YASF object before doing the actual comparison. As with "", if no object-level bindings exist then the stringification is just the template string itself.

String Concatenation (. and .=)

The string concatenation operators, . and .=, are also overloaded. The . operator always stringifies the object involved in the operation (with the same effect as the comparison operators when no bindings exist), regardless of which side of the operator it appears on.

The .= operator, however, can only accept a YASF object on the right-hand side. This is because YASF template strings are (currently) read-only. If you try to use an object on the left-hand side of .=, an exception is thrown.

Other operators may be overloaded in the future, as deemed useful or necessary.

SUBROUTINES/METHODS

The following methods and subroutines are provided by this module:

new

This is the object constructor. It takes one required argument and optional named arguments following that. The required argument is the string template that will be interpolated. The named arguments may be passed as a hash reference or as key/value pairs. The following parameters are recognized:

bindings

Specifies the bindings for the object. The value must be an array reference, a hash reference, or an object referent.

on_undef

Specifies the behavior for the interpolation when the key being interpolated has no value in the bindings (i.e., the value is undef). The possible values are:

warn

A warning is issued (using carp), similar to what Perl would do for interpolating an undefined value. However, the message specifies the token that had no value.

die

An error is issued (using croak), with the same message as warn generates.

ignore

The missing token is ignored, and a null string is inserted in its place.

token

The missing token is left in place, unchanged.

Note that if the token is the result of a "compound token", the token you see in the messages or left intact within the string may differ from what is in the template.

The default value is warn.

The return value is a new object of the class. Any errors will be signaled via croak.

bind

This method binds the object to a data structure reference. When an object has a bound data structure, it can be formatted or interpolated in a string without needed explicit bindings to be provided. This can be useful when binding to a hash reference whose contents will continually change, or an object whose internal state is continuously changing.

The method takes one required argument, the new bindings. This must be a reference to a hash, to an array, or to an object. If the argument does not meet these criteria (or is not given), an exception is thrown via croak.

If an object has a bound data structure, but is interpolated with % or format with an explicit set of bindings, the explicit bindings will supercede the internal bindings (but without replacing it permanently).

You can unbind data from the object by calling bind with undef as the argument.

format

This method formats the template within the object, using either bindings provided as an argument or using the object-level bindings that are already set.

bindings

A static accessor that returns the current object-level bindings data structure, or undef if there are no object-level bindings. Cannot be used to set the bindings; see bind, above.

template

A static accessor that returns the template string that this object is encapsulating. Cannot be used to change the template.

(At present, there is no way to change the template of an object. You can only create a new object.)

on_undef

A static accessor that returns the value of the on_undef property of the object. Like the template property, this cannot be changed on an object once the object is created.

YASF

This is a convenience function for quickly creating an unbound YASF object. It requires the template string as the only parameter and returns a new object. This can be useful for one-off usage, etc., and is a few characters shorter than calling new directly:

    require HTTP::Daemon;
    
    my $d = HTTP::Daemon->new;
    print YASF "{product_tokens} listening at {url}\n" % $d;

The only real difference between this and new, is that you cannot pass any additional arguments to YASF.

The YASF function is not exported by default; you must explicitly import it:

    use YASF 'YASF';

DIAGNOSTICS

Presently, all errors are signaled via the croak function. This may change as the module evolves.

BUGS

As this is alpha software, the likelihood of bugs is pretty close to 100%. Please report any issues you find to either the CPAN RT instance or to the GitHub issues page:

SUPPORT

LICENSE AND COPYRIGHT

This file and the code within are copyright © 2016 by Randy J. Ray.

Copying and distribution are permitted under the terms of the Artistic License 1.0 or the GNU GPL 1. See the file LICENSE in the distribution of this module.

AUTHOR

Randy J. Ray <rjray@blackperl.com>