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 )