More docs
[gitmo/MooseX-Role-Parameterized.git] / lib / MooseX / Role / Parameterized.pm
CommitLineData
7b42fc96 1#!/usr/bin/env perl
2package MooseX::Role::Parameterized;
fc4a95b6 3use Moose (
4 extends => { -as => 'moose_extends' },
03c4551d 5 around => { -as => 'moose_around' },
2293e5f1 6 qw/confess blessed/,
fc4a95b6 7);
8
9use Carp 'croak';
7b42fc96 10use Moose::Role ();
fc4a95b6 11moose_extends 'Moose::Exporter';
7b42fc96 12
d93bd54d 13use MooseX::Role::Parameterized::Meta::Role::Parameterizable;
5b82ffb1 14
a457ed60 15our $CURRENT_METACLASS;
4534bdce 16
5b82ffb1 17__PACKAGE__->setup_import_methods(
209e00d2 18 with_caller => ['parameter', 'role', 'method'],
2293e5f1 19 as_is => ['has', 'with', 'extends', 'requires', 'excludes', 'augment', 'inner', 'before', 'after', 'around', 'super', 'override', 'confess', 'blessed'],
19af6e75 20);
21
22sub parameter {
23 my $caller = shift;
bd3dd853 24 my $names = shift;
25
26 $names = [$names] if !ref($names);
27
28 for my $name (@$names) {
9a21e637 29 Class::MOP::Class->initialize($caller)->add_parameter($name, @_);
bd3dd853 30 }
19af6e75 31}
7b42fc96 32
5b82ffb1 33sub role {
34 my $caller = shift;
35 my $role_generator = shift;
9a21e637 36 Class::MOP::Class->initialize($caller)->role_generator($role_generator);
5b82ffb1 37}
38
7b42fc96 39sub init_meta {
40 my $self = shift;
41
42 return Moose::Role->init_meta(@_,
d93bd54d 43 metaclass => 'MooseX::Role::Parameterized::Meta::Role::Parameterizable',
7b42fc96 44 );
45}
46
5b82ffb1 47# give role a (&) prototype
03c4551d 48moose_around _make_wrapper => sub {
5b82ffb1 49 my $orig = shift;
50 my ($self, $caller, $sub, $fq_name) = @_;
51
52 if ($fq_name =~ /::role$/) {
53 return sub (&) { $sub->($caller, @_) };
54 }
55
56 return $orig->(@_);
57};
58
a457ed60 59sub has {
60 confess "has must be called within the role { ... } block."
61 unless $CURRENT_METACLASS;
62
209e00d2 63 my $names = shift;
a457ed60 64 $names = [$names] if !ref($names);
65
66 for my $name (@$names) {
67 $CURRENT_METACLASS->add_attribute($name, @_);
68 }
69}
70
209e00d2 71sub method {
72 confess "method must be called within the role { ... } block."
73 unless $CURRENT_METACLASS;
74
75 my $caller = shift;
76 my $name = shift;
77 my $body = shift;
78
31c69f88 79 my $method = $CURRENT_METACLASS->method_metaclass->wrap(
209e00d2 80 package_name => $caller,
81 name => $name,
82 body => $body,
83 );
84
85 $CURRENT_METACLASS->add_method($name => $method);
86}
87
03c4551d 88sub before {
89 confess "before must be called within the role { ... } block."
90 unless $CURRENT_METACLASS;
91
92 my $code = pop @_;
93
94 for (@_) {
95 croak "Roles do not currently support "
96 . ref($_)
97 . " references for before method modifiers"
98 if ref $_;
99 $CURRENT_METACLASS->add_before_method_modifier($_, $code);
100 }
101}
102
103sub after {
104 confess "after must be called within the role { ... } block."
105 unless $CURRENT_METACLASS;
106
107 my $code = pop @_;
108
109 for (@_) {
110 croak "Roles do not currently support "
111 . ref($_)
112 . " references for after method modifiers"
113 if ref $_;
114 $CURRENT_METACLASS->add_after_method_modifier($_, $code);
115 }
116}
117
118sub around {
119 confess "around must be called within the role { ... } block."
120 unless $CURRENT_METACLASS;
121
122 my $code = pop @_;
123
124 for (@_) {
125 croak "Roles do not currently support "
126 . ref($_)
127 . " references for around method modifiers"
128 if ref $_;
129 $CURRENT_METACLASS->add_around_method_modifier($_, $code);
130 }
131}
132
d55c8861 133sub with {
134 confess "with must be called within the role { ... } block."
135 unless $CURRENT_METACLASS;
136 Moose::Util::apply_all_roles($CURRENT_METACLASS, @_);
137}
138
eac6d242 139sub requires {
140 confess "requires must be called within the role { ... } block."
141 unless $CURRENT_METACLASS;
142 croak "Must specify at least one method" unless @_;
143 $CURRENT_METACLASS->add_required_methods(@_);
144}
145
fa627596 146sub excludes {
147 confess "excludes must be called within the role { ... } block."
148 unless $CURRENT_METACLASS;
149 croak "Must specify at least one role" unless @_;
150 $CURRENT_METACLASS->add_excluded_roles(@_);
151}
152
20725a2d 153# see Moose.pm for discussion
154sub super {
155 return unless $Moose::SUPER_BODY;
156 $Moose::SUPER_BODY->(@Moose::SUPER_ARGS);
157}
158
159sub override {
160 confess "override must be called within the role { ... } block."
161 unless $CURRENT_METACLASS;
162
163 my ($name, $code) = @_;
164 $CURRENT_METACLASS->add_override_method_modifier($name, $code);
165}
166
fc4a95b6 167sub extends { croak "Roles do not currently support 'extends'" }
168
169sub inner { croak "Roles cannot support 'inner'" }
170
171sub augment { croak "Roles cannot support 'augment'" }
172
7b42fc96 1731;
174
a4ac31fa 175__END__
176
177=head1 NAME
178
179MooseX::Role::Parameterized - parameterized roles, at long last
180
181=head1 SYNOPSIS
182
183 package MyRole::Counter;
184 use MooseX::Role::Parameterized;
185
186 parameter name => (
187 is => 'ro',
188 isa => 'Str',
189 required => 1,
190 );
191
192 role {
193 my $p = shift;
194
195 my $name = $p->name;
196
197 has $name => (
198 is => 'rw',
199 isa => 'Int',
200 default => 0,
201 );
202
203 method "increment_$name" => sub {
204 my $self = shift;
205 $self->$name($self->$name + 1);
206 };
207
208 method "decrement_$name" => sub {
209 my $self = shift;
210 $self->$name($self->$name - 1);
211 };
212 };
213
214 package MyGame::Tile;
215 use Moose;
216
217 with 'MyRole::Counter' => { name => 'stepped_on' };
218
219=head1 L<MooseX::Role::Parameterized::Tutorial>
220
221B<Stop!> If you're new here, please read
222L<MooseX::Role::Parameterized::Tutorial>.
223
224=head1 DESCRIPTION
225
226Your parameterized role consists of two things: parameter declarations and a
227C<role> block.
228
229Parameters are declared using the L</parameter> keyword which very much
230resembles L<Moose/has>. You can use any option that L<Moose/has> accepts.
231These parameters will get their values when the consuming class (or role) uses
232L<Moose/with>. A parameter object will be constructed with these values, and
233passed to the C<role> block.
234
235The C<role> block then uses the usual L<Moose::Role> keywords to build up a
236role. You can shift off the parameter object to inspect what the consuming
237class provided as parameters. You can use the parameters to make your role
238customizable!
239
240There are many paths to parameterized roles (hopefully with a consistent enough
241API); I believe this to be the easiest and most flexible implementation.
242Coincidentally, Pugs has a very similar design (I'm not convinced that that is
243a good thing yet).
244
245=head1 CAVEATS
246
247You must use this syntax to declare methods in the role block:
248C<method NAME => sub { ... };>. This is due to a limitation in Perl. In return
249though you can use parameters I<in your methods>!
250
251L<Moose::Role/alias> and L<Moose::Role/excludes> are not yet supported. Because
252I'm totally unsure of whether they should be handled by this module, both
253declaring and providing a parameter named C<alias> or C<excludes> is an error.
254
255=head1 AUTHOR
256
257Shawn M Moore, C<< <sartak@bestpractical.com> >>
258
259=cut
260