Commit | Line | Data |
3b788714 |
1 | package Moose::Cookbook::Extending::ExtensionOverview; |
c8d5f1e1 |
2 | |
daa0fd7d |
3 | # ABSTRACT: Moose extension overview |
4 | |
5 | __END__ |
c8d5f1e1 |
6 | |
c8d5f1e1 |
7 | |
daa0fd7d |
8 | =pod |
c8d5f1e1 |
9 | |
10 | =head1 DESCRIPTION |
11 | |
0558aab4 |
12 | Moose provides several ways in which extensions can hook into Moose |
13 | and change its behavior. Moose also has a lot of behavior that can be |
14 | changed. This recipe will provide an overview of each extension method |
15 | and give you some recommendations on what tools to use. |
c8d5f1e1 |
16 | |
17 | If you haven't yet read the recipes on metaclasses, go read those |
0558aab4 |
18 | first. You can't write Moose extensions without understanding the |
19 | metaclasses, and those recipes also demonstrate some basic extension |
20 | mechanisms, such as metaclass subclasses and traits. |
c8d5f1e1 |
21 | |
22 | =head2 Playing Nice With Others |
23 | |
24 | One of the goals of this overview is to help you build extensions that |
25 | cooperate well with other extensions. This is especially important if |
26 | you plan to release your extension to CPAN. |
27 | |
28 | Moose comes with several modules that exist to help your write |
29 | cooperative extensions. These are L<Moose::Exporter> and |
0558aab4 |
30 | L<Moose::Util::MetaRole>. By using these two modules, you will ensure |
31 | that your extension works with both the Moose core features and any |
32 | other CPAN extension using those modules. |
c8d5f1e1 |
33 | |
34 | =head1 PARTS OF Moose YOU CAN EXTEND |
35 | |
0558aab4 |
36 | The types of things you might want to do in Moose extensions fall into |
37 | a few broad categories. |
c8d5f1e1 |
38 | |
39 | =head2 Metaclass Extensions |
40 | |
41 | One way of extending Moose is by extending one or more Moose |
826230c2 |
42 | metaclasses. For example, in L<Moose::Cookbook::Meta::Table_MetaclassTrait> we saw |
546a18e9 |
43 | a metaclass role that added a C<table> attribute to the |
c8d5f1e1 |
44 | metaclass. If you were writing an ORM, this would be a logical |
45 | extension. |
46 | |
47 | Many of the Moose extensions on CPAN work by providing an attribute |
546a18e9 |
48 | metaclass role. For example, the L<MooseX::Aliases> module |
d50f0281 |
49 | provides an attribute metaclass trait that lets you specify aliases |
50 | to install for methods and attribute accessors. |
c8d5f1e1 |
51 | |
546a18e9 |
52 | A metaclass extension can be packaged as a role/trait or a subclass. If you |
53 | can, we recommend using traits instead of subclasses, since it's much easier |
54 | to combine disparate traits than it is to combine a bunch of subclasses. |
c8d5f1e1 |
55 | |
56 | When your extensions are implemented as roles, you can apply them with |
57 | the L<Moose::Util::MetaRole> module. |
58 | |
0558aab4 |
59 | =head2 Providing Sugar Functions |
c8d5f1e1 |
60 | |
61 | As part of a metaclass extension, you may also want to provide some |
0558aab4 |
62 | sugar functions, just like L<Moose.pm|Moose> does. Moose provides a |
63 | helper module called L<Moose::Exporter> that makes this much |
64 | simpler. We will be use L<Moose::Exporter> in several of the extension |
65 | recipes. |
c8d5f1e1 |
66 | |
67 | =head2 Object Class Extensions |
68 | |
546a18e9 |
69 | Another common Moose extension technique is to change the default object |
70 | class's behavior. As with metaclass extensions, this can be done with a |
71 | role/trait or with a subclass. For example, L<MooseX::StrictConstructor> |
72 | extension applies a trait that makes the constructor reject arguments which |
73 | don't match its attributes. |
c8d5f1e1 |
74 | |
0558aab4 |
75 | Object class extensions often include metaclass extensions as well. In |
c8d5f1e1 |
76 | particular, if you want your object extension to work when a class is |
546a18e9 |
77 | made immutable, you may need to modify the behavior of some or all of the |
0558aab4 |
78 | L<Moose::Meta::Instance>, L<Moose::Meta::Method::Constructor>, and |
79 | L<Moose::Meta::Method::Destructor> objects. |
c8d5f1e1 |
80 | |
81 | The L<Moose::Util::MetaRole> module lets you apply roles to the base |
82 | object class, as well as the meta classes just mentioned. |
83 | |
84 | =head2 Providing a Role |
85 | |
86 | Some extensions come in the form of a role for you to consume. The |
0558aab4 |
87 | L<MooseX::Object::Pluggable> extension is a great example of this. In |
c8d5f1e1 |
88 | fact, despite the C<MooseX> name, it does not actually change anything |
89 | about Moose's behavior. Instead, it is just a role that an object |
90 | which wants to be pluggable can consume. |
91 | |
92 | If you are implementing this sort of extension, you don't need to do |
93 | anything special. You simply create a role and document that it should |
94 | be used via the normal C<with> sugar: |
95 | |
0558aab4 |
96 | package MyApp::User; |
c8d5f1e1 |
97 | |
98 | use Moose; |
99 | |
546a18e9 |
100 | with 'My::Role'; |
101 | |
102 | Don't use "MooseX" in the name for such packages. |
c8d5f1e1 |
103 | |
104 | =head2 New Types |
105 | |
106 | Another common Moose extension is a new type for the Moose type |
107 | system. In this case, you simply create a type in your module. When |
108 | people load your module, the type is created, and they can refer to it |
0558aab4 |
109 | by name after that. The L<MooseX::Types::URI> and |
110 | L<MooseX::Types::DateTime> distributions are two good examples of how |
111 | this works. These both build on top of the L<MooseX::Types> extension. |
c8d5f1e1 |
112 | |
113 | =head1 ROLES VS TRAITS VS SUBCLASSES |
114 | |
713eb831 |
115 | It is important to understand that B<roles and traits are the same thing>. A |
9bc4e3da |
116 | trait is simply a role applied to a instance. The only thing that may |
713eb831 |
117 | distinguish the two is that a trait can be packaged in a way that lets Moose |
118 | resolve a short name to a class name. In other words, with a trait, the caller |
119 | can refer to it by a short name like "Big", and Moose will resolve it to a |
120 | class like C<MooseX::Embiggen::Meta::Attribute::Role::Big>. |
c8d5f1e1 |
121 | |
b1301316 |
122 | See L<Moose::Cookbook::Meta::Labeled_AttributeTrait> and |
826230c2 |
123 | L<Moose::Cookbook::Meta::Table_MetaclassTrait> for examples of traits in |
124 | action. In particular, both of these recipes demonstrate the trait resolution |
c8d5f1e1 |
125 | mechanism. |
126 | |
127 | Implementing an extension as a (set of) metaclass or base object |
128 | role(s) will make your extension more cooperative. It is hard for an |
129 | end-user to effectively combine together multiple metaclass |
0558aab4 |
130 | subclasses, but it is very easy to combine roles. |
c8d5f1e1 |
131 | |
132 | =head1 USING YOUR EXTENSION |
133 | |
134 | There are a number of ways in which an extension can be applied. In |
135 | some cases you can provide multiple ways of consuming your extension. |
136 | |
137 | =head2 Extensions as Metaclass Traits |
138 | |
139 | If your extension is available as a trait, you can ask end users to |
140 | simply specify it in a list of traits. Currently, this only works for |
0558aab4 |
141 | (class) metaclass and attribute metaclass traits: |
c8d5f1e1 |
142 | |
143 | use Moose -traits => [ 'Big', 'Blue' ]; |
144 | |
6a7e3999 |
145 | has 'animal' => ( |
146 | traits => [ 'Big', 'Blue' ], |
147 | ... |
148 | ); |
c8d5f1e1 |
149 | |
150 | If your extension applies to any other metaclass, or the object base |
151 | class, you cannot use the trait mechanism. |
152 | |
153 | The benefit of the trait mechanism is that is very easy to see where a |
154 | trait is applied in the code, and consumers have fine-grained control |
155 | over what the trait applies to. This is especially true for attribute |
156 | traits, where you can apply the trait to just one attribute in a |
157 | class. |
158 | |
c8d5f1e1 |
159 | =head2 Extensions as Metaclass (and Base Object) Roles |
160 | |
161 | Implementing your extensions as metaclass roles makes your extensions |
0558aab4 |
162 | easy to apply, and cooperative with other role-based extensions for |
163 | metaclasses. |
c8d5f1e1 |
164 | |
165 | Just as with a subclass, you will probably want to package your |
166 | extensions for consumption with a single module that uses |
167 | L<Moose::Exporter>. However, in this case, you will use |
168 | L<Moose::Util::MetaRole> to apply all of your roles. The advantage of |
169 | using this module is that I<it preserves any subclassing or roles |
0558aab4 |
170 | already applied to the user's metaclasses>. This means that your |
c8d5f1e1 |
171 | extension is cooperative I<by default>, and consumers of your |
95056a1e |
172 | extension can easily use it with other role-based extensions. Most |
173 | uses of L<Moose::Util::MetaRole> can be handled by L<Moose::Exporter> |
174 | directly; see the L<Moose::Exporter> docs. |
c8d5f1e1 |
175 | |
176 | package MooseX::Embiggen; |
177 | |
c8d5f1e1 |
178 | use Moose::Exporter; |
c8d5f1e1 |
179 | |
180 | use MooseX::Embiggen::Role::Meta::Class; |
181 | use MooseX::Embiggen::Role::Meta::Attribute; |
6a7e3999 |
182 | use MooseX::Embiggen::Role::Meta::Method::Constructor; |
c8d5f1e1 |
183 | use MooseX::Embiggen::Role::Object; |
184 | |
546a18e9 |
185 | Moose::Exporter->setup_import_methods( |
186 | class_metaroles => { |
187 | class => ['MooseX::Embiggen::Role::Meta::Class'], |
188 | attribute => ['MooseX::Embiggen::Role::Meta::Attribute'], |
189 | constructor => |
190 | ['MooseX::Embiggen::Role::Meta::Method::Constructor'], |
191 | }, |
dae259f8 |
192 | base_class_roles => ['MooseX::Embiggen::Role::Object'], |
95056a1e |
193 | ); |
c8d5f1e1 |
194 | |
0558aab4 |
195 | As you can see from this example, you can use L<Moose::Util::MetaRole> |
c8d5f1e1 |
196 | to apply roles to any metaclass, as well as the base object class. If |
197 | some other extension has already applied its own roles, they will be |
198 | preserved when your extension applies its roles, and vice versa. |
199 | |
200 | =head2 Providing Sugar |
201 | |
546a18e9 |
202 | With L<Moose::Exporter>, you can also export your own sugar functions: |
c8d5f1e1 |
203 | |
204 | package MooseX::Embiggen; |
205 | |
c8d5f1e1 |
206 | use Moose::Exporter; |
207 | |
208 | Moose::Exporter->setup_import_methods( |
546a18e9 |
209 | with_meta => ['embiggen'], |
210 | class_metaroles => { |
211 | class => ['MooseX::Embiggen::Role::Meta::Class'], |
212 | }, |
c8d5f1e1 |
213 | ); |
214 | |
c8d5f1e1 |
215 | sub embiggen { |
d5447d26 |
216 | my $meta = shift; |
217 | $meta->embiggen(@_); |
c8d5f1e1 |
218 | } |
219 | |
220 | And then the consumer of your extension can use your C<embiggen> sub: |
221 | |
222 | package Consumer; |
223 | |
b67e38c5 |
224 | use Moose; |
c8d5f1e1 |
225 | use MooseX::Embiggen; |
226 | |
227 | extends 'Thing'; |
228 | |
229 | embiggen ...; |
230 | |
231 | This can be combined with metaclass and base class roles quite easily. |
232 | |
8bb03292 |
233 | =head2 More advanced extensions |
546a18e9 |
234 | |
8bb03292 |
235 | Providing your extension simply as a set of traits that gets applied to the |
236 | appropriate metaobjects is easy, but sometimes not sufficient. For instance, |
237 | sometimes you need to supply not just a base object role, but an actual base |
238 | object class (due to needing to interact with existing systems that only |
239 | provide a base class). To write extensions like this, you will need to provide |
240 | a custom C<init_meta> method in your exporter. For instance: |
546a18e9 |
241 | |
242 | package MooseX::Embiggen; |
243 | |
546a18e9 |
244 | use Moose::Exporter; |
245 | |
8bb03292 |
246 | my ($import, $unimport, $init_meta) = Moose::Exporter->build_import_methods( |
247 | install => ['import', 'unimport'], |
248 | with_meta => ['embiggen'], |
249 | class_metaroles => { |
250 | class => ['MooseX::Embiggen::Role::Meta::Class'], |
251 | }, |
252 | ); |
546a18e9 |
253 | |
8bb03292 |
254 | sub embiggen { |
255 | my $meta = shift; |
256 | $meta->embiggen(@_); |
257 | } |
546a18e9 |
258 | |
259 | sub init_meta { |
8bb03292 |
260 | my $package = shift; |
546a18e9 |
261 | my %options = @_; |
8bb03292 |
262 | if (my $meta = Class::MOP::class_of($options{for_class})) { |
263 | if ($meta->isa('Class::MOP::Class')) { |
264 | my @supers = $meta->superclasses; |
265 | $meta->superclasses('MooseX::Embiggen::Base::Class') |
266 | if @supers == 1 && $supers[0] eq 'Moose::Object'; |
267 | } |
268 | } |
269 | $package->$init_meta(%options); |
546a18e9 |
270 | } |
271 | |
8bb03292 |
272 | In the previous examples, C<init_meta> was generated for you, but here you must |
273 | override it in order to add additional functionality. Some differences to note: |
274 | |
275 | =over 4 |
276 | |
277 | =item C<build_import_methods> instead of C<setup_import_methods> |
278 | |
279 | C<build_import_methods> simply returns the C<import>, C<unimport>, and |
280 | C<init_meta> methods, rather than installing them under the appropriate names. |
281 | This way, you can write your own which wrap the existing functionality. |
282 | C<build_import_methods> also takes an additional C<install> parameter, which |
283 | tells it to just go ahead and install these methods (since we don't need to |
284 | modify them). |
285 | |
286 | =item C<sub init_meta> |
287 | |
288 | Next, we must write our C<init_meta> wrapper. The important things to remember |
289 | are that it is called as a method, and that C<%options> needs to be passed |
290 | through to the existing implementation. Calling the base implementation just |
291 | uses the C<$init_meta> subroutine reference that was returned by |
292 | C<build_import_methods> earlier. |
293 | |
294 | =item Additional implementation |
295 | |
296 | This extension sets a different default base object class. To do so, it first |
297 | checks to see if it's being applied to a class, and then checks to see if |
298 | L<Moose::Object> is that class's only superclass, and if so, replaces that with |
299 | the superclass that this extension requires. |
300 | |
301 | Note that two extensions that do this same thing will not work together |
302 | properly (the second extension to be loaded won't see L<Moose::Object> as the |
303 | base object, since it has already been overridden). This is why using a base |
304 | object role is recommended for the general case. |
305 | |
306 | This C<init_meta> also works defensively, by only applying its functionality if |
307 | a metaclass already exists. This makes sure it doesn't break with legacy |
308 | extensions which override the metaclass directly (and so must be the first |
309 | extension to initialize the metaclass). This is likely not necessary, since |
310 | almost no extensions work this way anymore, but just provides an additional |
311 | level of protection. The common case of C<use Moose; use MooseX::Embiggen;> |
312 | is not affected regardless. |
313 | |
314 | =back |
315 | |
316 | This is just one example of what can be done with a custom C<init_meta> method. |
317 | It can also be used for preventing an extension from being applied to a role, |
318 | doing other kinds of validation on the class being applied to, or pretty much |
319 | anything that would otherwise be done in an C<import> method. |
e32cc906 |
320 | |
0558aab4 |
321 | =head1 LEGACY EXTENSION MECHANISMS |
c8d5f1e1 |
322 | |
323 | Before the existence of L<Moose::Exporter> and |
324 | L<Moose::Util::MetaRole>, there were a number of other ways to extend |
325 | Moose. In general, these methods were less cooperative, and only |
326 | worked well with a single extension. |
327 | |
0558aab4 |
328 | These methods include L<metaclass.pm|metaclass>, L<Moose::Policy> |
329 | (which uses L<metaclass.pm|metaclass> under the hood), and various |
330 | hacks to do what L<Moose::Exporter> does. Please do not use these for |
331 | your own extensions. |
c8d5f1e1 |
332 | |
333 | Note that if you write a cooperative extension, it should cooperate |
334 | with older extensions, though older extensions generally do not |
8745c929 |
335 | cooperate with each other. |
c8d5f1e1 |
336 | |
337 | =head1 CONCLUSION |
338 | |
339 | If you can write your extension as one or more metaclass and base |
340 | object roles, please consider doing so. Make sure to read the docs for |
341 | L<Moose::Exporter> and L<Moose::Util::MetaRole> as well. |
342 | |
c8d5f1e1 |
343 | =cut |