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

YASF - Yet Another String Formatter

VERSION

version 0.001

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.

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.

This is not provided in this initial release of 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 binding argument in the constructor), it is interpolated against these bindings and the resulting string is used.

  2. If the object has no object-level binding, 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.

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. Currently, only one named parameter is recognized:

binding

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

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 binding. 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 binding, the explicit binding will supercede the internal binding (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.

binding

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.)

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>