Expand tutorial a bit more
[gitmo/MooseX-Role-Parameterized.git] / lib / MooseX / Role / Parameterized / Tutorial.pod
1 =pod
2
3 =head1 NAME
4
5 MooseX::Role::Parameterized::Tutorial - why and how
6
7 =head1 MOTIVATION
8
9 Roles are composable units of behavior. They are useful for factoring out
10 functionality common to many classes from any part of your class hierarchy. See
11 L<Moose::Cookbook::Roles::Recipe1> for an introduction to L<Moose::Role>.
12
13 While combining roles affords you a great deal of flexibility, individual roles
14 have very little in the way of configurability. Core Moose provides C<alias>
15 for renaming methods and C<excludes> for ignoring methods. These options are
16 primarily (perhaps solely) for disambiguating role conflicts. See
17 L<Moose::Cookbook::Roles::Recipe2> for more about C<alias> and C<excludes>.
18
19 Because roles serve many different masters, they usually provide only the least
20 common denominator of functionality. To empower roles further, more
21 configurability than C<alias> and C<excludes> is required. Perhaps your role
22 needs to know which method to call when it is done. Or what default value to
23 use for its C<url> attribute.
24
25 Parameterized roles offer exactly this solution.
26
27 =head1 USAGE
28
29 =head3 C<with>
30
31 The syntax of a class consuming a parameterized role has not changed from the
32 standard C<with>. You pass in parameters just like you pass in C<alias> and
33 C<excludes> to ordinary roles:
34
35     with 'MyRole::InstrumentMethod' => {
36         method_name => 'dbh_do',
37         log_to      => 'query.log',
38     };
39
40 You can still combine parameterized roles. You just need to specify parameters
41 immediately after the role they belong to:
42
43     with (
44         'My::Parameterized::Role' => {
45             needs_better_example => 1,
46         },
47         'My::Other::Role',
48     );
49
50 =head3 C<parameter>
51
52 Inside your parameterized role, you specify a set of parameters. This is
53 exactly like specifying the attributes of a class. Instead of C<has> you use
54 the keyword C<parameter>, but your parameters can use any options to C<has>.
55
56     parameter 'delegation' => (
57         isa       => 'HashRef|ArrayRef|RegexpRef',
58         predicate => 'has_delegation',
59     );
60
61 You do have to declare what parameters you accept, just like you have to
62 declare what attributes you accept for regular Moose objects.
63
64 =head3 C<role>
65
66 C<role> takes a block of code that will be used to generate your role with its
67 parameters bound. Here is where you declare parameterized components: use
68 C<has>, method modifiers, and so on. The C<role> block receives an argument,
69 which contains the parameters specified by C<with>. You can access the
70 parameters just like regular attributes on that object.
71
72 Each time you compose this parameterized role, the role {} block will be
73 executed. It will receive a new parameter object and produce an entirely new
74 role. That's the whole point, after all.
75
76 Due to limitations inherent in Perl, you must declare methods with
77 C<< method name => sub { ... } >> instead of the usual C<sub name { ... }>.
78 Your methods may, of course, close over the parameter object. This means that
79 your methods may use parameters however they wish!
80
81 =head1 IMPLEMENTATION NOTES
82
83 =head1 USES
84
85 Ideally these will become fully-explained examples in something resembling
86 L<Moose::Cookbook>. But for now, only a braindump.
87
88 =over 4
89
90 =item Configure a role's attributes
91
92 You can rename methods with core Moose, but now you can rename attributes. You
93 can now also choose type, default value, whether it's required, B<traits>, etc.
94
95     parameter traits => (
96         isa     => 'ArrayRef[Str]',
97         default => sub { [] },
98     );
99
100     parameter type => (
101         isa     => 'Str',
102         default => 'Any',
103     );
104
105     role {
106         my $p = shift;
107
108         has action => (
109             traits => $p->traits,
110             isa    => $p->type,
111             ...
112         );
113     }
114
115 =item Inform a role of your class' attributes and methods
116
117 Core roles can require only methods with specific names. Now your roles can
118 require that you specify a method name you wish the role to instrument, or
119 which attributes to dump to a file.
120
121     parameter instrument_method => (
122         isa      => 'Str',
123         required => 1,
124     );
125
126     role {
127         my $p = shift;
128         around $p->instrument_method => sub { ... };
129     }
130
131 =item Arbitrary execution choices
132
133 Your role may be able to provide configuration in how the role's methods
134 operate. For example, you can tell the role whether to save intermediate
135 states.
136
137     parameter save_intermediate => (
138         isa     => 'Bool',
139         default => 0,
140     );
141
142     role {
143         my $p = shift;
144         method process => sub {
145             ...
146             if ($p->save_intermediate) { ... }
147             ...
148         };
149     }
150
151 =item Deciding a backend
152
153 Your role may be able to freeze and thaw your instances using L<YAML>, L<JSON>,
154 L<Storable>. Which backend to use can be a parameter.
155
156     parameter format => (
157         isa     => (enum ['Storable', 'YAML', 'JSON']),
158         default => 'Storable',
159     );
160
161     role {
162         my $p = shift;
163         if ($p->format eq 'Storable') {
164             method freeze => \&Storable::freeze;
165             method thaw   => \&Storable::thaw;
166         }
167         elsif ($p->format eq 'YAML') {
168             method freeze => \&YAML::Dump;
169             method thaw   => \&YAML::Load;
170         }
171         ...
172     }
173
174 =back
175
176 =cut
177