Creative Commons License
Excepto donde se indique otra cosa, todo el contenido de este lugar está bajo una licencia de Creative Commons.
Taquiones > software > factory > Iterator::BreakOn

Iterator::BreakOn

Este módulo Perl permite crear rupturas de control en iteradores de datos, especialmente aquellos que trabajan con DBI ó alguno de sus derivados (Class::DBI, DBIx::Class, ... ).

    1     use Iterator::BreakOn;
    2 
    3     my $datasource = get_generic_data_source();
    4 
    5     my $iter = Iterator::BreakOn->new(
    6                     datasource => $datasource,
    7                     break_before => [ qw(location) ],
    8                     break_after => [ qw(zipcode) ],
    9                     on_last_item    =>  sub { print "Finnished !"; },
   10                     );
   11 
   12     #
   13     # There are three uses modes:
   14     #
   15     #   Fully automatic mode: useless if not defined code for breaks
   16     $iter->run();
   17 
   18     #   Semi-automatic mode: get only the item
   19     while (my $data_item = $iter->next()) {
   20         # do something ...
   21 
   22     }
   23 
   24     #   Manual mode: get every event
   25     while (my $event = $iter->next_event()) {
   26         if ($event eq 'before_location') {
   27             # do something before a new location comes
   28 
   29         }
   30         elsif ($event eq 'after_zipcode')) {
   31             # do something after the last zipcode reached
   32 
   33         }
   34         elsif ($event eq 'next_item' ) {
   35             # get the item (including the first and last items)
   36             my $data = $iter->next();
   37 
   38             # and do something whit him
   39 
   40         }
   41         elsif ($event eq 'last_item') {
   42             # and do something when the end of data reached
   43 
   44         }
   45     } # end while

Se asume que la fuente de datos está ordenada -al menos- por los campos de ruptura, puesto que no se comprueba si éstos están en un orden concreto, simplemente si cambian su valor con respecto al anterior registro.

Como casos especiales, claro está, aparece el primer registro, el último y una lista vacía, aunque la dinámica de creación de eventos se mantiene.

Creación de objetos

Existe el tradicional método new para crear un objeto de este tipo con los siguientes atributos:

  • datasource: referencia a un objeto con un método next disponible para recibir uno a uno los datos (obligatorio).
  • getmethod: nombre del método para recuperar valores individuales de cada elemento leído. De esto se deduce que cada llamada al método next anteriormente mencionado debe retornar un objeto con dicha capacidad. El valor predeterminado es get en el módulo Iterator::BreakOn::Base y get_column en Iterator::BreakOn.
  • private: Permite guardar una referencia a algo que será recuperada mediante el método Iterator::BreakOn::private() en cualquier parte y momento.
  • break_before: Lista de campos sobre los que generar un evento antes de cambiar su valor.
  • break_after: Lista de campos sobre los que generar un evento después de que cambie su valor.
  • on_first: Referencia a código para ejecutar antes del primer elemento leído.
  • on_every: Referencia a código para ejecutar con cada elemento.
  • on_last: Referencia a código para ejecutar tras el último elemento.

En el caso de break_before y break_after la lista de campos puede consistir en una simple lista de nombres ó todos ó sólo algunos de ellos pueden hacer referencia a código.

Ejemplo

    1 my $iter = Iterator::BreakOn->new(
    2             break_before    =>  [
    3                     'custom',
    4                     'article'   =>  \&before_change_article,
    5                     'product'
    6                     ],
    7                 );

Todas las funciones proporcionadas reciben una referencia al objeto iterador como primer parámetro, a excepción de los eventos after y before, en cuyo caso se añaden a éste el nombre del campo y el valor del mismo que ha provocado el evento.

Orden de los eventos

El orden en el que se producen los eventos es el siguiente:

  1. La primera vez que se lee de la fuente de datos:
    • Generamos un único evento on_first.
    • Generamos un evento before_ por cada campo listado.
  2. Lee un registro:
    1. Si es el último:
      • Generamos un evento after_ por cada campo listado desde el más interior al más exterior.
      • Generamos un único evento on_last.
    2. Si no es el último
      • Si ha cambiado alguno de los valores:
        • Generamos un evento after por cada campo desde el que provoca la ruptura has el último, pero inviertiendo el orden de ejecución como mencionamos más arriba.
        • Generamos un evento before por cada campo desde el que provoca la rupta hasta el último.
      • Generamos un evento on_every con los datos del registro.

