line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
5
|
|
|
5
|
|
2837932
|
use strict; |
|
5
|
|
|
|
|
14
|
|
|
5
|
|
|
|
|
171
|
|
2
|
5
|
|
|
5
|
|
33
|
use warnings; |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
299
|
|
3
|
|
|
|
|
|
|
package MooseX::ComposedBehavior; |
4
|
|
|
|
|
|
|
{ |
5
|
|
|
|
|
|
|
$MooseX::ComposedBehavior::VERSION = '0.004'; |
6
|
|
|
|
|
|
|
} |
7
|
|
|
|
|
|
|
# ABSTRACT: implement custom strategies for composing units of code |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
|
10
|
5
|
|
|
5
|
|
3002
|
use MooseX::ComposedBehavior::Guts; |
|
5
|
|
|
|
|
16
|
|
|
5
|
|
|
|
|
297
|
|
11
|
|
|
|
|
|
|
|
12
|
5
|
|
|
|
|
82
|
use Sub::Exporter -setup => { |
13
|
|
|
|
|
|
|
groups => [ compose => \'_build_composed_behavior' ], |
14
|
5
|
|
|
5
|
|
108
|
}; |
|
5
|
|
|
|
|
9
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
my $i = 0; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub _build_composed_behavior { |
19
|
8
|
|
|
8
|
|
4446
|
my ($self, $name, $arg, $col) = @_; |
20
|
|
|
|
|
|
|
|
21
|
8
|
|
|
|
|
14
|
my %sub; |
22
|
|
|
|
|
|
|
|
23
|
8
|
|
|
|
|
18
|
my $sugar_name = $arg->{sugar_name}; |
24
|
8
|
|
|
|
|
30
|
my $stub_name = 'MooseX_ComposedBehavior_' . $i++ . "_$sugar_name"; |
25
|
|
|
|
|
|
|
|
26
|
8
|
100
|
|
|
|
58
|
my $role = MooseX::ComposedBehavior::Guts->meta->generate_role( |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
27
|
|
|
|
|
|
|
($arg->{role_name} ? (package => $arg->{role_name}) : ()), |
28
|
|
|
|
|
|
|
parameters => { |
29
|
|
|
|
|
|
|
stub_method_name => $stub_name, |
30
|
|
|
|
|
|
|
compositor => $arg->{compositor}, |
31
|
|
|
|
|
|
|
method_name => $arg->{method_name}, |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
(defined $arg->{also_compose} |
34
|
|
|
|
|
|
|
? (also_compose => $arg->{also_compose}) |
35
|
|
|
|
|
|
|
: ()), |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
(defined $arg->{method_order} |
38
|
|
|
|
|
|
|
? (method_order => $arg->{method_order}) |
39
|
|
|
|
|
|
|
: ()), |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
(defined $arg->{context} ? (context => $arg->{context}) : ()), |
42
|
|
|
|
|
|
|
}, |
43
|
|
|
|
|
|
|
); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my $import = Sub::Exporter::build_exporter({ |
46
|
|
|
|
|
|
|
groups => [ default => [ $sugar_name ] ], |
47
|
|
|
|
|
|
|
exports => { |
48
|
|
|
|
|
|
|
$sugar_name => sub { |
49
|
24
|
|
|
24
|
|
3263
|
my ($self, $name, $arg, $col) = @_; |
50
|
24
|
|
|
|
|
85
|
my $target = $col->{INIT}{target}; |
51
|
|
|
|
|
|
|
return sub (&) { |
52
|
25
|
|
|
25
|
|
265757
|
my ($code) = shift; |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
Moose::Util::add_method_modifier( |
55
|
|
|
|
|
|
|
$target->meta, |
56
|
|
|
|
|
|
|
'around', |
57
|
|
|
|
|
|
|
[ |
58
|
|
|
|
|
|
|
$stub_name, |
59
|
|
|
|
|
|
|
sub { |
60
|
41
|
|
|
41
|
|
242
|
my ($orig, $self, $arg, $col) = @_; |
61
|
|
|
|
|
|
|
|
62
|
41
|
100
|
|
|
|
141
|
my @array = (wantarray |
63
|
|
|
|
|
|
|
? $self->$code(@$arg) |
64
|
|
|
|
|
|
|
: scalar $self->$code(@$arg) |
65
|
|
|
|
|
|
|
); |
66
|
|
|
|
|
|
|
|
67
|
41
|
100
|
|
|
|
252
|
push @$col, wantarray ? \@array : $array[0]; |
68
|
41
|
|
|
|
|
126
|
$self->$orig($arg, $col); |
69
|
|
|
|
|
|
|
}, |
70
|
25
|
|
|
|
|
163
|
], |
71
|
|
|
|
|
|
|
); |
72
|
|
|
|
|
|
|
} |
73
|
24
|
|
|
|
|
197
|
}, |
74
|
|
|
|
|
|
|
}, |
75
|
|
|
|
|
|
|
collectors => { |
76
|
|
|
|
|
|
|
INIT => sub { |
77
|
24
|
|
|
24
|
|
87232
|
my $target = $_[1]{into}; |
78
|
24
|
|
|
|
|
95
|
$_[0] = { target => $target }; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# Applying roles to the target fails mysteriously if it is not (yet) |
81
|
|
|
|
|
|
|
# something to which roles can be applied, for example if the "use |
82
|
|
|
|
|
|
|
# Moose" decl appears after "use MooseX::ComposedBehavior" [MJD] |
83
|
24
|
50
|
|
|
|
115
|
Moose::Util::find_meta($target) |
84
|
|
|
|
|
|
|
or Carp::confess(__PACKAGE__ . |
85
|
|
|
|
|
|
|
": target package '$target' is not a Moose class"); |
86
|
24
|
|
|
|
|
379
|
Moose::Util::apply_all_roles($target, $role); |
87
|
24
|
|
|
|
|
38490
|
return 1; |
88
|
|
|
|
|
|
|
}, |
89
|
|
|
|
|
|
|
}, |
90
|
8
|
|
|
|
|
1722
|
}); |
91
|
|
|
|
|
|
|
|
92
|
8
|
|
|
|
|
2077
|
$sub{import} = $import; |
93
|
|
|
|
|
|
|
|
94
|
8
|
|
|
|
|
46
|
return \%sub; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
1; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
__END__ |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=pod |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head1 NAME |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
MooseX::ComposedBehavior - implement custom strategies for composing units of code |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=head1 VERSION |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
version 0.004 |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=head1 OVERVIEW |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
First, B<a warning>: MooseX::ComposedBehavior is a weird and powerful tool |
114
|
|
|
|
|
|
|
meant to be used only I<well> after traditional means of composition have |
115
|
|
|
|
|
|
|
failed. Almost everything most programs will need can be represented with |
116
|
|
|
|
|
|
|
Moose's normal mechanisms for roles, classes, and method modifiers. |
117
|
|
|
|
|
|
|
MooseX::ComposedBehavior addresses edge cases. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Second, B<another warning>: the API for MooseX::ComposedBehavior is not quite |
120
|
|
|
|
|
|
|
stable, and may yet change. More likely, though, the underlying implementation |
121
|
|
|
|
|
|
|
may change. The current implementation is something of a hack, and should be |
122
|
|
|
|
|
|
|
replaced by a more robust one. When that happens, if your code is not sticking |
123
|
|
|
|
|
|
|
strictly to the MooseX::ComposedBehavior API, you will probably have all kinds |
124
|
|
|
|
|
|
|
of weird problems. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head1 SYNOPSIS |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
First, you describe your composed behavior, say in the package "TagProvider": |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
package TagProvider; |
131
|
|
|
|
|
|
|
use strict; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
use MooseX::ComposedBehavior -compose => { |
134
|
|
|
|
|
|
|
method_name => 'tags', |
135
|
|
|
|
|
|
|
sugar_name => 'add_tags', |
136
|
|
|
|
|
|
|
context => 'list', |
137
|
|
|
|
|
|
|
compositor => sub { |
138
|
|
|
|
|
|
|
my ($self, $results) = @_; |
139
|
|
|
|
|
|
|
return map { @$_ } @$results if wantarray; |
140
|
|
|
|
|
|
|
}, |
141
|
|
|
|
|
|
|
}; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
Now, any class or role can C<use TagProvider> to declare that it's going to |
144
|
|
|
|
|
|
|
contribute to a collection of tags. Any class that has used C<TagProvider> |
145
|
|
|
|
|
|
|
will have a C<tags> method, named by the C<method_name> argument. When it's |
146
|
|
|
|
|
|
|
called, code registered the class's constituent parts will be called. For |
147
|
|
|
|
|
|
|
example, consider this example: |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
{ |
150
|
|
|
|
|
|
|
package Foo; |
151
|
|
|
|
|
|
|
use Moose::Role; |
152
|
|
|
|
|
|
|
use TagProvider; |
153
|
|
|
|
|
|
|
add_tags { qw(foo baz) }; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
{ |
157
|
|
|
|
|
|
|
package Bar; |
158
|
|
|
|
|
|
|
use Moose::Role; |
159
|
|
|
|
|
|
|
use t::TagProvider; |
160
|
|
|
|
|
|
|
add_tags { qw(bar quux) }; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
{ |
164
|
|
|
|
|
|
|
package Thing; |
165
|
|
|
|
|
|
|
use Moose; |
166
|
|
|
|
|
|
|
use t::TagProvider; |
167
|
|
|
|
|
|
|
with qw(Foo Bar); |
168
|
|
|
|
|
|
|
add_tags { qw(bingo) }; |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
Now, when you say: |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
my $thing = Thing->new; |
174
|
|
|
|
|
|
|
my @tags = $thing->tags; |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
...each of the C<add_tags> code blocks above is called. The result of each |
177
|
|
|
|
|
|
|
block is gathered and an arrayref of all the results is passed to the |
178
|
|
|
|
|
|
|
C<compositor> routine. The one we defined above is very simple, and just |
179
|
|
|
|
|
|
|
concatenates all the results together. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
C<@tags> will contain, in no particular order: foo, bar, baz, quux, and bingo |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
Result composition can be much more complex, and the context in which the |
184
|
|
|
|
|
|
|
registered blocks are called can be controlled. The options for composed |
185
|
|
|
|
|
|
|
behavior are described below. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head1 HOW TO USE IT |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=over 4 |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item 1 |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
make a helper module, like the "TagProvider" one above |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=item 2 |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
C<use> the helper in every relevant role or class |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=item 3 |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
write blocks using the "sugar" function |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=item 4 |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
call the method on instances as needed |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=item 5 |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
you're done! |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=back |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
There isn't much to using it beyond knowing how to write the actual behavior |
214
|
|
|
|
|
|
|
compositor (or "helper module") that you want. Helper modules will probably |
215
|
|
|
|
|
|
|
always be very short: package declaration, C<use strict>, |
216
|
|
|
|
|
|
|
MooseX::ComposedBehavior invocation, and nothing more. Everything important |
217
|
|
|
|
|
|
|
goes in the arguments to MooseX::ComposedBehavior's import routine: |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
package MyHelper; |
220
|
|
|
|
|
|
|
use strict; |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
use MooseX::ComposedBehavior -compose => { |
223
|
|
|
|
|
|
|
... important stuff goes here ... |
224
|
|
|
|
|
|
|
}; |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
1; |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head2 Options to MooseX::ComposedBehavior |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=over 4 |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=item C<method_name> |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
This is the name of the method that you'll call to get composed results. When |
235
|
|
|
|
|
|
|
this method is called, all the registered behavior is run, the results |
236
|
|
|
|
|
|
|
gathered, and those results passed to the compositor (described below). |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=item C<sugar_name> |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
This is the of the sugar to export into packages using the helper module. It |
241
|
|
|
|
|
|
|
should be called like this (assuming the C<sugar_name> is C<add_behavior>): |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
add_behavior { ...the behavior... ; return $value }; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
When this block is invoked, it will be passed the invocant (the class or |
246
|
|
|
|
|
|
|
instance) followed by all the arguments passed to the main method -- that is, |
247
|
|
|
|
|
|
|
the method named by C<method_name>. |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=item C<context> |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
This parameter forces a specific calling context on the registered blocks of |
252
|
|
|
|
|
|
|
behavior. It can be either "scalar" or "list" or may be omitted. The blocks |
253
|
|
|
|
|
|
|
registered by the sugar function will always be called in the given context. |
254
|
|
|
|
|
|
|
If no context is given, they will be called in the same context that the main |
255
|
|
|
|
|
|
|
method was called. |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
The C<context> option does I<not> affect the context in which the compositor is |
258
|
|
|
|
|
|
|
called. It is always called in the same context as the main method. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
Void context is propagated as scalar context. B<This may change in the |
261
|
|
|
|
|
|
|
future> to support void context per se. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=item C<compositor> |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
The compositor is a coderef that gets all the results of registered behavior |
266
|
|
|
|
|
|
|
(and C<also_compose>, below) and combines them into a final result, which will |
267
|
|
|
|
|
|
|
be returned from the main method. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
It is passed the invocant, followed by an arrayref of block results. The |
270
|
|
|
|
|
|
|
block results are in an undefined order. If the blocks were called in scalar |
271
|
|
|
|
|
|
|
context, each block's result is the returned scalar. If the blocks were called |
272
|
|
|
|
|
|
|
in list context, each block's result is an arrayref containing the returned |
273
|
|
|
|
|
|
|
list. |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
The compositor is I<always> called in the same context as the main method, even |
276
|
|
|
|
|
|
|
if the behavior blocks were forced into a different context. |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=item C<also_compose> |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
This parameter is a coderef or method name, or an arrayref of coderefs and/or |
281
|
|
|
|
|
|
|
method names. These will be called along with the rest of the registered |
282
|
|
|
|
|
|
|
behavior, in the same context, and their results will be composed like any |
283
|
|
|
|
|
|
|
other results. It would be possible to simply write this: |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
add_behavior { |
286
|
|
|
|
|
|
|
my $self = shift; |
287
|
|
|
|
|
|
|
$self->some_method; |
288
|
|
|
|
|
|
|
}; |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
...but if this was somehow composed more than once (by repeating a role |
291
|
|
|
|
|
|
|
application, for example) you would get the results of C<some_method> more than |
292
|
|
|
|
|
|
|
once. By putting the method into the C<also_compose> option, you are |
293
|
|
|
|
|
|
|
guaranteed that it will run only once. |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=item C<method_order> |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
By default, registered behaviors are called on the most derived class and its |
298
|
|
|
|
|
|
|
roles, first. That is: the class closest to the class of the method invocant, |
299
|
|
|
|
|
|
|
then upward toward superclasses. This is how the C<DEMOLISH> methods in |
300
|
|
|
|
|
|
|
L<Moose::Object> work. |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
If C<method_order> is provided, and is "reverse" then the methods are called in |
303
|
|
|
|
|
|
|
reverse order: base class first, followed by derived classes. This is how the |
304
|
|
|
|
|
|
|
C<BUILD> methods in Moose::Object work. |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=back |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=head1 AUTHOR |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
Ricardo Signes <rjbs@cpan.org> |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
This software is copyright (c) 2013 by Ricardo Signes. |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
317
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=cut |