1 package MooseX::Role::Parameterized;
4 extends => { -as => 'moose_extends' },
5 around => { -as => 'moose_around' },
11 moose_extends 'Moose::Exporter';
13 use MooseX::Role::Parameterized::Meta::Role::Parameterizable;
15 our $CURRENT_METACLASS;
17 __PACKAGE__->setup_import_methods(
18 with_caller => ['parameter', 'role', 'method'],
20 'has', 'with', 'extends', 'requires', 'excludes', 'augment', 'inner',
21 'before', 'after', 'around', 'super', 'override', 'confess',
30 $names = [$names] if !ref($names);
32 for my $name (@$names) {
33 Class::MOP::Class->initialize($caller)->add_parameter($name, @_);
39 my $role_generator = shift;
40 Class::MOP::Class->initialize($caller)->role_generator($role_generator);
46 return Moose::Role->init_meta(@_,
47 metaclass => 'MooseX::Role::Parameterized::Meta::Role::Parameterizable',
51 # give role a (&) prototype
52 moose_around _make_wrapper => sub {
54 my ($self, $caller, $sub, $fq_name) = @_;
56 if ($fq_name =~ /::role$/) {
57 return sub (&) { $sub->($caller, @_) };
64 confess "has must be called within the role { ... } block."
65 unless $CURRENT_METACLASS;
68 $names = [$names] if !ref($names);
70 for my $name (@$names) {
71 $CURRENT_METACLASS->add_attribute($name, @_);
76 confess "method must be called within the role { ... } block."
77 unless $CURRENT_METACLASS;
83 my $method = $CURRENT_METACLASS->method_metaclass->wrap(
84 package_name => $caller,
89 $CURRENT_METACLASS->add_method($name => $method);
93 confess "before must be called within the role { ... } block."
94 unless $CURRENT_METACLASS;
99 croak "Roles do not currently support "
101 . " references for before method modifiers"
103 $CURRENT_METACLASS->add_before_method_modifier($_, $code);
108 confess "after must be called within the role { ... } block."
109 unless $CURRENT_METACLASS;
114 croak "Roles do not currently support "
116 . " references for after method modifiers"
118 $CURRENT_METACLASS->add_after_method_modifier($_, $code);
123 confess "around must be called within the role { ... } block."
124 unless $CURRENT_METACLASS;
129 croak "Roles do not currently support "
131 . " references for around method modifiers"
133 $CURRENT_METACLASS->add_around_method_modifier($_, $code);
138 confess "with must be called within the role { ... } block."
139 unless $CURRENT_METACLASS;
140 Moose::Util::apply_all_roles($CURRENT_METACLASS, @_);
144 confess "requires must be called within the role { ... } block."
145 unless $CURRENT_METACLASS;
146 croak "Must specify at least one method" unless @_;
147 $CURRENT_METACLASS->add_required_methods(@_);
151 confess "excludes must be called within the role { ... } block."
152 unless $CURRENT_METACLASS;
153 croak "Must specify at least one role" unless @_;
154 $CURRENT_METACLASS->add_excluded_roles(@_);
157 # see Moose.pm for discussion
159 return unless $Moose::SUPER_BODY;
160 $Moose::SUPER_BODY->(@Moose::SUPER_ARGS);
164 confess "override must be called within the role { ... } block."
165 unless $CURRENT_METACLASS;
167 my ($name, $code) = @_;
168 $CURRENT_METACLASS->add_override_method_modifier($name, $code);
171 sub extends { croak "Roles do not currently support 'extends'" }
173 sub inner { croak "Roles cannot support 'inner'" }
175 sub augment { croak "Roles cannot support 'augment'" }
183 MooseX::Role::Parameterized - parameterized roles
187 package MyRole::Counter;
188 use MooseX::Role::Parameterized;
207 method "increment_$name" => sub {
209 $self->$name($self->$name + 1);
212 method "decrement_$name" => sub {
214 $self->$name($self->$name - 1);
218 package MyGame::Tile;
221 with 'MyRole::Counter' => { name => 'stepped_on' };
223 =head1 L<MooseX::Role::Parameterized::Tutorial>
225 B<Stop!> If you're new here, please read
226 L<MooseX::Role::Parameterized::Tutorial>.
230 Your parameterized role consists of two things: parameter declarations and a
233 Parameters are declared using the L</parameter> keyword which very much
234 resembles L<Moose/has>. You can use any option that L<Moose/has> accepts.
235 These parameters will get their values when the consuming class (or role) uses
236 L<Moose/with>. A parameter object will be constructed with these values, and
237 passed to the C<role> block.
239 The C<role> block then uses the usual L<Moose::Role> keywords to build up a
240 role. You can shift off the parameter object to inspect what the consuming
241 class provided as parameters. You can use the parameters to make your role
244 There are many paths to parameterized roles (hopefully with a consistent enough
245 API); I believe this to be the easiest and most flexible implementation.
246 Coincidentally, Pugs has a very similar design (I'm not yet convinced that that
251 You must use this syntax to declare methods in the role block:
252 C<< method NAME => sub { ... }; >>. This is due to a limitation in Perl. In
253 return though you can use parameters I<in your methods>!
255 You must use all the keywords in the role block. If it turns out to be correct,
256 we'll compose the parameterizable role (everything outside the role block) with
257 the parameterized role (everything inside the role block). We throw an error if
258 you try to use a keyword outside of the role block, so don't worry about it for
261 L<Moose::Role/alias> and L<Moose::Role/excludes> are not yet supported. I'm
262 completely unsure of whether they should be handled by this module. Until we
263 figure out a plan, both declaring and providing a parameter named C<alias> or
264 C<excludes> is an error.
268 Shawn M Moore, C<< <sartak@bestpractical.com> >>