1MooseX::Role::ParameterUiszeerd:C:oTnuttroirbiuatle(d3M)PoeorsleXD:o:cRuomleen:t:aPtairoanmeterized::Tutorial(3)
2
3
4
6 MooseX::Role::Parameterized::Tutorial - why and how
7
9 Roles are composable units of behavior. They are useful for factoring
10 out functionality common to many classes from any part of your class
11 hierarchy. See Moose::Cookbook::Roles::Recipe1 for an introduction to
12 Moose::Role.
13
14 While combining roles affords you a great deal of flexibility,
15 individual roles have very little in the way of configurability. Core
16 Moose provides "alias" for renaming methods and "excludes" for ignoring
17 methods. These options are primarily (perhaps solely) for resolving
18 role conflicts. See Moose::Cookbook::Roles::Recipe2 for more about
19 "alias" and "excludes".
20
21 Because roles serve many different masters, they usually provide only
22 the least common denominator of functionality. To empower roles
23 further, more configurability than "alias" and "excludes" is required.
24 Perhaps your role needs to know which method to call when it is done.
25 Or what default value to use for its "url" attribute.
26
27 Parameterized roles offer a solution to these (and other) kinds of
28 problems.
29
31 "with"
32
33 The syntax of a class consuming a parameterized role has not changed
34 from the standard "with". You pass in parameters just like you pass in
35 "alias" and "excludes" to ordinary roles:
36
37 with 'MyRole::InstrumentMethod' => {
38 method_name => 'dbh_do',
39 log_to => 'query.log',
40 };
41
42 You can still combine parameterized roles. You just need to specify
43 parameters immediately after the role they belong to:
44
45 with (
46 'My::Parameterized::Role' => {
47 needs_better_example => 1,
48 },
49 'My::Other::Role',
50 );
51
52 "parameter"
53
54 Inside your parameterized role, you specify a set of parameters. This
55 is exactly like specifying the attributes of a class. Instead of "has"
56 in Moose you use the keyword "parameter", but your parameters can use
57 any options to "has".
58
59 parameter 'delegation' => (
60 isa => 'HashRef|ArrayRef|RegexpRef',
61 predicate => 'has_delegation',
62 );
63
64 You do have to declare what parameters you accept, just like you have
65 to declare what attributes you accept for regular Moose objects.
66
67 One departure from "has" is that we create a reader accessor for you by
68 default. In other words, we assume "is => 'ro'". If you do not want an
69 accessor, you can use "is => 'bare'".
70
71 "role"
72
73 "role" takes a block of code that will be used to generate your role
74 with its parameters bound. Here is where you declare components that
75 depend on parameters. You can declare attributes, methods, modifiers,
76 etc. The first argument to the "role" is an object containing the
77 parameters specified by "with". You can access the parameters just like
78 regular attributes on that object.
79
80 Each time you compose this parameterized role, the "role {}" block will
81 be executed. It will receive a new parameter object and produce an
82 entirely new role. That's the whole point, after all.
83
84 Due to limitations inherent in Perl, you must declare methods with
85 "method name => sub { ... }" instead of the usual "sub name { ... }".
86 Your methods may, of course, close over the parameter object. This
87 means that your methods may use parameters however they wish!
88
90 Ideally these will become fully-explained examples in something
91 resembling Moose::Cookbook. But for now, only a braindump.
92
93 Configure a role's attributes
94 You can rename methods with core Moose, but now you can rename
95 attributes. You can now also choose type, default value, whether
96 it's required, traits, etc.
97
98 parameter traits => (
99 isa => 'ArrayRef',
100 default => sub { [] },
101 );
102
103 parameter type => (
104 isa => 'Str',
105 default => 'Any',
106 );
107
108 role {
109 my $p = shift;
110
111 has action => (
112 traits => $p->traits,
113 isa => $p->type,
114 ...
115 );
116 }
117
118 Inform a role of your class' attributes and methods
119 Core roles can only require methods with specific names chosen by
120 the role. Now your roles can demand that the class specifies a
121 method name you wish the role to instrument, or which attributes to
122 dump to a file.
123
124 parameter instrument_method => (
125 isa => 'Str',
126 required => 1,
127 );
128
129 role {
130 my $p = shift;
131 around $p->instrument_method => sub { ... };
132 }
133
134 Arbitrary execution choices
135 Your role may be able to provide configuration in how the role's
136 methods operate. For example, you can tell the role whether to save
137 intermediate states.
138
139 parameter save_intermediate => (
140 isa => 'Bool',
141 default => 0,
142 );
143
144 role {
145 my $p = shift;
146 method process => sub {
147 ...
148 if ($p->save_intermediate) { ... }
149 ...
150 };
151 }
152
153 Deciding a backend
154 Your role may be able to freeze and thaw your instances using YAML,
155 JSON, Storable. Which backend to use can be a parameter.
156
157 parameter format => (
158 isa => (enum ['Storable', 'YAML', 'JSON']),
159 default => 'Storable',
160 );
161
162 role {
163 my $p = shift;
164 if ($p->format eq 'Storable') {
165 method freeze => \&Storable::freeze;
166 method thaw => \&Storable::thaw;
167 }
168 elsif ($p->format eq 'YAML') {
169 method freeze => \&YAML::Dump;
170 method thaw => \&YAML::Load;
171 }
172 ...
173 }
174
175 Additional validation
176 Ordinary roles can require that its consumers have a particular
177 list of method names. Since parameterized roles have direct access
178 to its consumer, you can inspect it and throw errors if the
179 consumer does not meet your needs.
180
181 role {
182 my $p = shift;
183 my %args = @_;
184 my $consumer = $args{consumer};
185
186 $consumer->find_attribute_by_name('stack')
187 or confess "You must have a 'stack' attribute";
188
189 my $push = $consumer->find_method_by_name('push')
190 or confess "You must have a 'push' method";
191
192 my $params = $push->parsed_signature->positional_params->params;
193 @$params == 1
194 or confess "Your push method must take a single parameter";
195
196 $params->[0]->sigil eq '$'
197 or confess "Your push parameter must be a scalar";
198
199 ...
200 }
201
202
203
204perl v5.12.0 2010-M0o1o-s0e6X::Role::Parameterized::Tutorial(3)