1 package MooseX::Role::Parameterized;
3 extends => { -as => 'moose_extends' },
4 around => { -as => 'moose_around' },
7 moose_extends 'Moose::Exporter';
11 use MooseX::Role::Parameterized::Meta::Role::Parameterizable;
13 our $CURRENT_METACLASS;
15 __PACKAGE__->setup_import_methods(
16 with_caller => ['parameter', 'role', 'method', 'has', 'with', 'extends',
17 'requires', 'excludes', 'augment', 'inner', 'before',
18 'after', 'around', 'super', 'override'],
19 as_is => [ 'confess', 'blessed' ],
25 confess "'parameter' may not be used inside of the role block"
26 if $CURRENT_METACLASS;
28 my $meta = Class::MOP::class_of($caller);
31 $names = [$names] if !ref($names);
33 for my $name (@$names) {
34 $meta->add_parameter($name, @_);
40 my $role_generator = shift;
41 Class::MOP::class_of($caller)->role_generator($role_generator);
47 return Moose::Role->init_meta(@_,
48 metaclass => 'MooseX::Role::Parameterized::Meta::Role::Parameterizable',
54 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
57 $names = [$names] if !ref($names);
59 for my $name (@$names) {
60 $meta->add_attribute($name, @_);
66 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
71 my $method = $meta->method_metaclass->wrap(
72 package_name => $caller,
77 $meta->add_method($name => $method);
80 sub _add_method_modifier {
83 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
88 Carp::croak "Roles do not currently support "
90 . " references for $type method modifiers"
93 my $add_method = "add_${type}_method_modifier";
94 $meta->$add_method($_, $code);
99 _add_method_modifier('before', @_);
103 _add_method_modifier('after', @_);
107 _add_method_modifier('around', @_);
112 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
114 Moose::Util::apply_all_roles($meta, @_);
119 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
121 Carp::croak "Must specify at least one method" unless @_;
122 $meta->add_required_methods(@_);
127 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
129 Carp::croak "Must specify at least one role" unless @_;
130 $meta->add_excluded_roles(@_);
133 # see Moose.pm for discussion
135 return unless $Moose::SUPER_BODY;
136 $Moose::SUPER_BODY->(@Moose::SUPER_ARGS);
141 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
143 my ($name, $code) = @_;
144 $meta->add_override_method_modifier($name, $code);
147 sub extends { Carp::croak "Roles do not currently support 'extends'" }
149 sub inner { Carp::croak "Roles cannot support 'inner'" }
151 sub augment { Carp::croak "Roles cannot support 'augment'" }
159 MooseX::Role::Parameterized - parameterized roles
163 package MyRole::Counter;
164 use MooseX::Role::Parameterized;
182 method "increment_$name" => sub {
184 $self->$name($self->$name + 1);
187 method "decrement_$name" => sub {
189 $self->$name($self->$name - 1);
193 package MyGame::Tile;
196 with 'MyRole::Counter' => { name => 'stepped_on' };
198 =head1 L<MooseX::Role::Parameterized::Tutorial>
200 B<Stop!> If you're new here, please read
201 L<MooseX::Role::Parameterized::Tutorial> for a much gentler introduction.
205 Your parameterized role consists of two new things: parameter declarations
208 Parameters are declared using the L</parameter> keyword which very much
209 resembles L<Moose/has>. You can use any option that L<Moose/has> accepts. The
210 default value for the C<is> option is C<ro> as that's a very common case. These
211 parameters will get their values when the consuming class (or role) uses
212 L<Moose/with>. A parameter object will be constructed with these values, and
213 passed to the C<role> block.
215 The C<role> block then uses the usual L<Moose::Role> keywords to build up a
216 role. You can shift off the parameter object to inspect what the consuming
217 class provided as parameters. You use the parameters to customize your
218 role however you wish.
220 There are many possible implementations for parameterized roles (hopefully with
221 a consistent enough API); I believe this to be the easiest and most flexible
222 design. Coincidentally, Pugs originally had an eerily similar design.
224 =head2 Why a parameters object?
226 I've been asked several times "Why use a parameter I<object> and not just a
227 parameter I<hashref>? That would eliminate the need to explicitly declare your
230 The benefits of using an object are similar to the benefits of using Moose. You
231 get an easy way to specify lazy defaults, type constraint, delegation, and so
232 on. You get to use MooseX modules.
234 You also get the usual introspective and intercessory abilities that come
235 standard with the metaobject protocol. Ambitious users should be able to add
236 traits to the parameters metaclass to further customize behavior. Please let
237 me know if you're doing anything viciously complicated with this extension. :)
241 You must use this syntax to declare methods in the role block:
242 C<< method NAME => sub { ... }; >>. This is due to a limitation in Perl. In
243 return though you can use parameters I<in your methods>!
245 L<Moose::Role/alias> and L<Moose::Role/excludes> are not yet supported. I'm
246 completely unsure of whether they should be handled by this module. Until we
247 figure out a plan, either declaring or providing a parameter named C<alias> or
248 C<excludes> is an error.
252 Shawn M Moore, C<< <sartak@bestpractical.com> >>
258 =item L<MooseX::Role::Matcher>
260 =item L<MooseX::Role::XMLRPC::Client>
262 =item L<MooseX::RelatedClassRoles>
264 =item L<WWW::Mechanize::TreeBuilder>
266 =item L<NetHack::Item::Role::IncorporatesStats>
268 =item L<TAEB::Action::Role::Item>
270 =item L<KiokuDB::Role::Scan>
272 =item L<Fey::Role::MakesAliasObjects>
274 =item L<Fey::Role::HasAliasName>
276 =item L<Fey::Role::SetOperation>
282 L<http://sartak.blogspot.com/2009/05/parameterized-roles.html>