IM::Engine::HasPlugins - role for objects that have plugins


IM-Engine documentation Contained in the IM-Engine distribution.

Index


Code Index:

NAME

Top

IM::Engine::HasPlugins - role for objects that have plugins

DESCRIPTION

Top

This should probably only be applied to IM::Engine objects. Beware!

ATTRIBUTES

Top

plugins

METHODS

Top

find_plugins

Return the IM::Engine::Plugin objects that return true for the passed coderef.

plugins_with

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 +.

each_plugin

For each plugin that does the role argument, invoke the callback argument. Returns nothing.

plugin_relay

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.

plugin_default

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.

plugin_collect

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__