Commit | Line | Data |
c8d5f1e1 |
1 | |
2 | =pod |
3 | |
4 | =head1 NAME |
5 | |
6 | Moose::Cookbook::Extending::Recipe - Moose extension overview |
7 | |
8 | =head1 DESCRIPTION |
9 | |
10 | Moose has quite a number of ways in which extensions can hook into |
11 | Moose and change its behavior. Moose also has a lot of behavior that |
12 | can be changed. This recipe will provide an overview of each extension |
13 | method and give you some recommendations on what tools to use. |
14 | |
15 | If you haven't yet read the recipes on metaclasses, go read those |
16 | first. You can't really write Moose extensions without understanding |
17 | the metaclasses, and those recipes also demonstrate some basic |
18 | extensions mechanisms such as metaclass subclasses and traits. |
19 | |
20 | =head2 Playing Nice With Others |
21 | |
22 | One of the goals of this overview is to help you build extensions that |
23 | cooperate well with other extensions. This is especially important if |
24 | you plan to release your extension to CPAN. |
25 | |
26 | Moose comes with several modules that exist to help your write |
27 | cooperative extensions. These are L<Moose::Exporter> and |
28 | L<Moose::Util::MetaRole>. By using these two modules to implement your |
29 | extensions, you will ensure that your extension works with both the |
30 | Moose core features and any other CPAN extension using those modules. |
31 | |
32 | =head1 PARTS OF Moose YOU CAN EXTEND |
33 | |
34 | The types of things you might want to do in Moose extensions broadly |
35 | fall into a few categories. |
36 | |
37 | =head2 Metaclass Extensions |
38 | |
39 | One way of extending Moose is by extending one or more Moose |
40 | metaclasses. For example, in L<Moose::Cookbook::Meta::Recipe4> we saw |
41 | a metaclass subclass that added a C<table> attribute to the |
42 | metaclass. If you were writing an ORM, this would be a logical |
43 | extension. |
44 | |
45 | Many of the Moose extensions on CPAN work by providing an attribute |
46 | metaclass extension. For example, the C<MooseX::AttributeHelpers> |
47 | distro provides a new attribute metaclass that lets you delegate |
48 | behavior to a non-object attribute (a hashref or simple number). |
49 | |
50 | A metaclass extension can be packaged as a subclass or a |
51 | role/trait. If you can, we recommend using traits instead of |
52 | subclasses, since it's generally much easier to combine disparate |
53 | traits then it is to combine a bunch of subclasses. |
54 | |
55 | When your extensions are implemented as roles, you can apply them with |
56 | the L<Moose::Util::MetaRole> module. |
57 | |
58 | =head2 Providing Sugar Subs |
59 | |
60 | As part of a metaclass extension, you may also want to provide some |
61 | sugar subroutines, much like C<Moose.pm> does. Moose provides a helper |
62 | module called L<Moose::Exporter> that makes this much simpler. This |
63 | will be used in several of the extension recipes. |
64 | |
65 | =head2 Object Class Extensions |
66 | |
67 | Another common Moose extension is to change the default object class |
68 | behavior. For example, the C<MooseX::Singleton> extension changes the |
69 | behavior of your objects so that they are singletons. The |
70 | C<MooseX::StrictConstructor> extension makes the constructor reject |
71 | arguments which don't match its attributes. |
72 | |
73 | Object class extensions often also include metaclass extensions. In |
74 | particular, if you want your object extension to work when a class is |
75 | made immutable, you may need to extend some or all of the |
76 | C<Moose::Meta::Instance>, C<Moose::Meta::Method::Constructor>, and |
77 | C<Moose::Meta::Method::Destructor> objects. |
78 | |
79 | The L<Moose::Util::MetaRole> module lets you apply roles to the base |
80 | object class, as well as the meta classes just mentioned. |
81 | |
82 | =head2 Providing a Role |
83 | |
84 | Some extensions come in the form of a role for you to consume. The |
85 | C<MooseX::Object::Pluggable> extension is a great example of this. In |
86 | fact, despite the C<MooseX> name, it does not actually change anything |
87 | about Moose's behavior. Instead, it is just a role that an object |
88 | which wants to be pluggable can consume. |
89 | |
90 | If you are implementing this sort of extension, you don't need to do |
91 | anything special. You simply create a role and document that it should |
92 | be used via the normal C<with> sugar: |
93 | |
94 | package RoleConsumer; |
95 | |
96 | use Moose; |
97 | |
98 | with 'MooseX::My::Role'; |
99 | |
100 | =head2 New Types |
101 | |
102 | Another common Moose extension is a new type for the Moose type |
103 | system. In this case, you simply create a type in your module. When |
104 | people load your module, the type is created, and they can refer to it |
105 | by name after that. The C<MooseX::Types::URI> and |
106 | C<MooseX::Types::DateTime> distros are two good examples of how this |
107 | works. |
108 | |
109 | =head1 ROLES VS TRAITS VS SUBCLASSES |
110 | |
111 | It is important to understand that B<roles and traits are the same |
112 | thing>. A role can be used as a trait, and a trait is a role. The only |
113 | thing that distinguishes the two is that a trait is packaged in a way |
114 | that lets Moose resolve a short name to a class name. In other words, |
115 | with a trait, the caller can specify it by a short name like "Big", |
116 | and Moose will resolve it to a class like |
117 | C<MooseX::Embiggen::Meta::Attribute::Role::Big>. |
118 | |
119 | See L<Moose::Cookbook::Meta::Recipe3> and |
120 | L<Moose::Cookbook::Meta::Recipe5> for examples of traits in action. In |
121 | particular, both of these recipes demonstrate the trait resolution |
122 | mechanism. |
123 | |
124 | Implementing an extension as a (set of) metaclass or base object |
125 | role(s) will make your extension more cooperative. It is hard for an |
126 | end-user to effectively combine together multiple metaclass |
127 | subclasses, but it can be very easy to combine roles. |
128 | |
129 | =head1 USING YOUR EXTENSION |
130 | |
131 | There are a number of ways in which an extension can be applied. In |
132 | some cases you can provide multiple ways of consuming your extension. |
133 | |
134 | =head2 Extensions as Metaclass Traits |
135 | |
136 | If your extension is available as a trait, you can ask end users to |
137 | simply specify it in a list of traits. Currently, this only works for |
138 | metaclass and attribute metaclass traits: |
139 | |
140 | use Moose -traits => [ 'Big', 'Blue' ]; |
141 | |
142 | has 'animal' => |
143 | ( traits => [ 'Big', 'Blue' ], |
144 | ... |
145 | ); |
146 | |
147 | If your extension applies to any other metaclass, or the object base |
148 | class, you cannot use the trait mechanism. |
149 | |
150 | The benefit of the trait mechanism is that is very easy to see where a |
151 | trait is applied in the code, and consumers have fine-grained control |
152 | over what the trait applies to. This is especially true for attribute |
153 | traits, where you can apply the trait to just one attribute in a |
154 | class. |
155 | |
156 | =head2 Extensions as Metaclass (and Base Object) Subclasses |
157 | |
158 | Moose does not provide any simple APIs for consumers to use a subclass |
159 | extension, excep for attribute metaclasses. The attribute declaration |
160 | parameters include a C<metaclass> parameter a consumer of your |
161 | extension can use to specify your subclass. |
162 | |
163 | This is one reason why implementing an extension as a subclass can be |
164 | a poor choice. However, you can force the use of certain subclasses at |
165 | import time by calling C<< Moose->init_meta >> for the caller, and |
166 | providing an alternate metaclass or base object class. |
167 | |
168 | If you do want to do this, you should look at using C<Moose::Exporter> |
169 | to re-export the C<Moose.pm> sugar subroutines. When you use |
170 | L<Moose::Exporter> and your exporting class has an C<init_meta> |
171 | method, L<Moose::Exporter> makes sure that this C<init_meta> method |
172 | gets called when your class is imported. |
173 | |
174 | Then in your C<init_meta> you can arrange for the caller to use your |
175 | subclasses: |
176 | |
177 | package MooseX::Embiggen; |
178 | |
179 | use Moose (); |
180 | use Moose::Exporter; |
181 | |
182 | use MooseX::Embiggen::Meta::Class; |
183 | use MooseX::Embiggen::Object; |
184 | |
185 | Moose::Exporter->setup_import_methods( also => 'Moose' ); |
186 | |
187 | sub init_meta { |
188 | shift; # just your package name |
189 | my %options = @_; |
190 | |
191 | return Moose->init_meta( |
192 | for_class => $options{for_class}, |
193 | metaclass => 'MooseX::Embiggen::Meta::Class', |
194 | base_class => 'MooseX::Embiggen::Object', |
195 | ); |
196 | } |
197 | |
198 | =head2 Extensions as Metaclass (and Base Object) Roles |
199 | |
200 | Implementing your extensions as metaclass roles makes your extensions |
201 | easy to apply, and cooperative with other metaclass role-based extensions. |
202 | |
203 | Just as with a subclass, you will probably want to package your |
204 | extensions for consumption with a single module that uses |
205 | L<Moose::Exporter>. However, in this case, you will use |
206 | L<Moose::Util::MetaRole> to apply all of your roles. The advantage of |
207 | using this module is that I<it preserves any subclassing or roles |
208 | already applied to the users metaclasses>. This means that your |
209 | extension is cooperative I<by default>, and consumers of your |
210 | extension can easily use it with other role-based extensions. |
211 | |
212 | package MooseX::Embiggen; |
213 | |
214 | use Moose (); |
215 | use Moose::Exporter; |
216 | use Moose::Util::MetaRole; |
217 | |
218 | use MooseX::Embiggen::Role::Meta::Class; |
219 | use MooseX::Embiggen::Role::Meta::Attribute; |
220 | use MooseX::Embiggen::Role::Meta::Method::Constructor |
221 | use MooseX::Embiggen::Role::Object; |
222 | |
223 | Moose::Exporter->setup_import_methods( also => 'Moose' ); |
224 | |
225 | sub init_meta { |
226 | shift; # just your package name |
227 | my %options = @_; |
228 | |
229 | Moose->init_meta(%options); |
230 | |
231 | my $meta = Moose::Util::MetaRole::apply_metaclass_roles( |
232 | for_class => $options{for_class}, |
233 | metaclass_roles => ['MooseX::Embiggen::Role::Meta::Class'], |
234 | attribute_metaclass_roles => |
235 | ['MooseX::Embiggen::Role::Meta::Attribute'], |
236 | constructor_class_roles => |
237 | ['MooseX::Embiggen::Role::Meta::Method::Constructor'], |
238 | ); |
239 | |
240 | Moose::Util::MetaRole::apply_base_class_roles( |
241 | for_class => $options{for_class}, |
242 | roles => ['MooseX::Embiggen::Role::Object'], |
243 | ); |
244 | |
245 | return $meta; |
246 | } |
247 | |
248 | As you can see from this example, you can use C<Moose::Util::MetaRole> |
249 | to apply roles to any metaclass, as well as the base object class. If |
250 | some other extension has already applied its own roles, they will be |
251 | preserved when your extension applies its roles, and vice versa. |
252 | |
253 | =head2 Providing Sugar |
254 | |
255 | With L<Moose::Exporter>, you can also export your own sugar subs, as |
256 | well as those from other sugar modules: |
257 | |
258 | package MooseX::Embiggen; |
259 | |
260 | use Moose (); |
261 | use Moose::Exporter; |
262 | |
263 | Moose::Exporter->setup_import_methods( |
264 | with_caller => ['embiggen'], |
265 | also => 'Moose', |
266 | ); |
267 | |
268 | sub init_meta { ... } |
269 | |
270 | sub embiggen { |
271 | my $caller = shift; |
272 | $caller->meta()->embiggen(@_); |
273 | } |
274 | |
275 | And then the consumer of your extension can use your C<embiggen> sub: |
276 | |
277 | package Consumer; |
278 | |
279 | use MooseX::Embiggen; |
280 | |
281 | extends 'Thing'; |
282 | |
283 | embiggen ...; |
284 | |
285 | This can be combined with metaclass and base class roles quite easily. |
286 | |
287 | =head1 LEGACY EXTENSION METHODOLOGIES |
288 | |
289 | Before the existence of L<Moose::Exporter> and |
290 | L<Moose::Util::MetaRole>, there were a number of other ways to extend |
291 | Moose. In general, these methods were less cooperative, and only |
292 | worked well with a single extension. |
293 | |
294 | These methods include C<metaclass.pm>, C<Moose::Policy> (which uses |
295 | C<metaclass.pm> under the hood), and various hacks to do what |
296 | L<Moose::Exporter> does. Please do not use these for your own |
297 | extensions. |
298 | |
299 | Note that if you write a cooperative extension, it should cooperate |
300 | with older extensions, though older extensions generally do not |
301 | cooperate with each oether. |
302 | |
303 | =head1 CONCLUSION |
304 | |
305 | If you can write your extension as one or more metaclass and base |
306 | object roles, please consider doing so. Make sure to read the docs for |
307 | L<Moose::Exporter> and L<Moose::Util::MetaRole> as well. |
308 | |
309 | =head1 AUTHOR |
310 | |
311 | Dave Rolsky E<lt>autarch@urth.orgE<gt> |
312 | |
313 | =head1 COPYRIGHT AND LICENSE |
314 | |
315 | Copyright 2006-2008 by Infinity Interactive, Inc. |
316 | |
317 | L<http://www.iinteractive.com> |
318 | |
319 | This library is free software; you can redistribute it and/or modify |
320 | it under the same terms as Perl itself. |
321 | |
322 | =cut |