line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
5
|
|
|
5
|
|
2400042
|
use strict; |
|
5
|
|
|
|
|
48
|
|
|
5
|
|
|
|
|
129
|
|
2
|
5
|
|
|
5
|
|
25
|
use warnings; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
377
|
|
3
|
|
|
|
|
|
|
package MooseX::ComposedBehavior 0.005; |
4
|
|
|
|
|
|
|
# ABSTRACT: implement custom strategies for composing units of code |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
#pod =begin :prelude |
7
|
|
|
|
|
|
|
#pod |
8
|
|
|
|
|
|
|
#pod =head1 OVERVIEW |
9
|
|
|
|
|
|
|
#pod |
10
|
|
|
|
|
|
|
#pod First, B<a warning>: MooseX::ComposedBehavior is a weird and powerful tool |
11
|
|
|
|
|
|
|
#pod meant to be used only I<well> after traditional means of composition have |
12
|
|
|
|
|
|
|
#pod failed. Almost everything most programs will need can be represented with |
13
|
|
|
|
|
|
|
#pod Moose's normal mechanisms for roles, classes, and method modifiers. |
14
|
|
|
|
|
|
|
#pod MooseX::ComposedBehavior addresses edge cases. |
15
|
|
|
|
|
|
|
#pod |
16
|
|
|
|
|
|
|
#pod Second, B<another warning>: the API for MooseX::ComposedBehavior is not quite |
17
|
|
|
|
|
|
|
#pod stable, and may yet change. More likely, though, the underlying implementation |
18
|
|
|
|
|
|
|
#pod may change. The current implementation is something of a hack, and should be |
19
|
|
|
|
|
|
|
#pod replaced by a more robust one. When that happens, if your code is not sticking |
20
|
|
|
|
|
|
|
#pod strictly to the MooseX::ComposedBehavior API, you will probably have all kinds |
21
|
|
|
|
|
|
|
#pod of weird problems. |
22
|
|
|
|
|
|
|
#pod |
23
|
|
|
|
|
|
|
#pod =end :prelude |
24
|
|
|
|
|
|
|
#pod |
25
|
|
|
|
|
|
|
#pod =head1 SYNOPSIS |
26
|
|
|
|
|
|
|
#pod |
27
|
|
|
|
|
|
|
#pod First, you describe your composed behavior, say in the package "TagProvider": |
28
|
|
|
|
|
|
|
#pod |
29
|
|
|
|
|
|
|
#pod package TagProvider; |
30
|
|
|
|
|
|
|
#pod use strict; |
31
|
|
|
|
|
|
|
#pod |
32
|
|
|
|
|
|
|
#pod use MooseX::ComposedBehavior -compose => { |
33
|
|
|
|
|
|
|
#pod method_name => 'tags', |
34
|
|
|
|
|
|
|
#pod sugar_name => 'add_tags', |
35
|
|
|
|
|
|
|
#pod context => 'list', |
36
|
|
|
|
|
|
|
#pod compositor => sub { |
37
|
|
|
|
|
|
|
#pod my ($self, $results) = @_; |
38
|
|
|
|
|
|
|
#pod return map { @$_ } @$results if wantarray; |
39
|
|
|
|
|
|
|
#pod }, |
40
|
|
|
|
|
|
|
#pod }; |
41
|
|
|
|
|
|
|
#pod |
42
|
|
|
|
|
|
|
#pod Now, any class or role can C<use TagProvider> to declare that it's going to |
43
|
|
|
|
|
|
|
#pod contribute to a collection of tags. Any class that has used C<TagProvider> |
44
|
|
|
|
|
|
|
#pod will have a C<tags> method, named by the C<method_name> argument. When it's |
45
|
|
|
|
|
|
|
#pod called, code registered the class's constituent parts will be called. For |
46
|
|
|
|
|
|
|
#pod example, consider this example: |
47
|
|
|
|
|
|
|
#pod |
48
|
|
|
|
|
|
|
#pod { |
49
|
|
|
|
|
|
|
#pod package Foo; |
50
|
|
|
|
|
|
|
#pod use Moose::Role; |
51
|
|
|
|
|
|
|
#pod use TagProvider; |
52
|
|
|
|
|
|
|
#pod add_tags { qw(foo baz) }; |
53
|
|
|
|
|
|
|
#pod } |
54
|
|
|
|
|
|
|
#pod |
55
|
|
|
|
|
|
|
#pod { |
56
|
|
|
|
|
|
|
#pod package Bar; |
57
|
|
|
|
|
|
|
#pod use Moose::Role; |
58
|
|
|
|
|
|
|
#pod use t::TagProvider; |
59
|
|
|
|
|
|
|
#pod add_tags { qw(bar quux) }; |
60
|
|
|
|
|
|
|
#pod } |
61
|
|
|
|
|
|
|
#pod |
62
|
|
|
|
|
|
|
#pod { |
63
|
|
|
|
|
|
|
#pod package Thing; |
64
|
|
|
|
|
|
|
#pod use Moose; |
65
|
|
|
|
|
|
|
#pod use t::TagProvider; |
66
|
|
|
|
|
|
|
#pod with qw(Foo Bar); |
67
|
|
|
|
|
|
|
#pod add_tags { qw(bingo) }; |
68
|
|
|
|
|
|
|
#pod } |
69
|
|
|
|
|
|
|
#pod |
70
|
|
|
|
|
|
|
#pod Now, when you say: |
71
|
|
|
|
|
|
|
#pod |
72
|
|
|
|
|
|
|
#pod my $thing = Thing->new; |
73
|
|
|
|
|
|
|
#pod my @tags = $thing->tags; |
74
|
|
|
|
|
|
|
#pod |
75
|
|
|
|
|
|
|
#pod ...each of the C<add_tags> code blocks above is called. The result of each |
76
|
|
|
|
|
|
|
#pod block is gathered and an arrayref of all the results is passed to the |
77
|
|
|
|
|
|
|
#pod C<compositor> routine. The one we defined above is very simple, and just |
78
|
|
|
|
|
|
|
#pod concatenates all the results together. |
79
|
|
|
|
|
|
|
#pod |
80
|
|
|
|
|
|
|
#pod C<@tags> will contain, in no particular order: foo, bar, baz, quux, and bingo |
81
|
|
|
|
|
|
|
#pod |
82
|
|
|
|
|
|
|
#pod Result composition can be much more complex, and the context in which the |
83
|
|
|
|
|
|
|
#pod registered blocks are called can be controlled. The options for composed |
84
|
|
|
|
|
|
|
#pod behavior are described below. |
85
|
|
|
|
|
|
|
#pod |
86
|
|
|
|
|
|
|
#pod =head1 HOW TO USE IT |
87
|
|
|
|
|
|
|
#pod |
88
|
|
|
|
|
|
|
#pod =for :list |
89
|
|
|
|
|
|
|
#pod 1. make a helper module, like the "TagProvider" one above |
90
|
|
|
|
|
|
|
#pod 2. C<use> the helper in every relevant role or class |
91
|
|
|
|
|
|
|
#pod 3. write blocks using the "sugar" function |
92
|
|
|
|
|
|
|
#pod 4. call the method on instances as needed |
93
|
|
|
|
|
|
|
#pod 5. you're done! |
94
|
|
|
|
|
|
|
#pod |
95
|
|
|
|
|
|
|
#pod There isn't much to using it beyond knowing how to write the actual behavior |
96
|
|
|
|
|
|
|
#pod compositor (or "helper module") that you want. Helper modules will probably |
97
|
|
|
|
|
|
|
#pod always be very short: package declaration, C<use strict>, |
98
|
|
|
|
|
|
|
#pod MooseX::ComposedBehavior invocation, and nothing more. Everything important |
99
|
|
|
|
|
|
|
#pod goes in the arguments to MooseX::ComposedBehavior's import routine: |
100
|
|
|
|
|
|
|
#pod |
101
|
|
|
|
|
|
|
#pod package MyHelper; |
102
|
|
|
|
|
|
|
#pod use strict; |
103
|
|
|
|
|
|
|
#pod |
104
|
|
|
|
|
|
|
#pod use MooseX::ComposedBehavior -compose => { |
105
|
|
|
|
|
|
|
#pod ... important stuff goes here ... |
106
|
|
|
|
|
|
|
#pod }; |
107
|
|
|
|
|
|
|
#pod |
108
|
|
|
|
|
|
|
#pod 1; |
109
|
|
|
|
|
|
|
#pod |
110
|
|
|
|
|
|
|
#pod =head2 Options to MooseX::ComposedBehavior |
111
|
|
|
|
|
|
|
#pod |
112
|
|
|
|
|
|
|
#pod =begin :list |
113
|
|
|
|
|
|
|
#pod |
114
|
|
|
|
|
|
|
#pod = C<method_name> |
115
|
|
|
|
|
|
|
#pod |
116
|
|
|
|
|
|
|
#pod This is the name of the method that you'll call to get composed results. When |
117
|
|
|
|
|
|
|
#pod this method is called, all the registered behavior is run, the results |
118
|
|
|
|
|
|
|
#pod gathered, and those results passed to the compositor (described below). |
119
|
|
|
|
|
|
|
#pod |
120
|
|
|
|
|
|
|
#pod = C<sugar_name> |
121
|
|
|
|
|
|
|
#pod |
122
|
|
|
|
|
|
|
#pod This is the of the sugar to export into packages using the helper module. It |
123
|
|
|
|
|
|
|
#pod should be called like this (assuming the C<sugar_name> is C<add_behavior>): |
124
|
|
|
|
|
|
|
#pod |
125
|
|
|
|
|
|
|
#pod add_behavior { ...the behavior... ; return $value }; |
126
|
|
|
|
|
|
|
#pod |
127
|
|
|
|
|
|
|
#pod When this block is invoked, it will be passed the invocant (the class or |
128
|
|
|
|
|
|
|
#pod instance) followed by all the arguments passed to the main method -- that is, |
129
|
|
|
|
|
|
|
#pod the method named by C<method_name>. |
130
|
|
|
|
|
|
|
#pod |
131
|
|
|
|
|
|
|
#pod = C<context> |
132
|
|
|
|
|
|
|
#pod |
133
|
|
|
|
|
|
|
#pod This parameter forces a specific calling context on the registered blocks of |
134
|
|
|
|
|
|
|
#pod behavior. It can be either "scalar" or "list" or may be omitted. The blocks |
135
|
|
|
|
|
|
|
#pod registered by the sugar function will always be called in the given context. |
136
|
|
|
|
|
|
|
#pod If no context is given, they will be called in the same context that the main |
137
|
|
|
|
|
|
|
#pod method was called. |
138
|
|
|
|
|
|
|
#pod |
139
|
|
|
|
|
|
|
#pod The C<context> option does I<not> affect the context in which the compositor is |
140
|
|
|
|
|
|
|
#pod called. It is always called in the same context as the main method. |
141
|
|
|
|
|
|
|
#pod |
142
|
|
|
|
|
|
|
#pod Void context is propagated as scalar context. B<This may change in the |
143
|
|
|
|
|
|
|
#pod future> to support void context per se. |
144
|
|
|
|
|
|
|
#pod |
145
|
|
|
|
|
|
|
#pod = C<compositor> |
146
|
|
|
|
|
|
|
#pod |
147
|
|
|
|
|
|
|
#pod The compositor is a coderef that gets all the results of registered behavior |
148
|
|
|
|
|
|
|
#pod (and C<also_compose>, below) and combines them into a final result, which will |
149
|
|
|
|
|
|
|
#pod be returned from the main method. |
150
|
|
|
|
|
|
|
#pod |
151
|
|
|
|
|
|
|
#pod It is passed the invocant, followed by an arrayref of block results. The |
152
|
|
|
|
|
|
|
#pod block results are in an undefined order. If the blocks were called in scalar |
153
|
|
|
|
|
|
|
#pod context, each block's result is the returned scalar. If the blocks were called |
154
|
|
|
|
|
|
|
#pod in list context, each block's result is an arrayref containing the returned |
155
|
|
|
|
|
|
|
#pod list. |
156
|
|
|
|
|
|
|
#pod |
157
|
|
|
|
|
|
|
#pod The compositor is I<always> called in the same context as the main method, even |
158
|
|
|
|
|
|
|
#pod if the behavior blocks were forced into a different context. |
159
|
|
|
|
|
|
|
#pod |
160
|
|
|
|
|
|
|
#pod = C<also_compose> |
161
|
|
|
|
|
|
|
#pod |
162
|
|
|
|
|
|
|
#pod This parameter is a coderef or method name, or an arrayref of coderefs and/or |
163
|
|
|
|
|
|
|
#pod method names. These will be called along with the rest of the registered |
164
|
|
|
|
|
|
|
#pod behavior, in the same context, and their results will be composed like any |
165
|
|
|
|
|
|
|
#pod other results. It would be possible to simply write this: |
166
|
|
|
|
|
|
|
#pod |
167
|
|
|
|
|
|
|
#pod add_behavior { |
168
|
|
|
|
|
|
|
#pod my $self = shift; |
169
|
|
|
|
|
|
|
#pod $self->some_method; |
170
|
|
|
|
|
|
|
#pod }; |
171
|
|
|
|
|
|
|
#pod |
172
|
|
|
|
|
|
|
#pod ...but if this was somehow composed more than once (by repeating a role |
173
|
|
|
|
|
|
|
#pod application, for example) you would get the results of C<some_method> more than |
174
|
|
|
|
|
|
|
#pod once. By putting the method into the C<also_compose> option, you are |
175
|
|
|
|
|
|
|
#pod guaranteed that it will run only once. |
176
|
|
|
|
|
|
|
#pod |
177
|
|
|
|
|
|
|
#pod = C<method_order> |
178
|
|
|
|
|
|
|
#pod |
179
|
|
|
|
|
|
|
#pod By default, registered behaviors are called on the most derived class and its |
180
|
|
|
|
|
|
|
#pod roles, first. That is: the class closest to the class of the method invocant, |
181
|
|
|
|
|
|
|
#pod then upward toward superclasses. This is how the C<DEMOLISH> methods in |
182
|
|
|
|
|
|
|
#pod L<Moose::Object> work. |
183
|
|
|
|
|
|
|
#pod |
184
|
|
|
|
|
|
|
#pod If C<method_order> is provided, and is "reverse" then the methods are called in |
185
|
|
|
|
|
|
|
#pod reverse order: base class first, followed by derived classes. This is how the |
186
|
|
|
|
|
|
|
#pod C<BUILD> methods in Moose::Object work. |
187
|
|
|
|
|
|
|
#pod |
188
|
|
|
|
|
|
|
#pod =end :list |
189
|
|
|
|
|
|
|
#pod |
190
|
|
|
|
|
|
|
#pod =cut |
191
|
|
|
|
|
|
|
|
192
|
5
|
|
|
5
|
|
1912
|
use MooseX::ComposedBehavior::Guts; |
|
5
|
|
|
|
|
19
|
|
|
5
|
|
|
|
|
282
|
|
193
|
|
|
|
|
|
|
|
194
|
5
|
|
|
|
|
62
|
use Sub::Exporter -setup => { |
195
|
|
|
|
|
|
|
groups => [ compose => \'_build_composed_behavior' ], |
196
|
5
|
|
|
5
|
|
41
|
}; |
|
5
|
|
|
|
|
11
|
|
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
my $i = 0; |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub _build_composed_behavior { |
201
|
8
|
|
|
8
|
|
2766
|
my ($self, $name, $arg, $col) = @_; |
202
|
|
|
|
|
|
|
|
203
|
8
|
|
|
|
|
15
|
my %sub; |
204
|
|
|
|
|
|
|
|
205
|
8
|
|
|
|
|
17
|
my $sugar_name = $arg->{sugar_name}; |
206
|
8
|
|
|
|
|
26
|
my $stub_name = 'MooseX_ComposedBehavior_' . $i++ . "_$sugar_name"; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
my $role = MooseX::ComposedBehavior::Guts->meta->generate_role( |
209
|
|
|
|
|
|
|
($arg->{role_name} ? (package => $arg->{role_name}) : ()), |
210
|
|
|
|
|
|
|
parameters => { |
211
|
|
|
|
|
|
|
stub_method_name => $stub_name, |
212
|
|
|
|
|
|
|
compositor => $arg->{compositor}, |
213
|
|
|
|
|
|
|
method_name => $arg->{method_name}, |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
(defined $arg->{also_compose} |
216
|
|
|
|
|
|
|
? (also_compose => $arg->{also_compose}) |
217
|
|
|
|
|
|
|
: ()), |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
(defined $arg->{method_order} |
220
|
|
|
|
|
|
|
? (method_order => $arg->{method_order}) |
221
|
|
|
|
|
|
|
: ()), |
222
|
|
|
|
|
|
|
|
223
|
8
|
100
|
|
|
|
48
|
(defined $arg->{context} ? (context => $arg->{context}) : ()), |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
224
|
|
|
|
|
|
|
}, |
225
|
|
|
|
|
|
|
); |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
my $import = Sub::Exporter::build_exporter({ |
228
|
|
|
|
|
|
|
groups => [ default => [ $sugar_name ] ], |
229
|
|
|
|
|
|
|
exports => { |
230
|
|
|
|
|
|
|
$sugar_name => sub { |
231
|
24
|
|
|
24
|
|
3027
|
my ($self, $name, $arg, $col) = @_; |
232
|
24
|
|
|
|
|
57
|
my $target = $col->{INIT}{target}; |
233
|
|
|
|
|
|
|
return sub (&) { |
234
|
25
|
|
|
25
|
|
192214
|
my ($code) = shift; |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
Moose::Util::add_method_modifier( |
237
|
|
|
|
|
|
|
$target->meta, |
238
|
|
|
|
|
|
|
'around', |
239
|
|
|
|
|
|
|
[ |
240
|
|
|
|
|
|
|
$stub_name, |
241
|
|
|
|
|
|
|
sub { |
242
|
41
|
|
|
41
|
|
359
|
my ($orig, $self, $arg, $col) = @_; |
243
|
|
|
|
|
|
|
|
244
|
41
|
100
|
|
|
|
125
|
my @array = (wantarray |
245
|
|
|
|
|
|
|
? $self->$code(@$arg) |
246
|
|
|
|
|
|
|
: scalar $self->$code(@$arg) |
247
|
|
|
|
|
|
|
); |
248
|
|
|
|
|
|
|
|
249
|
41
|
100
|
|
|
|
192
|
push @$col, wantarray ? \@array : $array[0]; |
250
|
41
|
|
|
|
|
90
|
$self->$orig($arg, $col); |
251
|
|
|
|
|
|
|
}, |
252
|
25
|
|
|
|
|
125
|
], |
253
|
|
|
|
|
|
|
); |
254
|
|
|
|
|
|
|
} |
255
|
24
|
|
|
|
|
124
|
}, |
256
|
|
|
|
|
|
|
}, |
257
|
|
|
|
|
|
|
collectors => { |
258
|
|
|
|
|
|
|
INIT => sub { |
259
|
24
|
|
|
24
|
|
71235
|
my $target = $_[1]{into}; |
260
|
24
|
|
|
|
|
67
|
$_[0] = { target => $target }; |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# Applying roles to the target fails mysteriously if it is not (yet) |
263
|
|
|
|
|
|
|
# something to which roles can be applied, for example if the "use |
264
|
|
|
|
|
|
|
# Moose" decl appears after "use MooseX::ComposedBehavior" [MJD] |
265
|
24
|
50
|
|
|
|
81
|
Moose::Util::find_meta($target) |
266
|
|
|
|
|
|
|
or Carp::confess(__PACKAGE__ . |
267
|
|
|
|
|
|
|
": target package '$target' is not a Moose class"); |
268
|
24
|
|
|
|
|
300
|
Moose::Util::apply_all_roles($target, $role); |
269
|
24
|
|
|
|
|
31804
|
return 1; |
270
|
|
|
|
|
|
|
}, |
271
|
|
|
|
|
|
|
}, |
272
|
8
|
|
|
|
|
1435
|
}); |
273
|
|
|
|
|
|
|
|
274
|
8
|
|
|
|
|
2226
|
$sub{import} = $import; |
275
|
|
|
|
|
|
|
|
276
|
8
|
|
|
|
|
30
|
return \%sub; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
1; |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
__END__ |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
=pod |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=encoding UTF-8 |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=head1 NAME |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
MooseX::ComposedBehavior - implement custom strategies for composing units of code |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=head1 VERSION |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
version 0.005 |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=head1 OVERVIEW |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
First, B<a warning>: MooseX::ComposedBehavior is a weird and powerful tool |
298
|
|
|
|
|
|
|
meant to be used only I<well> after traditional means of composition have |
299
|
|
|
|
|
|
|
failed. Almost everything most programs will need can be represented with |
300
|
|
|
|
|
|
|
Moose's normal mechanisms for roles, classes, and method modifiers. |
301
|
|
|
|
|
|
|
MooseX::ComposedBehavior addresses edge cases. |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
Second, B<another warning>: the API for MooseX::ComposedBehavior is not quite |
304
|
|
|
|
|
|
|
stable, and may yet change. More likely, though, the underlying implementation |
305
|
|
|
|
|
|
|
may change. The current implementation is something of a hack, and should be |
306
|
|
|
|
|
|
|
replaced by a more robust one. When that happens, if your code is not sticking |
307
|
|
|
|
|
|
|
strictly to the MooseX::ComposedBehavior API, you will probably have all kinds |
308
|
|
|
|
|
|
|
of weird problems. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=head1 SYNOPSIS |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
First, you describe your composed behavior, say in the package "TagProvider": |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
package TagProvider; |
315
|
|
|
|
|
|
|
use strict; |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
use MooseX::ComposedBehavior -compose => { |
318
|
|
|
|
|
|
|
method_name => 'tags', |
319
|
|
|
|
|
|
|
sugar_name => 'add_tags', |
320
|
|
|
|
|
|
|
context => 'list', |
321
|
|
|
|
|
|
|
compositor => sub { |
322
|
|
|
|
|
|
|
my ($self, $results) = @_; |
323
|
|
|
|
|
|
|
return map { @$_ } @$results if wantarray; |
324
|
|
|
|
|
|
|
}, |
325
|
|
|
|
|
|
|
}; |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
Now, any class or role can C<use TagProvider> to declare that it's going to |
328
|
|
|
|
|
|
|
contribute to a collection of tags. Any class that has used C<TagProvider> |
329
|
|
|
|
|
|
|
will have a C<tags> method, named by the C<method_name> argument. When it's |
330
|
|
|
|
|
|
|
called, code registered the class's constituent parts will be called. For |
331
|
|
|
|
|
|
|
example, consider this example: |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
{ |
334
|
|
|
|
|
|
|
package Foo; |
335
|
|
|
|
|
|
|
use Moose::Role; |
336
|
|
|
|
|
|
|
use TagProvider; |
337
|
|
|
|
|
|
|
add_tags { qw(foo baz) }; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
{ |
341
|
|
|
|
|
|
|
package Bar; |
342
|
|
|
|
|
|
|
use Moose::Role; |
343
|
|
|
|
|
|
|
use t::TagProvider; |
344
|
|
|
|
|
|
|
add_tags { qw(bar quux) }; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
{ |
348
|
|
|
|
|
|
|
package Thing; |
349
|
|
|
|
|
|
|
use Moose; |
350
|
|
|
|
|
|
|
use t::TagProvider; |
351
|
|
|
|
|
|
|
with qw(Foo Bar); |
352
|
|
|
|
|
|
|
add_tags { qw(bingo) }; |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Now, when you say: |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
my $thing = Thing->new; |
358
|
|
|
|
|
|
|
my @tags = $thing->tags; |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
...each of the C<add_tags> code blocks above is called. The result of each |
361
|
|
|
|
|
|
|
block is gathered and an arrayref of all the results is passed to the |
362
|
|
|
|
|
|
|
C<compositor> routine. The one we defined above is very simple, and just |
363
|
|
|
|
|
|
|
concatenates all the results together. |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
C<@tags> will contain, in no particular order: foo, bar, baz, quux, and bingo |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Result composition can be much more complex, and the context in which the |
368
|
|
|
|
|
|
|
registered blocks are called can be controlled. The options for composed |
369
|
|
|
|
|
|
|
behavior are described below. |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=head1 PERL VERSION |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
This library should run on perls released even a long time ago. It should work |
374
|
|
|
|
|
|
|
on any version of perl released in the last five years. |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
Although it may work on older versions of perl, no guarantee is made that the |
377
|
|
|
|
|
|
|
minimum required version will not be increased. The version may be increased |
378
|
|
|
|
|
|
|
for any reason, and there is no promise that patches will be accepted to lower |
379
|
|
|
|
|
|
|
the minimum required perl. |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=head1 HOW TO USE IT |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=over 4 |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=item 1 |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
make a helper module, like the "TagProvider" one above |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=item 2 |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
C<use> the helper in every relevant role or class |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=item 3 |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
write blocks using the "sugar" function |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=item 4 |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
call the method on instances as needed |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=item 5 |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
you're done! |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=back |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
There isn't much to using it beyond knowing how to write the actual behavior |
408
|
|
|
|
|
|
|
compositor (or "helper module") that you want. Helper modules will probably |
409
|
|
|
|
|
|
|
always be very short: package declaration, C<use strict>, |
410
|
|
|
|
|
|
|
MooseX::ComposedBehavior invocation, and nothing more. Everything important |
411
|
|
|
|
|
|
|
goes in the arguments to MooseX::ComposedBehavior's import routine: |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
package MyHelper; |
414
|
|
|
|
|
|
|
use strict; |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
use MooseX::ComposedBehavior -compose => { |
417
|
|
|
|
|
|
|
... important stuff goes here ... |
418
|
|
|
|
|
|
|
}; |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
1; |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head2 Options to MooseX::ComposedBehavior |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=over 4 |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
=item C<method_name> |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
This is the name of the method that you'll call to get composed results. When |
429
|
|
|
|
|
|
|
this method is called, all the registered behavior is run, the results |
430
|
|
|
|
|
|
|
gathered, and those results passed to the compositor (described below). |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=item C<sugar_name> |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
This is the of the sugar to export into packages using the helper module. It |
435
|
|
|
|
|
|
|
should be called like this (assuming the C<sugar_name> is C<add_behavior>): |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
add_behavior { ...the behavior... ; return $value }; |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
When this block is invoked, it will be passed the invocant (the class or |
440
|
|
|
|
|
|
|
instance) followed by all the arguments passed to the main method -- that is, |
441
|
|
|
|
|
|
|
the method named by C<method_name>. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=item C<context> |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
This parameter forces a specific calling context on the registered blocks of |
446
|
|
|
|
|
|
|
behavior. It can be either "scalar" or "list" or may be omitted. The blocks |
447
|
|
|
|
|
|
|
registered by the sugar function will always be called in the given context. |
448
|
|
|
|
|
|
|
If no context is given, they will be called in the same context that the main |
449
|
|
|
|
|
|
|
method was called. |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
The C<context> option does I<not> affect the context in which the compositor is |
452
|
|
|
|
|
|
|
called. It is always called in the same context as the main method. |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
Void context is propagated as scalar context. B<This may change in the |
455
|
|
|
|
|
|
|
future> to support void context per se. |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=item C<compositor> |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
The compositor is a coderef that gets all the results of registered behavior |
460
|
|
|
|
|
|
|
(and C<also_compose>, below) and combines them into a final result, which will |
461
|
|
|
|
|
|
|
be returned from the main method. |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
It is passed the invocant, followed by an arrayref of block results. The |
464
|
|
|
|
|
|
|
block results are in an undefined order. If the blocks were called in scalar |
465
|
|
|
|
|
|
|
context, each block's result is the returned scalar. If the blocks were called |
466
|
|
|
|
|
|
|
in list context, each block's result is an arrayref containing the returned |
467
|
|
|
|
|
|
|
list. |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
The compositor is I<always> called in the same context as the main method, even |
470
|
|
|
|
|
|
|
if the behavior blocks were forced into a different context. |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=item C<also_compose> |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
This parameter is a coderef or method name, or an arrayref of coderefs and/or |
475
|
|
|
|
|
|
|
method names. These will be called along with the rest of the registered |
476
|
|
|
|
|
|
|
behavior, in the same context, and their results will be composed like any |
477
|
|
|
|
|
|
|
other results. It would be possible to simply write this: |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
add_behavior { |
480
|
|
|
|
|
|
|
my $self = shift; |
481
|
|
|
|
|
|
|
$self->some_method; |
482
|
|
|
|
|
|
|
}; |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
...but if this was somehow composed more than once (by repeating a role |
485
|
|
|
|
|
|
|
application, for example) you would get the results of C<some_method> more than |
486
|
|
|
|
|
|
|
once. By putting the method into the C<also_compose> option, you are |
487
|
|
|
|
|
|
|
guaranteed that it will run only once. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=item C<method_order> |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
By default, registered behaviors are called on the most derived class and its |
492
|
|
|
|
|
|
|
roles, first. That is: the class closest to the class of the method invocant, |
493
|
|
|
|
|
|
|
then upward toward superclasses. This is how the C<DEMOLISH> methods in |
494
|
|
|
|
|
|
|
L<Moose::Object> work. |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
If C<method_order> is provided, and is "reverse" then the methods are called in |
497
|
|
|
|
|
|
|
reverse order: base class first, followed by derived classes. This is how the |
498
|
|
|
|
|
|
|
C<BUILD> methods in Moose::Object work. |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=back |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=head1 AUTHOR |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
Ricardo Signes <cpan@semiotic.systems> |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=head1 CONTRIBUTORS |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
=for stopwords Mark Dominus Ricardo Signes |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=over 4 |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=item * |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
Mark Dominus <mjd@icgroup.com> |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=item * |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
Ricardo Signes <rjbs@semiotic.systems> |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=back |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
This software is copyright (c) 2022 by Ricardo Signes. |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
527
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=cut |