Commit | Line | Data |
38017482 |
1 | =head1 NAME |
2 | |
3 | Catalyst::Manual::ExtendingCatalyst - Extending The Framework |
4 | |
5 | =head1 DESCRIPTION |
6 | |
7 | This document will provide you with access points, techniques and best |
b7c570ac |
8 | practices to extend the L<Catalyst> framework, or to find more elegant |
9 | ways to abstract and use your own code. |
38017482 |
10 | |
b7c570ac |
11 | The design of Catalyst is such that the framework itself should not |
12 | get in your way. There are many entry points to alter or extend |
13 | Catalyst's behaviour, and this can be confusing. This document is |
14 | written to help you understand the possibilities, current practices |
15 | and their consequences. |
38017482 |
16 | |
080bb620 |
17 | Please read the L</BEST PRACTICES> section before deciding on a design, |
b7c570ac |
18 | especially if you plan to release your code to CPAN. The Catalyst |
19 | developer and user communities, which B<you are part of>, will benefit |
20 | most if we all work together and coordinate. |
21 | |
22 | If you are unsure on an implementation or have an idea you would like |
23 | to have RFC'ed, it surely is a good idea to send your questions and |
24 | suggestions to the Catalyst mailing list (See L<Catalyst/SUPPORT>) |
25 | and/or come to the C<#catalyst> channel on the C<irc.perl.org> |
26 | network. You might also want to refer to those places for research to |
27 | see if a module doing what you're trying to implement already |
28 | exists. This might give you a solution to your problem or a basis for |
29 | starting. |
38017482 |
30 | |
31 | =head1 BEST PRACTICES |
32 | |
b7c570ac |
33 | During Catalyst's early days, it was common to write plugins to |
34 | provide functionality application wide. Since then, Catalyst has |
35 | become a lot more flexible and powerful. It soon became a best |
36 | practice to use some other form of abstraction or interface, to keep |
37 | the scope of its influence as close as possible to where it belongs. |
38017482 |
38 | |
b7c570ac |
39 | For those in a hurry, here's a quick checklist of some fundamental |
40 | points. If you are going to read the whole thing anyway, you can jump |
38017482 |
41 | forward to L</Namespaces>. |
42 | |
43 | =head2 Quick Checklist |
44 | |
45 | =over |
46 | |
47 | =item Use the C<CatalystX::*> namespace if you can! |
48 | |
78170776 |
49 | If your extension isn't a Model, View, Controller, Plugin, Engine, |
50 | or Log, it's best to leave it out of the C<Catalyst::> namespace. |
51 | Use <CatalystX::> instead. |
38017482 |
52 | |
53 | =item Don't make it a plugin unless you have to! |
54 | |
1972ebdd |
55 | A plugin should be careful since it's overriding Catalyst internals. |
56 | If your plugin doesn't really need to muck with the internals, make it a |
57 | base Controller or Model. |
38017482 |
58 | |
fa025310 |
59 | Also, if you think you really need a plugin, please instead consider |
60 | using a L<Moose::Role>. |
78170776 |
61 | |
38017482 |
62 | =item There's a community. Use it! |
63 | |
b7c570ac |
64 | There are many experienced developers in the Catalyst community, |
65 | there's always the IRC channel and the mailing list to discuss things. |
38017482 |
66 | |
67 | =item Add tests and documentation! |
68 | |
b7c570ac |
69 | This gives a stable basis for contribution, and even more importantly, |
70 | builds trust. The easiest way is a test application. See |
56a12748 |
71 | L<Catalyst::Manual::Tutorial::Testing|Catalyst::Manual::Tutorial::08_Testing> |
72 | for more information. |
38017482 |
73 | |
74 | =back |
75 | |
76 | =head2 Namespaces |
77 | |
b7c570ac |
78 | While some core extensions (engines, plugins, etc.) have to be placed |
79 | in the C<Catalyst::*> namespace, the Catalyst core would like to ask |
38017482 |
80 | developers to use the C<CatalystX::*> namespace if possible. |
81 | |
fa025310 |
82 | Please B<do not> invent components which are outside the well |
83 | known C<Model>, C<View>, C<Controller> or C<Plugin> namespaces! |
84 | |
b7c570ac |
85 | When you try to put a base class for a C<Model>, C<View> or |
86 | C<Controller> directly under your C<MyApp> directory as, for example, |
87 | C<MyApp::Controller::Foo>, you will have the problem that Catalyst |
88 | will try to load that base class as a component of your |
89 | application. The solution is simple: Use another namespace. Common |
90 | ones are C<MyApp::Base::Controller::*> or C<MyApp::ControllerBase::*> |
91 | as examples. |
38017482 |
92 | |
93 | =head2 Can it be a simple module? |
94 | |
b7c570ac |
95 | Sometimes you want to use functionality in your application that |
96 | doesn't require the framework at all. Remember that Catalyst is just |
97 | Perl and you always can just C<use> a module. If you have application |
98 | specific code that doesn't need the framework, there is no problem in |
99 | putting it in your C<MyApp::*> namespace. Just don't put it in |
100 | C<Model>, C<Controller> or C<View>, because that would make Catalyst |
101 | try to load them as components. |
38017482 |
102 | |
1972ebdd |
103 | Writing a generic component that only works with Catalyst is wasteful |
104 | of your time. Try writing a plain perl module, and then a small bit |
105 | of glue that integrates it with Catalyst. See |
388f66e0 |
106 | L<Catalyst::Model::DBIC::Schema> for a |
1972ebdd |
107 | module that takes the approach. The advantage here is that your |
108 | "Catalyst" DBIC schema works perfectly outside of Catalyst, making |
109 | testing (and command-line scripts) a breeze. The actual Catalyst |
110 | Model is just a few lines of glue that makes working with the schema |
111 | convenient. |
112 | |
7d36d4ac |
113 | If you want the thinnest interface possible, take a look at |
388f66e0 |
114 | L<Catalyst::Model::Adaptor>. |
7d36d4ac |
115 | |
78170776 |
116 | =head2 Using Moose roles to apply method modifiers |
117 | |
118 | Rather than having a complex set of base classes which you have to mixin |
400fa4c3 |
119 | via multiple inheritance, if your functionality is well structured, then |
78170776 |
120 | it's possible to use the composability of L<Moose> roles, and method modifiers |
fa025310 |
121 | to hook onto to provide functionality. |
78170776 |
122 | |
23cf3a36 |
123 | These can be applied to your models/views/controllers, and your application |
4d719c7e |
124 | class, and shipped to CPAN. |
125 | Please see L<Catalyst::Manual::CatalystAndMoose> for specific information |
126 | about using Roles in combination with Catalyst, and L<Moose::Manual::Roles> |
127 | for more information about roles in general. |
78170776 |
128 | |
38017482 |
129 | =head2 Inheritance and overriding methods |
130 | |
433f1ad4 |
131 | When overriding a method, keep in mind that some day additional |
38017482 |
132 | arguments may be provided to the method, if the last parameter is not |
133 | a flat list. It is thus better to override a method by shifting the |
134 | invocant off of C<@_> and assign the rest of the used arguments, so |
135 | you can pass your complete arguments to the original method via C<@_>: |
136 | |
20a4dd98 |
137 | use MRO::Compat; ... |
38017482 |
138 | |
fa025310 |
139 | sub foo { |
140 | my $self = shift; |
141 | my ($bar, $baz) = @_; # ... return |
142 | $self->next::method(@_); |
143 | } |
38017482 |
144 | |
145 | If you would do the common |
146 | |
147 | my ($self, $foo, $bar) = @_; |
148 | |
149 | you'd have to use a much uglier construct to ensure that all arguments |
150 | will be passed along and the method is future proof: |
151 | |
152 | $self->next::method(@_[ 1 .. $#_ ]); |
153 | |
154 | =head2 Tests and documentation |
155 | |
b7c570ac |
156 | When you release your module to the CPAN, proper documentation and at |
157 | least a basic test suite (which means more than pod or even just |
158 | C<use_ok>, sorry) gives people a good base to contribute to the |
159 | module. It also shows that you care for your users. If you would like |
160 | your module to become a recommended addition, these things will prove |
38017482 |
161 | invaluable. |
162 | |
1972ebdd |
163 | If you're just getting started, try using |
388f66e0 |
164 | L<CatalystX::Starter> to generate some example |
1972ebdd |
165 | tests for your module. |
166 | |
38017482 |
167 | =head2 Maintenance |
168 | |
b7c570ac |
169 | In planning to release a module to the community (Catalyst or CPAN and |
170 | Perl), you should consider if you have the resources to keep it up to |
171 | date, including fixing bugs and accepting contributions. |
38017482 |
172 | |
b7c570ac |
173 | If you're not sure about this, you can always ask in the proper |
174 | Catalyst or Perl channels if someone else might be interested in the |
175 | project, and would jump in as co-maintainer. |
38017482 |
176 | |
b7c570ac |
177 | A public repository can further ease interaction with the |
178 | community. Even read only access enables people to provide you with |
179 | patches to your current development version. subversion, SVN and SVK, |
180 | are broadly preferred in the Catalyst community. |
38017482 |
181 | |
b7c570ac |
182 | If you're developing a Catalyst extension, please consider asking the |
183 | core team for space in Catalyst's own subversion repository. You can |
184 | get in touch about this via IRC or the Catalyst developers mailing |
185 | list. |
38017482 |
186 | |
187 | =head2 The context object |
188 | |
189 | Sometimes you want to get a hold of the context object in a component |
b7c570ac |
190 | that was created on startup time, where no context existed yet. Often |
38017482 |
191 | this is about the model reading something out of the stash or other |
b7c570ac |
192 | context information (current language, for example). |
38017482 |
193 | |
b7c570ac |
194 | If you use the context object in your component you have tied it to an |
195 | existing request. This means that you might get into problems when |
196 | you try to use the component (e.g. the model - the most common case) |
197 | outside of Catalyst, for example in cronjobs. |
38017482 |
198 | |
b7c570ac |
199 | A stable solution to this problem is to design the Catalyst model |
200 | separately from the underlying model logic. Let's take |
201 | L<Catalyst::Model::DBIC::Schema> as an example. You can create a |
38017482 |
202 | schema outside of Catalyst that knows nothing about the web. This kind |
203 | of design ensures encapsulation and makes development and maintenance |
204 | a whole lot easier. The you use the aforementioned model to tie your |
b7c570ac |
205 | schema to your application. This gives you a C<MyApp::DBIC> (the name |
206 | is of course just an example) model as well as |
207 | C<MyApp::DBIC::TableName> models to access your result sources |
208 | directly. |
209 | |
210 | By creating such a thin layer between the actual model and the |
211 | Catalyst application, the schema itself is not at all tied to any |
212 | application and the layer in-between can access the model's API using |
213 | information from the context object. |
214 | |
215 | A Catalyst component accesses the context object at request time with |
38017482 |
216 | L<Catalyst::Component/"ACCEPT_CONTEXT($c, @args)">. |
217 | |
218 | =head1 CONFIGURATION |
219 | |
b7c570ac |
220 | The application has to interact with the extension with some |
221 | configuration. There is of course again more than one way to do it. |
38017482 |
222 | |
223 | =head2 Attributes |
224 | |
b7c570ac |
225 | You can specify any valid Perl attribute on Catalyst actions you like. |
226 | (See L<attributes/"Syntax of Attribute Lists"> for a description of |
56a12748 |
227 | what is valid.) These will be available on the L<Catalyst::Action> |
b7c570ac |
228 | instance via its C<attributes> accessor. To give an example, this |
229 | action: |
38017482 |
230 | |
231 | sub foo : Local Bar('Baz') { |
232 | my ($self, $c) = @_; |
bbddff00 |
233 | my $attributes = $self->action_for('foo')->attributes; |
b7c570ac |
234 | $c->res->body($attributes->{Bar}[0] ); |
38017482 |
235 | } |
236 | |
b7c570ac |
237 | will set the response body to C<Baz>. The values always come in an |
238 | array reference. As you can see, you can use attributes to configure |
239 | your actions. You can specify or alter these attributes via |
240 | L</"Component Configuration">, or even react on them as soon as |
241 | Catalyst encounters them by providing your own L<component base |
4307eb1c |
242 | class|/"Component base classes">. |
38017482 |
243 | |
d7823323 |
244 | =head2 Component Configuration |
38017482 |
245 | |
b7c570ac |
246 | At creation time, the class configuration of your component (the one |
429d1caf |
247 | available via C<< $self->config >>) will be merged with possible |
38017482 |
248 | configuration settings from the applications configuration (either |
6e1417cd |
249 | directly or via config file). This is done by Catalyst, and the |
250 | correctly merged configuration is passed to your component's |
251 | constructor (i.e. the new method). |
38017482 |
252 | |
6e1417cd |
253 | Ergo, if you define an accessor for each configuration value |
254 | that your component takes, then the value will be automatically stored |
255 | in the controller object's hash reference, and available from the |
256 | accessor. |
38017482 |
257 | |
6e1417cd |
258 | The C<config> accessor always only contains the original class configuration |
080bb620 |
259 | and you B<MUST NEVER> call C<< $self->config >> to get your component configuration, |
6e1417cd |
260 | as the data there is likely to be a subset of the correct config. |
38017482 |
261 | |
6e1417cd |
262 | For example: |
bbddff00 |
263 | |
6e1417cd |
264 | package MyApp |
265 | use Moose; |
266 | |
267 | extends 'Catalyst'; |
268 | |
269 | ... |
270 | |
271 | __PACKAGE__->config( |
272 | 'Controller::Foo' => { some_value => 'bar' }, |
273 | ); |
274 | |
275 | ... |
bbddff00 |
276 | |
277 | package MyApp::Controller::Foo; |
278 | use Moose; |
279 | use namespace::autoclean; |
280 | BEGIN { extends 'Catalyst::Controller' }; |
281 | |
6e1417cd |
282 | has some_value ( is => 'ro', required => 1 ); |
283 | |
284 | sub some_method { |
285 | my $self = shift; |
286 | return "the value of 'some_value' is " . $self->some_value; |
287 | } |
38017482 |
288 | |
38017482 |
289 | ... |
6e1417cd |
290 | |
291 | my $controller = $c->controller('Foo'); |
292 | warn $controller->some_value; |
293 | warn $controller->some_method; |
38017482 |
294 | |
295 | =head1 IMPLEMENTATION |
296 | |
b7c570ac |
297 | This part contains the technical details of various implementation |
38017482 |
298 | methods. Please read the L</"BEST PRACTICES"> before you start your |
299 | implementation, if you haven't already. |
300 | |
301 | =head2 Action classes |
302 | |
303 | Usually, your action objects are of the class L<Catalyst::Action>. |
304 | You can override this with the C<ActionClass> attribute to influence |
b7c570ac |
305 | execution and/or dispatching of the action. A widely used example of |
306 | this is L<Catalyst::Action::RenderView>, which is used in every newly |
307 | created Catalyst application in your root controller: |
38017482 |
308 | |
309 | sub end : ActionClass('RenderView') { } |
310 | |
b7c570ac |
311 | Usually, you want to override the C<execute> and/or the C<match> |
312 | method. The execute method of the action will naturally call the |
313 | methods code. You can surround this by overriding the method in a |
314 | subclass: |
38017482 |
315 | |
7ce05098 |
316 | package Catalyst::Action::MyFoo; |
bbddff00 |
317 | use Moose; |
318 | use namespace::autoclean; |
7ce05098 |
319 | use MRO::Compat; |
bbddff00 |
320 | extends 'Catalyst::Action'; |
38017482 |
321 | |
322 | sub execute { |
323 | my $self = shift; |
324 | my ($controller, $c, @args) = @_; |
38017482 |
325 | # put your 'before' code here |
326 | my $r = $self->next::method(@_); |
327 | # put your 'after' code here |
38017482 |
328 | return $r; |
329 | } |
38017482 |
330 | 1; |
331 | |
20a4dd98 |
332 | We are using L<MRO::Compat> to ensure that you have the next::method |
7ce05098 |
333 | call, from L<Class::C3> (in older perls), or natively (if you are using |
334 | perl 5.10) to re-dispatch to the original C<execute> method in the |
20a4dd98 |
335 | L<Catalyst::Action> class. |
38017482 |
336 | |
b7c570ac |
337 | The Catalyst dispatcher handles an incoming request and, depending |
7ce05098 |
338 | upon the dispatch type, will call the appropriate target or chain. |
b7c570ac |
339 | From time to time it asks the actions themselves, or through the |
340 | controller, if they would match the current request. That's what the |
341 | C<match> method does. So by overriding this, you can change on what |
342 | the action will match and add new matching criteria. |
38017482 |
343 | |
b7c570ac |
344 | For example, the action class below will make the action only match on |
345 | Mondays: |
38017482 |
346 | |
7ce05098 |
347 | package Catalyst::Action::OnlyMondays; |
bbddff00 |
348 | use Moose; |
349 | use namespace::autoclean; |
20a4dd98 |
350 | use MRO::Compat; |
bbddff00 |
351 | extends 'Catalyst::Action'; |
38017482 |
352 | |
353 | sub match { |
354 | my $self = shift; |
355 | return 0 if ( localtime(time) )[6] == 1; |
356 | return $self->next::method(@_); |
b7c570ac |
357 | } |
38017482 |
358 | 1; |
359 | |
360 | And this is how we'd use it: |
361 | |
362 | sub foo: Local ActionClass('OnlyMondays') { |
363 | my ($self, $c) = @_; |
364 | $c->res->body('I feel motivated!'); |
365 | } |
366 | |
b7c570ac |
367 | If you are using action classes often or have some specific base |
368 | classes that you want to specify more conveniently, you can implement |
369 | a component base class providing an attribute handler. |
38017482 |
370 | |
bbddff00 |
371 | It is not possible to use multiple action classes at once, however |
372 | L<Catalyst::Controller::ActionRole> allows you to apply L<Moose Roles|Moose::Role> |
373 | to actions. |
374 | |
375 | For further information on action classes and roles, please refer to |
38017482 |
376 | L<Catalyst::Action> and L<Catalyst::Manual::Actions>. |
377 | |
378 | =head2 Component base classes |
379 | |
b7c570ac |
380 | Many L<Catalyst::Plugin> that were written in Catalyst's early days |
381 | should really have been just controller base classes. With such a |
382 | class, you could provide functionality scoped to a single controller, |
383 | not polluting the global namespace in the context object. |
38017482 |
384 | |
b7c570ac |
385 | You can provide regular Perl methods in a base class as well as |
386 | actions which will be inherited to the subclass. Please refer to |
387 | L</Controllers> for an example of this. |
38017482 |
388 | |
b7c570ac |
389 | You can introduce your own attributes by specifying a handler method |
390 | in the controller base. For example, to use a C<FullClass> attribute |
391 | to specify a fully qualified action class name, you could use the |
392 | following implementation. Note, however, that this functionality is |
393 | already provided via the C<+> prefix for action classes. A simple |
38017482 |
394 | |
395 | sub foo : Local ActionClass('+MyApp::Action::Bar') { ... } |
396 | |
397 | will use C<MyApp::Action::Bar> as action class. |
398 | |
bbddff00 |
399 | package MyApp::Base::Controller::FullClass; |
400 | use Moose; |
401 | use namespace::autoclean; |
402 | BEGIN { extends 'Catalyst::Controller'; } |
38017482 |
403 | |
404 | sub _parse_FullClass_attr { |
405 | my ($self, $app_class, $action_name, $value, $attrs) = @_; |
406 | return( ActionClass => $value ); |
407 | } |
38017482 |
408 | 1; |
409 | |
b7c570ac |
410 | Note that the full line of arguments is only provided for completeness |
411 | sake. We could use this attribute in a subclass like any other |
412 | Catalyst attribute: |
38017482 |
413 | |
414 | package MyApp::Controller::Foo; |
bbddff00 |
415 | use Moose; |
416 | use namespace::autoclean; |
417 | BEGIN { extends 'MyApp::Base::Controller::FullClass'; } |
38017482 |
418 | |
419 | sub foo : Local FullClass('MyApp::Action::Bar') { ... } |
420 | |
421 | 1; |
422 | |
423 | =head2 Controllers |
424 | |
b7c570ac |
425 | Many things can happen in controllers, and it often improves |
426 | maintainability to abstract some of the code out into reusable base |
38017482 |
427 | classes. |
428 | |
429 | You can provide usual Perl methods that will be available via your |
b7c570ac |
430 | controller object, or you can even define Catalyst actions which will |
431 | be inherited by the subclasses. Consider this controller base class: |
38017482 |
432 | |
433 | package MyApp::Base::Controller::ModelBase; |
bbddff00 |
434 | use Moose; |
435 | use namespace::autoclean; |
436 | |
437 | BEGIN { extends 'Catalyst::Controller'; } |
38017482 |
438 | |
439 | sub list : Chained('base') PathPart('') Args(0) { |
440 | my ($self, $c) = @_; |
b7c570ac |
441 | my $model = $c->model( $self->{model_name} ); |
38017482 |
442 | my $condition = $self->{model_search_condition} || {}; |
b7c570ac |
443 | my $attrs = $self->{model_search_attrs} || {}; |
38017482 |
444 | $c->stash(rs => $model->search($condition, $attrs); |
bbddff00 |
445 | } |
38017482 |
446 | |
447 | sub load : Chained('base') PathPart('') CaptureArgs(1) { |
448 | my ($self, $c, $id) = @_; |
449 | my $model = $c->model( $self->{model_name} ); |
450 | $c->stash(row => $model->find($id)); |
bbddff00 |
451 | } |
38017482 |
452 | 1; |
453 | |
b7c570ac |
454 | This example implements two simple actions. The C<list> action chains |
455 | to a (currently non-existent) C<base> action and puts a result-set |
456 | into the stash taking a configured C<model_name> as well as a search |
457 | condition and attributes. This action is a |
458 | L<chained|Catalyst::DispatchType::Chained> endpoint. The other action, |
459 | called C< load > is a chain midpoint that takes one argument. It takes |
460 | the value as an ID and loads the row from the configured model. Please |
461 | not that the above code is simplified for clarity. It misses error |
462 | handling, input validation, and probably other things. |
38017482 |
463 | |
b7c570ac |
464 | The class above is not very useful on its own, but we can combine it |
465 | with some custom actions by sub-classing it: |
38017482 |
466 | |
467 | package MyApp::Controller::Foo; |
bbddff00 |
468 | use Moose; |
469 | use namespace::autoclean; |
7ce05098 |
470 | |
bbddff00 |
471 | BEGIN { extends 'MyApp::Base::Controller::ModelBase'; } |
38017482 |
472 | |
b7c570ac |
473 | __PACKAGE__->config( model_name => 'DB::Foo', |
474 | model_search_condition=> { is_active => 1 }, |
475 | model_search_attrs => { order_by => 'name' }, |
476 | ); |
38017482 |
477 | |
478 | sub base : Chained PathPart('foo') CaptureArgs(0) { } |
479 | |
480 | sub view : Chained('load') Args(0) { |
481 | my ($self, $c) = @_; |
482 | my $row = $c->stash->{row}; |
b7c570ac |
483 | $c->res->body(join ': ', $row->name, |
484 | $row->description); } |
38017482 |
485 | 1; |
486 | |
b7c570ac |
487 | This class uses the formerly created controller as a base |
488 | class. First, we see the configurations that were used in the parent |
489 | class. Next comes the C<base> action, where everything chains off of. |
38017482 |
490 | |
b7c570ac |
491 | Note that inherited actions act like they were declared in your |
492 | controller itself. You can therefor call them just by their name in |
38017482 |
493 | C<forward>s, C<detaches> and C<Chained(..)> specifications. This is an |
494 | important part of what makes this technique so useful. |
495 | |
b7c570ac |
496 | The new C<view> action ties itself to the C<load> action specified in |
497 | the base class and outputs the loaded row's C<name> and C<description> |
498 | columns. The controller C<MyApp::Controller::Foo> now has these |
499 | publicly available paths: |
38017482 |
500 | |
501 | =over |
502 | |
503 | =item /foo |
504 | |
b7c570ac |
505 | Will call the controller's C<base>, then the base classes C<list> |
506 | action. |
38017482 |
507 | |
508 | =item /foo/$id/view |
509 | |
b7c570ac |
510 | First, the controller's C<base> will be called, then it will C<load> |
511 | the row with the corresponding C<$id>. After that, C<view> will |
512 | display some fields out of the object. |
38017482 |
513 | |
514 | =back |
515 | |
516 | =head2 Models and Views |
517 | |
b7c570ac |
518 | If the functionality you'd like to add is really a data-set that you |
519 | want to manipulate, for example internal document types, images, |
520 | files, it might be better suited as a model. |
38017482 |
521 | |
b7c570ac |
522 | The same applies for views. If your code handles representation or |
523 | deals with the applications interface and should be universally |
524 | available, it could be a perfect candidate for a view. |
38017482 |
525 | |
b7c570ac |
526 | Please implement a C<process> method in your views. This method will |
527 | be called by Catalyst if it is asked to forward to a component without |
528 | a specified action. Note that C<process> is B<not a Catalyst action> |
529 | but a simple Perl method. |
38017482 |
530 | |
531 | You are also encouraged to implement a C<render> method corresponding |
532 | with the one in L<Catalyst::View::TT>. This has proven invaluable, |
533 | because people can use your view for much more fine-grained content |
534 | generation. |
535 | |
536 | Here is some example code for a fictional view: |
537 | |
bbddff00 |
538 | package Catalyst::View::MyView; |
539 | use Moose; |
540 | use namespace::autoclean; |
7ce05098 |
541 | |
bbddff00 |
542 | extends 'Catalyst::View'; |
38017482 |
543 | |
544 | sub process { |
545 | my ($self, $c) = @_; |
38017482 |
546 | my $template = $c->stash->{template}; |
b7c570ac |
547 | my $content = $self->render($c, $template, $c->stash); |
38017482 |
548 | $c->res->body( $content ); |
549 | } |
550 | |
551 | sub render { |
552 | my ($self, $c, $template, $args) = @_; |
b7c570ac |
553 | # prepare content here |
38017482 |
554 | return $content; |
555 | } |
38017482 |
556 | 1; |
557 | |
558 | =head2 Plugins |
559 | |
b7c570ac |
560 | The first thing to say about plugins is that if you're not sure if |
561 | your module should be a plugin, it probably shouldn't. It once was |
562 | common to add features to Catalyst by writing plugins that provide |
563 | accessors to said functionality. As Catalyst grew more popular, it |
564 | became obvious that this qualifies as bad practice. |
565 | |
566 | By designing your module as a Catalyst plugin, every method you |
567 | implement, import or inherit will be available via your applications |
568 | context object. A plugin pollutes the global namespace, and you |
569 | should be only doing that when you really need to. |
570 | |
571 | Often, developers design extensions as plugins because they need to |
572 | get hold of the context object. Either to get at the stash or |
573 | request/response objects are the widely spread reasons. It is, |
574 | however, perfectly possible to implement a regular Catalyst component |
575 | (read: model, view or controller) that receives the current context |
576 | object via L<Catalyst::Component/"ACCEPT_CONTEXT($c, @args)">. |
577 | |
578 | When is a plugin suited to your task? Your code needs to be a |
579 | plugin to act upon or alter specific parts of Catalyst's request |
78170776 |
580 | lifecycle. If your functionality needs to change some C<prepare_*> or |
b7c570ac |
581 | C<finalize_*> stages, you won't get around a plugin. |
582 | |
78170776 |
583 | Note, if you just want to hook into such a stage, and run code before, |
080bb620 |
584 | or after it, then it is recommended that you use L<Moose>'s method modifiers |
78170776 |
585 | to do this. |
586 | |
b7c570ac |
587 | Another valid target for a plugin architecture are things that |
588 | B<really> have to be globally available, like sessions or |
589 | authentication. |
590 | |
591 | B<Please do not> release Catalyst extensions as plugins only to |
592 | provide some functionality application wide. Design it as a controller |
bbddff00 |
593 | base class or another better suited technique with a smaller scope, so that |
b7c570ac |
594 | your code only influences those parts of the application where it is |
595 | needed, and namespace clashes and conflicts are ruled out. |
38017482 |
596 | |
597 | The implementation is pretty easy. Your plugin will be inserted in the |
598 | application's inheritance list, above Catalyst itself. You can by this |
b7c570ac |
599 | alter Catalyst's request lifecycle behaviour. Every method you |
600 | declare, every import in your package will be available as method on |
601 | the application and the context object. As an example, let's say you |
78170776 |
602 | want Catalyst to warn you every time uri_for was called without an action |
603 | object as the first parameter, for example to test that all your chained |
604 | uris are generated from actions (a recommended best practice). |
605 | You could do this with this simple |
38017482 |
606 | implementation (excuse the lame class name, it's just an example): |
607 | |
608 | package Catalyst::Plugin::UriforUndefWarning; |
609 | use strict; |
78170776 |
610 | use Scalar::Util qw/blessed/; |
20a4dd98 |
611 | use MRO::Compat; |
38017482 |
612 | |
613 | sub uri_for { |
b7c570ac |
614 | my $c = shift; |
38017482 |
615 | my $uri = $c->next::method(@_); |
78170776 |
616 | $c->log->warn( 'uri_for with non action: ', join(', ', @_), ) |
617 | if (!blessed($_[0]) || !$_[0]->isa('Catalyst::Action')); |
38017482 |
618 | return $uri; |
619 | } |
620 | |
621 | 1; |
622 | |
b7c570ac |
623 | This would override Catalyst's C<uri_for> method and emit a C<warn> |
78170776 |
624 | log entry containing the arguments to uri_for. |
625 | |
626 | Please note this is not a practical example, as string URLs are fine for |
627 | static content etc. |
628 | |
629 | A simple example like this is actually better as a L<Moose> role, for example: |
630 | |
631 | package CatalystX::UriforUndefWarning; |
632 | use Moose::Role; |
4d719c7e |
633 | use namespace::autoclean; |
78170776 |
634 | |
635 | after 'uri_for' => sub { |
636 | my ($c, $arg) = @_; |
637 | $c->log->warn( 'uri_for with non action: ', join(', ', @_), ) |
638 | if (!blessed($_[0]) || !$_[0]->isa('Catalyst::Action')); |
639 | return $uri; |
fa025310 |
640 | }; |
7ce05098 |
641 | |
bbddff00 |
642 | Note that Catalyst will load any Moose Roles in the plugin list, |
643 | and apply them to your application class. |
38017482 |
644 | |
645 | =head2 Factory components with COMPONENT() |
646 | |
b7c570ac |
647 | Every component inheriting from L<Catalyst::Component> contains a |
648 | C<COMPONENT> method. It is used on application startup by |
649 | C<setup_components> to instantiate the component object for the |
650 | Catalyst application. By default, this will merge the components own |
651 | C<config>uration with the application wide overrides and call the |
652 | class' C<new> method to return the component object. |
38017482 |
653 | |
b7c570ac |
654 | You can override this method and do and return whatever you want. |
fa025310 |
655 | However, you should use L<Class::C3> (via L<MRO::Compat>) to forward |
656 | to the original C<COMPONENT> method to merge the configuration of |
20a4dd98 |
657 | your component. |
38017482 |
658 | |
659 | Here is a stub C<COMPONENT> method: |
660 | |
661 | package CatalystX::Component::Foo; |
bbddff00 |
662 | use Moose; |
663 | use namespace::autoclean; |
7ce05098 |
664 | |
bbddff00 |
665 | extends 'Catalyst::Component'; |
38017482 |
666 | |
667 | sub COMPONENT { |
668 | my $class = shift; |
a70cede4 |
669 | # Note: $app is like $c, but since the application isn't fully |
7ce05098 |
670 | # initialized, we don't want to call it $c yet. $config |
a70cede4 |
671 | # is a hashref of config options possibly set on this component. |
672 | my ($app, $config) = @_; |
673 | |
674 | # Do things here before instantiation |
675 | $new = $class->next::method(@_); |
676 | # Do things to object after instantiation |
677 | return $new; |
38017482 |
678 | } |
679 | |
680 | The arguments are the class name of the component, the class name of |
b7c570ac |
681 | the application instantiating the component, and a hash reference with |
682 | the controller's configuration. |
38017482 |
683 | |
b7c570ac |
684 | You are free to re-bless the object, instantiate a whole other |
685 | component or really do anything compatible with Catalyst's |
686 | expectations on a component. |
38017482 |
687 | |
fa025310 |
688 | For more information, please see |
689 | L<Catalyst::Component/"COMPONENT($c,$arguments)">. |
38017482 |
690 | |
bbddff00 |
691 | =head2 Applying roles to parts of the framework |
692 | |
693 | L<CatalystX::RoleApplicator> will allow you to apply Roles to |
694 | the following classes: |
695 | |
696 | =over |
697 | |
698 | =item Request |
699 | |
700 | =item Response |
701 | |
702 | =item Engine |
703 | |
704 | =item Dispatcher |
705 | |
706 | =item Stats |
707 | |
708 | =back |
709 | |
710 | These roles can add new methods to these classes, or wrap preexisting methods. |
711 | |
712 | The namespace for roles like this is C<Catalyst::TraitFor::XXX::YYYY>. |
713 | |
714 | For an example of a CPAN component implemented in this manor, see |
715 | L<Catalyst::TraitFor::Request::BrowserDetect>. |
716 | |
38017482 |
717 | =head1 SEE ALSO |
718 | |
b7c570ac |
719 | L<Catalyst>, L<Catalyst::Manual::Actions>, L<Catalyst::Component> |
38017482 |
720 | |
bbddff00 |
721 | =head1 AUTHORS |
38017482 |
722 | |
bbddff00 |
723 | Catalyst Contributors, see Catalyst.pm |
1972ebdd |
724 | |
bbddff00 |
725 | =head1 COPYRIGHT |
38017482 |
726 | |
bbddff00 |
727 | This library is free software. You can redistribute it and/or modify it under |
38017482 |
728 | the same terms as Perl itself. |
729 | |
730 | =cut |