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

Data::Tubes::Plugin::Plumbing

DESCRIPTION

This module contains tubes factories for handling general plumbing requirements, e.g. put some other tubes in a sequence.

FUNCTIONS

alternatives
   $tube = alternatives(@tubes); # OR
   $tube = alternatives(@tubes, \%args);

consider a series of tubes as different alternatives, to be triggered in order until one of them returns something.

In simple terms, the first item in @tubes is called with the input record. If it returns nothing, the second item in @tubes is tried, and so on. The first one to return something (i.e. a record, or multiple ones) wins and its result is returned. Think of it as some OR function in tubeland.

If no tube returns anything, the tube itself returns nothing.

You can set the following options with the optional %args:

name

set a name for the dispatcher, might be useful while debugging if you plan to use more than one dispatcher.

dispatch
   my $tube = dispatch(%args); # OR
   my $tube = dispatch(\%args);

this function decides a sub-tube to use for dispatching a specific record. The selection of the sub-tube is performed through two different mechanisms:

-

first, a selector function is applied to the input record, optionally defaulting to a configurable value. This selector is a string that MUST uniquely identify the output tube where the record should be dispatched;

-

then, if the tube associated to the selector is already known, it will be used for the dispatching. Otherwise, a factory will be used to get a new handler tube for the specific selector, if possible.

The arguments passed through %args allow you to define the selector and the factory in a flexible way. Available options are:

default

this allows defining the default selection key when none is available (i.e. it would be the undefined value). If set to an undef value, lack of a selector will throw an exception. Defaults to undef;

factory

set a sub reference to generate new tubes when needed. The factory function will be fed with the specific selection key as the first argument, and the record as the second argument, and it is supposed to return a valid tube (although it might throw an exception by itself, of course);

handlers

this is a quick way to set a simple factory that just returns elements from a hash reference (that is passed as value). If this is used, every key that is not present in the hash will throw an exception. If factory is present, this field will be ignored;

key

this is a quick way to specify a selector function. It points to either a string/integer, or an array containing a sequence of strings/integers; these items will be used to access the provided $record in a "visit" that uses an item at each step. Example:

   $record = {aref => [1, 2, {foo => 'bar'}]};
   @key = qw< aref 2 foo >; # this will select 'bar' above

If the option selector is passed, this field will be ignored;

name

set a name for the dispatcher, might be useful while debugging if you plan to use more than one dispatcher;

selector

set to a subroutine reference that will be passed the input record and SHOULD provide a string back, that will uniquely identify a tube.

One between selector or key MUST be provided. One between factory and handlers MUST be provided.

fallback
   $tube = fallback(@tubes); # OR
   $tube = fallback(@tubes, \%args);

consider a series of tubes as different alternatives, to be triggered in order until one of them does not throw an exception.

In simple terms, the first item in @tubes is called with the input record. If it throws an exception, the second item in @tubes is tried, and so on. The first one to NOT throw na exception wins and its result is returned. Think of it as some OR function in tubeland, applied to exception throwing. This function is very similar to "alternatives", although there is a different exception handling here.

Returns nothing if all tubes throw an exception, otherwise it returns the return value of the first tube that does not throw an exception, and ignores the rest of the tubes.

The exception handling is performed via Try::Tiny.

You can set the following options with the optional %args:

catch

an optional sub reference to be called when an exception is catched. The sub is called like this:

   $catcher->($exception, $record);

The return value of this function is ignored.

name

set a name for the dispatcher, might be useful while debugging if you plan to use more than one dispatcher.

logger
   my $tube = logger(%args); # OR
   my $tube = logger(\%args);

this function generates a tube that is useful for logging things. You can pass the following arguments:

loglevel

the level where the logging should happen. See Log::Log4perl::Tiny for the available ones. You can pass either the numeric value of the log level (as exported via :levels by Log::Log4perl::Tiny) or the log level name (uppercase, e.g. INFO or DEBUG);

name

the name assigned to the logger tube, might be useful while debugging;

target

a facility to isolate part of the target record and/or produce a message suitable for logging.

If not provided or undefined (which is the default), the whole input record will be passed to the logger function. This is probably what you don't want in the vast majority of cases, as you will only see a strange address printed out. Works fine if the input record is something printable, anyway.

The most flexible thing that you can pass is a sub reference. This will receive the input record, and SHOULD return back a string that will be printed in the log stream.

You can also provide either a string or a sequence of strings in an array reference. In this case, the record will be visited using these keys, much in the same way as described for "dispatch" above. Again, you should be pretty sure that the leaf value found after this traversal is something meaningful for printing.

The generated tube always returns back the input record, unchanged.

pipeline
   $tube = pipeline(@tubes); # OR
   $tube = pipeline(@tubes, \%args);

this is a thin wrapper around "sequence", added to avoid changing its signature. It is the same as calling:

   $tube = sequence(tubes => \@tubes); # OR
   $tube = sequence(%args, tubes => \@tubes);

(depending on what you provide as input), only a bit more natural.

sequence
   my $tube = sequence(%args); # OR
   my $tube = sequence(\%args);

this function takes a sequence of tubes (i.e. functions that are compliant with the tube definition) and returns a tube that provides serialization of the operations, in the order as the passed list.

The returned tube is such that it will always return an iterator back (in particular, it will return two elements, the first is the string iterator and the second is an iterator sub reference).

Arguments can be passed through a single reference to a hash, or as a sequence of key/value pairs. The following options are supported:

name

set a name for the sequence, which might come handy when debugging. Defaults to sequence;

logger

can be optionally set to a function that will be called for each input record, being passed the record itself and a reference to the hash of arguments. Use this if you want to do some logging, ignore otherwise;

tubes

an array reference containing the list of tubes part of the sequence. These can be either direct tubes (i.e. references to subroutines) or definitions suitable for calling tube in Data::Tubes;

The sequence makes no assumption as to the input record, although the first element in the provided list might do.

Note that the last tube in the sequence might actually return an output record with an undef or otherwise false value (Perl-wise). To cope with this, when called in list context, the iterator is guaranteed to either return one single output record, or the empty list when the iterator is exhausted.

The suggested idiom for taking items from the iterator is then the following:

   my $it1 = $sequence1->($input_record)->{iterator};
   while (my ($output_record) = $it1->()) {
      # work with $output_record here, it's your output record!
   }

   # if you're waiting for a single output record, use if
   my $it2 = $sequence2->($input_record)->{iterator};
   if (my ($output_record) = $it2->()) {
      # work with $output_record here, it's your output record!
   }

BUGS AND LIMITATIONS

Report bugs either through RT or GitHub (patches welcome).

AUTHOR

Flavio Poletti <polettix@cpan.org>

COPYRIGHT AND LICENSE

Copyright (C) 2016 by Flavio Poletti <polettix@cpan.org>

This module is free software. You can redistribute it and/or modify it under the terms of the Artistic License 2.0.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.