Commit | Line | Data |
daa0fd7d |
1 | package Moose::Cookbook::Extending::Recipe1; |
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 |
42 | metaclasses. For example, in L<Moose::Cookbook::Meta::Recipe4> we saw |
43 | a metaclass subclass that added a C<table> attribute to the |
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 |
0558aab4 |
48 | metaclass extension. For example, the L<MooseX::AttributeHelpers> |
6549b0d1 |
49 | distribution provides a new attribute metaclass that lets you delegate |
c8d5f1e1 |
50 | behavior to a non-object attribute (a hashref or simple number). |
48af8283 |
51 | (MooseX::AttributeHelpers has been deprecated in favour of |
52 | L<Moose::Meta::Attribute::Native>, but can still serve as an example). |
c8d5f1e1 |
53 | |
54 | A metaclass extension can be packaged as a subclass or a |
55 | role/trait. If you can, we recommend using traits instead of |
38f0beac |
56 | subclasses, since it's much easier to combine disparate traits than it |
0558aab4 |
57 | is to combine a bunch of subclasses. |
c8d5f1e1 |
58 | |
59 | When your extensions are implemented as roles, you can apply them with |
60 | the L<Moose::Util::MetaRole> module. |
61 | |
0558aab4 |
62 | =head2 Providing Sugar Functions |
c8d5f1e1 |
63 | |
64 | As part of a metaclass extension, you may also want to provide some |
0558aab4 |
65 | sugar functions, just like L<Moose.pm|Moose> does. Moose provides a |
66 | helper module called L<Moose::Exporter> that makes this much |
67 | simpler. We will be use L<Moose::Exporter> in several of the extension |
68 | recipes. |
c8d5f1e1 |
69 | |
70 | =head2 Object Class Extensions |
71 | |
0558aab4 |
72 | Another common Moose extension technique is to change the default |
73 | object class's behavior. For example, the L<MooseX::Singleton> |
74 | extension changes the behavior of your objects so that they are |
75 | singletons. The L<MooseX::StrictConstructor> extension makes the |
76 | constructor reject arguments which don't match its attributes. |
c8d5f1e1 |
77 | |
0558aab4 |
78 | Object class extensions often include metaclass extensions as well. In |
c8d5f1e1 |
79 | particular, if you want your object extension to work when a class is |
80 | made immutable, you may need to extend some or all of the |
0558aab4 |
81 | L<Moose::Meta::Instance>, L<Moose::Meta::Method::Constructor>, and |
82 | L<Moose::Meta::Method::Destructor> objects. |
c8d5f1e1 |
83 | |
84 | The L<Moose::Util::MetaRole> module lets you apply roles to the base |
85 | object class, as well as the meta classes just mentioned. |
86 | |
87 | =head2 Providing a Role |
88 | |
89 | Some extensions come in the form of a role for you to consume. The |
0558aab4 |
90 | L<MooseX::Object::Pluggable> extension is a great example of this. In |
c8d5f1e1 |
91 | fact, despite the C<MooseX> name, it does not actually change anything |
92 | about Moose's behavior. Instead, it is just a role that an object |
93 | which wants to be pluggable can consume. |
94 | |
95 | If you are implementing this sort of extension, you don't need to do |
96 | anything special. You simply create a role and document that it should |
97 | be used via the normal C<with> sugar: |
98 | |
0558aab4 |
99 | package MyApp::User; |
c8d5f1e1 |
100 | |
101 | use Moose; |
102 | |
103 | with 'MooseX::My::Role'; |
104 | |
105 | =head2 New Types |
106 | |
107 | Another common Moose extension is a new type for the Moose type |
108 | system. In this case, you simply create a type in your module. When |
109 | people load your module, the type is created, and they can refer to it |
0558aab4 |
110 | by name after that. The L<MooseX::Types::URI> and |
111 | L<MooseX::Types::DateTime> distributions are two good examples of how |
112 | this works. These both build on top of the L<MooseX::Types> extension. |
c8d5f1e1 |
113 | |
114 | =head1 ROLES VS TRAITS VS SUBCLASSES |
115 | |
713eb831 |
116 | It is important to understand that B<roles and traits are the same thing>. A |
9bc4e3da |
117 | trait is simply a role applied to a instance. The only thing that may |
713eb831 |
118 | distinguish the two is that a trait can be packaged in a way that lets Moose |
119 | resolve a short name to a class name. In other words, with a trait, the caller |
120 | can refer to it by a short name like "Big", and Moose will resolve it to a |
121 | class like C<MooseX::Embiggen::Meta::Attribute::Role::Big>. |
c8d5f1e1 |
122 | |
123 | See L<Moose::Cookbook::Meta::Recipe3> and |
124 | L<Moose::Cookbook::Meta::Recipe5> for examples of traits in action. In |
125 | particular, both of these recipes demonstrate the trait resolution |
126 | mechanism. |
127 | |
128 | Implementing an extension as a (set of) metaclass or base object |
129 | role(s) will make your extension more cooperative. It is hard for an |
130 | end-user to effectively combine together multiple metaclass |
0558aab4 |
131 | subclasses, but it is very easy to combine roles. |
c8d5f1e1 |
132 | |
133 | =head1 USING YOUR EXTENSION |
134 | |
135 | There are a number of ways in which an extension can be applied. In |
136 | some cases you can provide multiple ways of consuming your extension. |
137 | |
138 | =head2 Extensions as Metaclass Traits |
139 | |
140 | If your extension is available as a trait, you can ask end users to |
141 | simply specify it in a list of traits. Currently, this only works for |
0558aab4 |
142 | (class) metaclass and attribute metaclass traits: |
c8d5f1e1 |
143 | |
144 | use Moose -traits => [ 'Big', 'Blue' ]; |
145 | |
6a7e3999 |
146 | has 'animal' => ( |
147 | traits => [ 'Big', 'Blue' ], |
148 | ... |
149 | ); |
c8d5f1e1 |
150 | |
151 | If your extension applies to any other metaclass, or the object base |
152 | class, you cannot use the trait mechanism. |
153 | |
154 | The benefit of the trait mechanism is that is very easy to see where a |
155 | trait is applied in the code, and consumers have fine-grained control |
156 | over what the trait applies to. This is especially true for attribute |
157 | traits, where you can apply the trait to just one attribute in a |
158 | class. |
159 | |
160 | =head2 Extensions as Metaclass (and Base Object) Subclasses |
161 | |
162 | Moose does not provide any simple APIs for consumers to use a subclass |
4223567e |
163 | extension, except for attribute metaclasses. The attribute declaration |
16fb3624 |
164 | options include a C<metaclass> option a consumer of your extension can |
165 | use to specify your subclass. |
c8d5f1e1 |
166 | |
167 | This is one reason why implementing an extension as a subclass can be |
168 | a poor choice. However, you can force the use of certain subclasses at |
169 | import time by calling C<< Moose->init_meta >> for the caller, and |
170 | providing an alternate metaclass or base object class. |
171 | |
0558aab4 |
172 | If you do want to do this, you should look at using L<Moose::Exporter> |
173 | to re-export the L<Moose.pm|Moose> sugar function. With |
174 | L<Moose::Exporter>, if your exporting class has an C<init_meta> |
c8d5f1e1 |
175 | method, L<Moose::Exporter> makes sure that this C<init_meta> method |
176 | gets called when your class is imported. |
177 | |
178 | Then in your C<init_meta> you can arrange for the caller to use your |
179 | subclasses: |
180 | |
181 | package MooseX::Embiggen; |
182 | |
183 | use Moose (); |
184 | use Moose::Exporter; |
185 | |
186 | use MooseX::Embiggen::Meta::Class; |
187 | use MooseX::Embiggen::Object; |
188 | |
189 | Moose::Exporter->setup_import_methods( also => 'Moose' ); |
190 | |
191 | sub init_meta { |
6a7e3999 |
192 | shift; # just your package name |
c8d5f1e1 |
193 | my %options = @_; |
194 | |
195 | return Moose->init_meta( |
196 | for_class => $options{for_class}, |
197 | metaclass => 'MooseX::Embiggen::Meta::Class', |
198 | base_class => 'MooseX::Embiggen::Object', |
199 | ); |
200 | } |
201 | |
a8de959b |
202 | NOTE: Make sure that your C<init_meta> returns the metaclass object, just as |
203 | C<< Moose->init_meta >> does. |
204 | |
c8d5f1e1 |
205 | =head2 Extensions as Metaclass (and Base Object) Roles |
206 | |
207 | Implementing your extensions as metaclass roles makes your extensions |
0558aab4 |
208 | easy to apply, and cooperative with other role-based extensions for |
209 | metaclasses. |
c8d5f1e1 |
210 | |
211 | Just as with a subclass, you will probably want to package your |
212 | extensions for consumption with a single module that uses |
213 | L<Moose::Exporter>. However, in this case, you will use |
214 | L<Moose::Util::MetaRole> to apply all of your roles. The advantage of |
215 | using this module is that I<it preserves any subclassing or roles |
0558aab4 |
216 | already applied to the user's metaclasses>. This means that your |
c8d5f1e1 |
217 | extension is cooperative I<by default>, and consumers of your |
95056a1e |
218 | extension can easily use it with other role-based extensions. Most |
219 | uses of L<Moose::Util::MetaRole> can be handled by L<Moose::Exporter> |
220 | directly; see the L<Moose::Exporter> docs. |
c8d5f1e1 |
221 | |
222 | package MooseX::Embiggen; |
223 | |
224 | use Moose (); |
225 | use Moose::Exporter; |
c8d5f1e1 |
226 | |
227 | use MooseX::Embiggen::Role::Meta::Class; |
228 | use MooseX::Embiggen::Role::Meta::Attribute; |
6a7e3999 |
229 | use MooseX::Embiggen::Role::Meta::Method::Constructor; |
c8d5f1e1 |
230 | use MooseX::Embiggen::Role::Object; |
231 | |
dae259f8 |
232 | my ( $import, $unimport, $init_meta ) = Moose::Exporter->build_import_methods( |
e298a0f0 |
233 | also => ['Moose'], metaclass_roles => |
dae259f8 |
234 | ['MooseX::Embiggen::Role::Meta::Class'], |
95056a1e |
235 | attribute_metaclass_roles => ['MooseX::Embiggen::Role::Meta::Attribute'], |
dae259f8 |
236 | constructor_class_roles => |
95056a1e |
237 | ['MooseX::Embiggen::Role::Meta::Method::Constructor'], |
dae259f8 |
238 | base_class_roles => ['MooseX::Embiggen::Role::Object'], |
239 | install => [qw(import unimport)], |
95056a1e |
240 | ); |
c8d5f1e1 |
241 | |
242 | sub init_meta { |
95056a1e |
243 | my $package = shift; |
c8d5f1e1 |
244 | my %options = @_; |
c8d5f1e1 |
245 | Moose->init_meta(%options); |
95056a1e |
246 | return $package->$init_meta(%options); |
c8d5f1e1 |
247 | } |
248 | |
0558aab4 |
249 | As you can see from this example, you can use L<Moose::Util::MetaRole> |
c8d5f1e1 |
250 | to apply roles to any metaclass, as well as the base object class. If |
251 | some other extension has already applied its own roles, they will be |
252 | preserved when your extension applies its roles, and vice versa. |
253 | |
254 | =head2 Providing Sugar |
255 | |
0558aab4 |
256 | With L<Moose::Exporter>, you can also export your own sugar functions, |
257 | as well as those from other modules: |
c8d5f1e1 |
258 | |
259 | package MooseX::Embiggen; |
260 | |
261 | use Moose (); |
262 | use Moose::Exporter; |
263 | |
264 | Moose::Exporter->setup_import_methods( |
d5447d26 |
265 | with_meta => ['embiggen'], |
266 | also => 'Moose', |
c8d5f1e1 |
267 | ); |
268 | |
c8d5f1e1 |
269 | sub embiggen { |
d5447d26 |
270 | my $meta = shift; |
271 | $meta->embiggen(@_); |
c8d5f1e1 |
272 | } |
273 | |
274 | And then the consumer of your extension can use your C<embiggen> sub: |
275 | |
276 | package Consumer; |
277 | |
278 | use MooseX::Embiggen; |
279 | |
280 | extends 'Thing'; |
281 | |
282 | embiggen ...; |
283 | |
284 | This can be combined with metaclass and base class roles quite easily. |
285 | |
0558aab4 |
286 | =head1 LEGACY EXTENSION MECHANISMS |
c8d5f1e1 |
287 | |
288 | Before the existence of L<Moose::Exporter> and |
289 | L<Moose::Util::MetaRole>, there were a number of other ways to extend |
290 | Moose. In general, these methods were less cooperative, and only |
291 | worked well with a single extension. |
292 | |
0558aab4 |
293 | These methods include L<metaclass.pm|metaclass>, L<Moose::Policy> |
294 | (which uses L<metaclass.pm|metaclass> under the hood), and various |
295 | hacks to do what L<Moose::Exporter> does. Please do not use these for |
296 | your own extensions. |
c8d5f1e1 |
297 | |
298 | Note that if you write a cooperative extension, it should cooperate |
299 | with older extensions, though older extensions generally do not |
8745c929 |
300 | cooperate with each other. |
c8d5f1e1 |
301 | |
302 | =head1 CONCLUSION |
303 | |
304 | If you can write your extension as one or more metaclass and base |
305 | object roles, please consider doing so. Make sure to read the docs for |
306 | L<Moose::Exporter> and L<Moose::Util::MetaRole> as well. |
307 | |
c8d5f1e1 |
308 | =cut |