1 package MooseX::Role::Parameterized;
3 # ABSTRACT: parameterized roles, at long last
6 extends => { -as => 'moose_extends' },
7 around => { -as => 'moose_around' },
13 moose_extends 'Moose::Exporter';
15 use MooseX::Role::Parameterized::Meta::Role::Parameterizable;
17 our $CURRENT_METACLASS;
19 __PACKAGE__->setup_import_methods(
20 with_caller => ['parameter', 'role', 'method'],
22 'has', 'with', 'extends', 'requires', 'excludes', 'augment', 'inner',
23 'before', 'after', 'around', 'super', 'override', 'confess',
32 $names = [$names] if !ref($names);
34 for my $name (@$names) {
35 Class::MOP::Class->initialize($caller)->add_parameter($name, @_);
41 my $role_generator = shift;
42 Class::MOP::Class->initialize($caller)->role_generator($role_generator);
48 return Moose::Role->init_meta(@_,
49 metaclass => 'MooseX::Role::Parameterized::Meta::Role::Parameterizable',
53 # give role a (&) prototype
54 moose_around _make_wrapper => sub {
56 my ($self, $caller, $sub, $fq_name) = @_;
58 if ($fq_name =~ /::role$/) {
59 return sub (&) { $sub->($caller, @_) };
66 confess "has must be called within the role { ... } block."
67 unless $CURRENT_METACLASS;
70 $names = [$names] if !ref($names);
72 for my $name (@$names) {
73 $CURRENT_METACLASS->add_attribute($name, @_);
78 confess "method must be called within the role { ... } block."
79 unless $CURRENT_METACLASS;
85 my $method = $CURRENT_METACLASS->method_metaclass->wrap(
86 package_name => $caller,
91 $CURRENT_METACLASS->add_method($name => $method);
95 confess "before must be called within the role { ... } block."
96 unless $CURRENT_METACLASS;
101 croak "Roles do not currently support "
103 . " references for before method modifiers"
105 $CURRENT_METACLASS->add_before_method_modifier($_, $code);
110 confess "after must be called within the role { ... } block."
111 unless $CURRENT_METACLASS;
116 croak "Roles do not currently support "
118 . " references for after method modifiers"
120 $CURRENT_METACLASS->add_after_method_modifier($_, $code);
125 confess "around must be called within the role { ... } block."
126 unless $CURRENT_METACLASS;
131 croak "Roles do not currently support "
133 . " references for around method modifiers"
135 $CURRENT_METACLASS->add_around_method_modifier($_, $code);
140 confess "with must be called within the role { ... } block."
141 unless $CURRENT_METACLASS;
142 Moose::Util::apply_all_roles($CURRENT_METACLASS, @_);
146 confess "requires must be called within the role { ... } block."
147 unless $CURRENT_METACLASS;
148 croak "Must specify at least one method" unless @_;
149 $CURRENT_METACLASS->add_required_methods(@_);
153 confess "excludes must be called within the role { ... } block."
154 unless $CURRENT_METACLASS;
155 croak "Must specify at least one role" unless @_;
156 $CURRENT_METACLASS->add_excluded_roles(@_);
159 # see Moose.pm for discussion
161 return unless $Moose::SUPER_BODY;
162 $Moose::SUPER_BODY->(@Moose::SUPER_ARGS);
166 confess "override must be called within the role { ... } block."
167 unless $CURRENT_METACLASS;
169 my ($name, $code) = @_;
170 $CURRENT_METACLASS->add_override_method_modifier($name, $code);
173 sub extends { croak "Roles do not currently support 'extends'" }
175 sub inner { croak "Roles cannot support 'inner'" }
177 sub augment { croak "Roles cannot support 'augment'" }
185 package MyRole::Counter;
186 use MooseX::Role::Parameterized;
205 method "increment_$name" => sub {
207 $self->$name($self->$name + 1);
210 method "decrement_$name" => sub {
212 $self->$name($self->$name - 1);
216 package MyGame::Tile;
219 with 'MyRole::Counter' => { name => 'stepped_on' };
221 =head1 L<MooseX::Role::Parameterized::Tutorial>
223 B<Stop!> If you're new here, please read
224 L<MooseX::Role::Parameterized::Tutorial>.
228 Your parameterized role consists of two things: parameter declarations and a
231 Parameters are declared using the L</parameter> keyword which very much
232 resembles L<Moose/has>. You can use any option that L<Moose/has> accepts.
233 These parameters will get their values when the consuming class (or role) uses
234 L<Moose/with>. A parameter object will be constructed with these values, and
235 passed to the C<role> block.
237 The C<role> block then uses the usual L<Moose::Role> keywords to build up a
238 role. You can shift off the parameter object to inspect what the consuming
239 class provided as parameters. You can use the parameters to make your role
242 There are many paths to parameterized roles (hopefully with a consistent enough
243 API); I believe this to be the easiest and most flexible implementation.
244 Coincidentally, Pugs has a very similar design (I'm not convinced that that is
249 You must use this syntax to declare methods in the role block:
250 C<method NAME => sub { ... };>. This is due to a limitation in Perl. In return
251 though you can use parameters I<in your methods>!
253 L<Moose::Role/alias> and L<Moose::Role/excludes> are not yet supported. Because
254 I'm totally unsure of whether they should be handled by this module, both
255 declaring and providing a parameter named C<alias> or C<excludes> is an error.
259 Shawn M Moore, C<< <sartak@bestpractical.com> >>