Modos de funcionamiento

Existen tres modos de funcionamiento básico para recorrer el iterador de datos. Cada uno de los cuales puede necesitar más o menos referencias a código externo, dependiendo de lo que queramos conseguir.

Modo automático

    1 $iter->run()

La llamada a este método provoca la lectura ininterrumpida de la fuente de datos, y el procesado de todos y cada uno de los eventos. Aquellos que no tengan código asociado serán ignorados completamente.

Modo semiautomático

    1     while (my $data_item = $iter->next()) {
    2         # do something ...
    3 
    4     }

El método next retorna el siguiente elemento de la fuente de datos, procesando automáticamente cualquier evento distinto de on_every. Idóneo para centrarse únicamente en los datos.

Modo manual

    1   while (my $event = $iter->next_event()) {
    2         if ($event eq 'before_location') {
    3             # do something before a new location comes
    4 
    5         }
    6         elsif ($event eq 'after_zipcode')) {
    7             # do something after the last zipcode reached
    8 
    9         }
   10         elsif ($event eq 'next_item' ) {
   11             # get the item (including the first and last items)
   12             my $data = $iter->next();
   13 
   14             # and do something whit him
   15 
   16         }
   17         elsif ($event eq 'last_item') {
   18             # and do something when the end of data reached
   19 
   20         }
   21     } # end while

El método next_event permite tener constancia de cada uno de los eventos que se crean al leer la fuente de datos. Pensando para cuando se prefiere un control absoluto sobre el proceso.

Ejemplos de uso

    1 #!/usr/bin/perl
    2 
    3 use lib qw(lib examples);
    4 use datasource;
    5 use Iterator::BreakOn;
    6 
    7 my $resultset = datasource->new();
    8 
    9 #
   10 #   Data source is ordered by location, zipcode and name
   11 #
   12 my $iter = Iterator::BreakOn->new(
   13                 datasource      => $resultset,
   14                 private         =>  {
   15                         zipcode_totals => 0,
   16                         location_totals => 0,
   17                                 },
   18                 break_before    =>  [
   19                     location    =>  \&before,
   20                     zipcode     =>  \&before,
   21                     ],
   22                 break_after     =>  [
   23                     location    =>  \&after,
   24                     zipcode     =>  \&after,
   25                     ],
   26                 on_every        => \&on_every,
   27                         );
   28 
   29 $iter->run();
   30 
   31 sub before {
   32     my  ($self, $field, $value) = @_;
   33     my  $data = $self->private();
   34 
   35     if ($field eq 'location') {
   36         print sprintf("%s: %s\n", $field, $value);
   37         $data->{location_totals} = 0;
   38     }
   39     elsif ($field eq 'zipcode') {
   40         $data->{zipcode_totals} = 0;
   41     }
   42 }
   43 
   44 sub after {
   45     my  ($self, $field, $value) = @_;
   46     my  $data = $self->private();
   47 
   48     if ($field eq 'zipcode') {
   49         print sprintf("\n\t\tTotals for zipcode %s: %.2f\n\n", $value,
   50                             $data->{zipcode_totals});
   51         $data->{location_totals} += $data->{zipcode_totals};
   52         $data->{zipcode_totals} = 0;
   53     }
   54     elsif ($field eq 'location') {
   55         print sprintf("\n\tTotals for location %s: %.2f\n\n", $value,
   56                             $data->{location_totals});
   57         $data->{location_totals} = 0;
   58     }
   59 
   60     return;
   61 }
   62 
   63 sub on_every {
   64     my  $self   =   shift;
   65     my  %values =   $self->item->getall();
   66     my  $data = $self->private();
   67 
   68     print sprintf("\t%s\t%-30s\t%.2f\n", $values{zipcode}, $values{name},
   69                     $values{amount} );
   70 
   71     $data->{zipcode_totals} += $values{amount};
   72 }
   73 
   74 

Existe un módulo complementario que crea y mantiene la fuente de datos llamado datasource.pm.

Descargas

Este software está disponible en el CPAN como Iterator::BreakOn y en mi repositorio Debian.

Y además, las fuentes pueden descargarse de:

mar 29 sep 2009 11:55:22 BST Iterator-BreakOn-0.3.tar.gz