Use class_of
[gitmo/MooseX-Role-Parameterized.git] / lib / MooseX / Role / Parameterized.pm
CommitLineData
7b42fc96 1package MooseX::Role::Parameterized;
fc4a95b6 2use Moose (
3 extends => { -as => 'moose_extends' },
7557429d 4 around => { -as => 'moose_around' },
2293e5f1 5 qw/confess blessed/,
fc4a95b6 6);
767906ab 7moose_extends 'Moose::Exporter';
fc4a95b6 8
7b42fc96 9use Moose::Role ();
7b42fc96 10
d93bd54d 11use MooseX::Role::Parameterized::Meta::Role::Parameterizable;
5b82ffb1 12
a457ed60 13our $CURRENT_METACLASS;
4534bdce 14
5b82ffb1 15__PACKAGE__->setup_import_methods(
ff2ccd89 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' ],
19af6e75 20);
21
22sub parameter {
23 my $caller = shift;
4f3cfe3f 24
25 confess "'parameter' may not be used inside of the role block"
26 if $CURRENT_METACLASS;
27
82b8405c 28 my $meta = Class::MOP::class_of($caller);
bd3dd853 29
ff2ccd89 30 my $names = shift;
bd3dd853 31 $names = [$names] if !ref($names);
32
33 for my $name (@$names) {
ff2ccd89 34 $meta->add_parameter($name, @_);
bd3dd853 35 }
19af6e75 36}
7b42fc96 37
e8e8ef39 38sub role (&) {
5b82ffb1 39 my $caller = shift;
40 my $role_generator = shift;
82b8405c 41 Class::MOP::class_of($caller)->role_generator($role_generator);
5b82ffb1 42}
43
7b42fc96 44sub init_meta {
45 my $self = shift;
46
47 return Moose::Role->init_meta(@_,
d93bd54d 48 metaclass => 'MooseX::Role::Parameterized::Meta::Role::Parameterizable',
7b42fc96 49 );
50}
51
a457ed60 52sub has {
ff2ccd89 53 my $caller = shift;
82b8405c 54 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
a457ed60 55
209e00d2 56 my $names = shift;
a457ed60 57 $names = [$names] if !ref($names);
58
59 for my $name (@$names) {
ff2ccd89 60 $meta->add_attribute($name, @_);
a457ed60 61 }
62}
63
209e00d2 64sub method {
209e00d2 65 my $caller = shift;
82b8405c 66 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
ff2ccd89 67
209e00d2 68 my $name = shift;
69 my $body = shift;
70
ff2ccd89 71 my $method = $meta->method_metaclass->wrap(
209e00d2 72 package_name => $caller,
73 name => $name,
74 body => $body,
75 );
76
ff2ccd89 77 $meta->add_method($name => $method);
209e00d2 78}
79
03c4551d 80sub before {
ff2ccd89 81 my $caller = shift;
82b8405c 82 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
03c4551d 83
84 my $code = pop @_;
85
86 for (@_) {
767906ab 87 Carp::croak "Roles do not currently support "
03c4551d 88 . ref($_)
89 . " references for before method modifiers"
90 if ref $_;
ff2ccd89 91 $meta->add_before_method_modifier($_, $code);
03c4551d 92 }
93}
94
95sub after {
ff2ccd89 96 my $caller = shift;
82b8405c 97 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
03c4551d 98
99 my $code = pop @_;
100
101 for (@_) {
767906ab 102 Carp::croak "Roles do not currently support "
03c4551d 103 . ref($_)
104 . " references for after method modifiers"
105 if ref $_;
ff2ccd89 106 $meta->add_after_method_modifier($_, $code);
03c4551d 107 }
108}
109
110sub around {
ff2ccd89 111 my $caller = shift;
82b8405c 112 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
03c4551d 113
114 my $code = pop @_;
115
116 for (@_) {
767906ab 117 Carp::croak "Roles do not currently support "
03c4551d 118 . ref($_)
119 . " references for around method modifiers"
120 if ref $_;
ff2ccd89 121 $meta->add_around_method_modifier($_, $code);
03c4551d 122 }
123}
124
d55c8861 125sub with {
ff2ccd89 126 my $caller = shift;
82b8405c 127 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
ff2ccd89 128
129 Moose::Util::apply_all_roles($meta, @_);
d55c8861 130}
131
eac6d242 132sub requires {
ff2ccd89 133 my $caller = shift;
82b8405c 134 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
ff2ccd89 135
767906ab 136 Carp::croak "Must specify at least one method" unless @_;
ff2ccd89 137 $meta->add_required_methods(@_);
eac6d242 138}
139
fa627596 140sub excludes {
ff2ccd89 141 my $caller = shift;
82b8405c 142 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
ff2ccd89 143
767906ab 144 Carp::croak "Must specify at least one role" unless @_;
ff2ccd89 145 $meta->add_excluded_roles(@_);
fa627596 146}
147
20725a2d 148# see Moose.pm for discussion
149sub super {
150 return unless $Moose::SUPER_BODY;
151 $Moose::SUPER_BODY->(@Moose::SUPER_ARGS);
152}
153
154sub override {
ff2ccd89 155 my $caller = shift;
82b8405c 156 my $meta = $CURRENT_METACLASS || Class::MOP::class_of($caller);
20725a2d 157
158 my ($name, $code) = @_;
ff2ccd89 159 $meta->add_override_method_modifier($name, $code);
20725a2d 160}
161
767906ab 162sub extends { Carp::croak "Roles do not currently support 'extends'" }
fc4a95b6 163
767906ab 164sub inner { Carp::croak "Roles cannot support 'inner'" }
fc4a95b6 165
767906ab 166sub augment { Carp::croak "Roles cannot support 'augment'" }
fc4a95b6 167
7b42fc96 1681;
169
a4ac31fa 170__END__
171
30788701 172=head1 NAME
173
174MooseX::Role::Parameterized - parameterized roles
175
a4ac31fa 176=head1 SYNOPSIS
177
178 package MyRole::Counter;
179 use MooseX::Role::Parameterized;
180
181 parameter name => (
a4ac31fa 182 isa => 'Str',
183 required => 1,
184 );
185
186 role {
187 my $p = shift;
188
189 my $name = $p->name;
190
191 has $name => (
192 is => 'rw',
193 isa => 'Int',
194 default => 0,
195 );
196
197 method "increment_$name" => sub {
198 my $self = shift;
199 $self->$name($self->$name + 1);
200 };
201
202 method "decrement_$name" => sub {
203 my $self = shift;
204 $self->$name($self->$name - 1);
205 };
206 };
207
208 package MyGame::Tile;
209 use Moose;
210
211 with 'MyRole::Counter' => { name => 'stepped_on' };
212
213=head1 L<MooseX::Role::Parameterized::Tutorial>
214
215B<Stop!> If you're new here, please read
216L<MooseX::Role::Parameterized::Tutorial>.
217
218=head1 DESCRIPTION
219
aeaaabaf 220Your parameterized role consists of two new things: parameter declarations
221and a C<role> block.
a4ac31fa 222
223Parameters are declared using the L</parameter> keyword which very much
fa2e6c00 224resembles L<Moose/has>. You can use any option that L<Moose/has> accepts. The
aeaaabaf 225default value for the C<is> option is C<ro> as that's a very common case. These
fa2e6c00 226parameters will get their values when the consuming class (or role) uses
a4ac31fa 227L<Moose/with>. A parameter object will be constructed with these values, and
228passed to the C<role> block.
229
230The C<role> block then uses the usual L<Moose::Role> keywords to build up a
231role. You can shift off the parameter object to inspect what the consuming
aeaaabaf 232class provided as parameters. You use the parameters to customize your
233role however you wish.
a4ac31fa 234
aeaaabaf 235There are many possible implementations for parameterized roles (hopefully with
236a consistent enough API); I believe this to be the easiest and most flexible
70afb58d 237design. Coincidentally, Pugs originally had an eerily similar design.
238
239=head2 Why a parameters object?
240
241I've been asked several times "Why use a parameter I<object> and not just a
242parameter I<hashref>? That would eliminate the need to explicitly declare your
243parameters."
244
245The benefits of using an object are similar to the benefits of using Moose. You
246get an easy way to specify lazy defaults, type constraint, delegation, and so
247on. You get to use MooseX modules.
248
249You also get the usual introspective and intercessory abilities that come
250standard with the metaobject protocol. Ambitious users should be able to add
251traits to the parameters metaclass to further customize behavior. Please let
252me know if you're doing anything viciously complicated with this extension. :)
a4ac31fa 253
254=head1 CAVEATS
255
256You must use this syntax to declare methods in the role block:
08609551 257C<< method NAME => sub { ... }; >>. This is due to a limitation in Perl. In
258return though you can use parameters I<in your methods>!
a4ac31fa 259
9d029b3d 260L<Moose::Role/alias> and L<Moose::Role/excludes> are not yet supported. I'm
261completely unsure of whether they should be handled by this module. Until we
aeaaabaf 262figure out a plan, either declaring or providing a parameter named C<alias> or
9d029b3d 263C<excludes> is an error.
a4ac31fa 264
265=head1 AUTHOR
266
267Shawn M Moore, C<< <sartak@bestpractical.com> >>
268
d5487cd9 269=head1 EXAMPLES
988cddaa 270
d5487cd9 271=over 4
272
273=item L<MooseX::Role::Matcher>
274
d5487cd9 275=item L<MooseX::Role::XMLRPC::Client>
276
bfac2339 277=item L<MooseX::RelatedClassRoles>
278
d5487cd9 279=item L<WWW::Mechanize::TreeBuilder>
280
281=item L<TAEB::Action::Role::Item>
282
283=item L<KiokuDB::Role::Scan>
284
285=item L<Fey::Role::MakesAliasObjects>
286
287=item L<Fey::Role::HasAliasName>
288
289=item L<Fey::Role::SetOperation>
290
291=back
988cddaa 292
a4ac31fa 293=cut
294