Iterator-Flex

 view release on metacpan or  search on metacpan

lib/Iterator/Flex/Manual/Authoring.pod  view on Meta::CPAN


=head3 An iterator class

Creating a class requires a few steps more, and gives the following benefits:

=over

=item *

A much cleaner interface, e.g.

  $iter = Iterator::Flex::Array->new( \@array );

vs. the multi-liner above.

=item *

The ability to freeze and thaw the iterator

=item *

some of the construction costs can be moved from run time to compile  time.

=back

An iterator class must

=over

=item *

subclass L<Iterator::Flex::Base>;

=item *

provide two class methods, C<new> and C<construct>; and

=item *

register its capabilities.

=back

=head4 new

The C<new> method converts from the API most comfortable to your usage
to the internal API used by L<Iterator::Flex::Base>.  By convention,
the last argument should be reserved for a hashref containing general
iterator arguments (such as the C<exhaustion> key).  This hashref is
documented in L<Iterator::Flex::Base/new_from_attrs>.

The super class' constructor takes two arguments: a variable containing
iterator specific data (state), and the above-mentioned general
argument hash.  The state variable can take any form, it is not
interpreted by the C<Iterator::Flex> framework.

Here's the code for L<Iterator::Flex::Array/new>:

  sub new ( $class, $array, $pars={} ) {
      throw_failure( parameter => "argument must be an ARRAY reference" )
        unless Ref::Util::is_arrayref( $array );
      $class->SUPER::new( { array => $array }, $pars );
  }

It's pretty simple. It saves the general options hash if present,
stores the passed array (the state) in a hash, and passes both of
them to the super class' constructor.  (A hash is used here because
L<Iterator::Flex::Array> can be serialized, and extra state is
required to do so).

=head4 construct

The C<construct> class method's duty is to return a C<%AttrHash>.
It's called as

  $AttrHash = $class->construct( $state );

where C<$state> is the state variable passed to
L<Iterator::Flex::Base/new>.  Unsurprisingly, it is remarkably similar
to the C<construct> subroutine developed earlier.

There are a few differences:

=over

=item *

The signature changes, as this is a class method, rather than a subroutine.

=item *

There are additional C<%AttrHash> entries available: C<_roles>, which
supports run-time enabling of capabilities and C<freeze>, which
supports serialization.

=item *

Capabilities other than C<next> can be implemented as actual class
methods, rather than closures.  This decreases the cost of creating
iterators (because they only need to be compiled once, rather than for
every instance of the iterator) but increases run time costs, as they
cannot use closed over variables to access state information.

=back

=head4 Registering Capabilities

Unlike when using L<Iterator::Flex::Factory/construct_from_attr>,
which helpfully looks at C<%AttrHash> to determine which capabilities
are provided (albeit at run time), classes are encouraged to register
their capabilities at compile time via the C<_add_roles> method.  For
the example iterator class, this would be done via

  __PACKAGE__->_add_roles( qw[
        State::Registry
        Next::ClosedSelf
        Rewind::Closure
        Reset::Closure
        Prev::Closure
        Current::Closure
  ] );

(These are all accepted shorthand for roles in the
L<Iterator::Flex::Role> namespace.)

If capabilities must be added at run time, use the C<_roles> entry in C<%AttrHash>.

The specific roles used here are:

=over

=item Next::ClosedSelf

This indicates that the C<next> capability uses a closed over C<$self> variable,
and that C<Iterator::Flex> should use the C<_self> hash entry to initialize it.

=item State::Registry

This indicates that the exhaustion state should be stored in the central iterator Registry.
Another implementation uses a closed over variable (and the role C<State::Closure>).
See L<Iterator::Flex::Manual::Internals/Exhaustion>.

=item Reset::Closure

=item Prev::Closure

=item Current::Closure

=item Rewind::Closure

These indicate that the named capability is present and implemented as a closure.

=back

=head4 All together

  package My::Array;

  use strict;
  use warnings;

  use parent 'Iterator::Flex::Base';

  sub new {
      my $class = shift;
      my $gpar = Ref::Util::is_hashref( $_[-1] ) ? pop : {};

      throw_failure( parameter => "argument must be an ARRAY reference" )
        unless Ref::Util::is_arrayref( $_[0] );

      $class->SUPER::new( { array => $_[0] }, $gpar );
  }

  sub construct {
     my ( $class, $state ) = @_;

     # initialize lexical variables here
     ...
     my $arr = $state->{array};

     my %AttrHash = ( ... );
     return \%AttrHash;
 }

  __PACKAGE__->_add_roles( qw[
        State::Registry
        Next::ClosedSelf
        Rewind::Closure
        Reset::Closure
        Prev::Closure
        Current::Closure
  ] );

  1;

=head1 SUPPORT

=head2 Bugs

Please report any bugs or feature requests to bug-iterator-flex@rt.cpan.org  or through the web interface at: L<https://rt.cpan.org/Public/Dist/Display.html?Name=Iterator-Flex>

=head2 Source

Source is available at

  https://gitlab.com/djerius/iterator-flex

and may be cloned from

  https://gitlab.com/djerius/iterator-flex.git

=head1 SEE ALSO

Please see those modules/websites for more information related to this module.

=over 4

=item *

L<Iterator::Flex|Iterator::Flex>

=item *

L<Iterator::Flex::Manual|Iterator::Flex::Manual>

=back

=head1 AUTHOR



( run in 0.679 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )