The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package IM::Engine::PluggableConstructor;
use MooseX::Role::Parameterized;
with 'MooseX::Traits';

parameter role_specifier => (
    isa      => 'Str',
    required => 1,
);

role {
    my $p = shift;
    my $role_specifier = $p->role_specifier;

    method new_with_plugins => sub {
        my $class = shift;
        my %args  = %{ $class->BUILDARGS(@_) };

        my $engine = delete $args{engine}
            or confess "You must pass the engine to new_with_plugins";

        %args = (
            $engine->plugin_collect(
                role   => $role_specifier,
                method => 'constructor_arguments',
            ),
            %args,
        );

        push @{ $args{traits} ||= [] }, $engine->plugin_collect(
            role   => $role_specifier,
            method => 'traits',
        );

        $class->new_with_traits(\%args);
    };
};

no MooseX::Role::Parameterized;

1;

__END__

=head1 NAME

IM::Engine::PluggableConstructor - provide C<new_with_plugins>

=head1 DESCRIPTION

Some plugins need to extend built-in classes. For example,
L<IM::Engine::Plugin::State> needs to extend L<IM::Engine::User> with methods
such as C<get_state> and C<set_state>. This role provides a new constructor
C<new_with_plugins> to classes that need to be extensible. Plugins can then
specify roles to use for the instance, as well as additional constructor
parameters.

=head1 PARAMETERS

=head2 role_specifier

A string representing the role that can extend the consuming class. The role
specifier uses the same rules as L<MooseX::Traits>: prefix a C<+> character to
specify an absolute role name, otherwise the class's C<_trait_namespace> is
prepended.

=head1 METHODS

=head2 new_with_plugins

An alternate constructor that includes additional parameters specified by
plugins. This also extends the C<traits> option to include additional traits
specified by plugins.

You must pass the C<engine> argument so we can ask plugins about what to
include.

=cut