| IM-Engine documentation | Contained in the IM-Engine distribution. |
IM::Engine::HasPlugins - role for objects that have plugins
This should probably only be applied to IM::Engine objects. Beware!
Return the IM::Engine::Plugin objects that return true for the passed coderef.
Return the IM::Engine::Plugin objects that do the particular role. For
convenience, the role specifier has IM::Engine::Plugin:: prepended to it,
unless it is prefixed with +.
For each plugin that does the role argument, invoke the callback
argument. Returns nothing.
For each plugin that does the role argument, call the method on it,
passing the baton argument to it. The return value of method is used as
the baton for the next plugin. The return value of this method is the final
state of the baton.
This is useful for letting each plugin get a chance at modifying some value or object.
For each plugin that does the role argument, call the method on it. The
first return value of method that is defined will be returned from this
method.
This is useful for (among other things) letting each plugin get a chance at short-circuiting some other calculation.
For each plugin that does the role argument, call the method on it. The
return values of all method calls are collected into a list to be returned
by this method.
This is useful for (among other things) letting each plugin contribute to constructor arguments.
| IM-Engine documentation | Contained in the IM-Engine distribution. |
package IM::Engine::HasPlugins; use Moose::Role; use MooseX::AttributeHelpers; use IM::Engine::Plugin; use Data::OptList 'mkopt'; requires 'engine'; has plugins_args => ( is => 'ro', isa => 'ArrayRef', init_arg => 'plugins', default => sub { [] }, ); has _plugins => ( metaclass => 'Collection::List', isa => 'ArrayRef[IM::Engine::Plugin]', builder => '_build_plugins', init_arg => undef, lazy => 1, provides => { elements => 'plugins', grep => 'find_plugins', }, ); sub BUILD { } # provide an empty default in case the class has none after BUILD => sub { my $self = shift; # Initialize plugin list so the plugins can perform further initialization $self->plugins; # Let them validate that other plugins exist, etc. $_->post_initialization for $self->plugins; }; sub _build_plugins { my $self = shift; my $args = mkopt( $self->plugins_args, 'plugins', 0, # can have more than one instance of the same plugin [qw(HASH)], ); my @plugins; for (@$args) { my ($class, $params) = @$_; $class = "IM::Engine::Plugin::$class" unless $class =~ s/^\+//; Class::MOP::load_class($class); push @plugins, $class->new( %{ $params || {} }, engine => $self->engine, ); } return \@plugins; } sub plugins_with { my $self = shift; my $role = shift; $role = "IM::Engine::Plugin::$role" unless $role =~ s/^\+//; return $self->find_plugins(sub { $_->does($role) }); } sub each_plugin { my $self = shift; my %args = @_; my $role = $args{role}; my $callback = $args{callback}; for my $plugin ($self->plugins_with($role)) { $callback->($plugin); } return; } sub plugin_relay { my $self = shift; my %args = @_; my $method = $args{method}; my $baton = $args{baton}; $self->each_plugin( %args, callback => sub { $baton = shift->$method($baton, \%args) }, ); return $baton; } sub plugin_default { my $self = shift; my %args = @_; my $method = $args{method}; my $default; # I think I want to use Continuation::Escape here :) $self->each_plugin( %args, callback => sub { return if $default; my $plugin = shift; $default = $plugin->$method(\%args); }, ); return $default; } sub plugin_collect { my $self = shift; my %args = @_; my $method = $args{method}; my @items = @{ $args{items} || [] }; $self->each_plugin( %args, callback => sub { push @items, shift->$method(\%args) }, ); return @items; } no Moose::Role; 1; __END__