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 |
8 | practices to extend the framework itself or to find more elegant ways |
9 | to abstract and use your own code. |
10 | |
11 | The L<Catalyst> core developer community tries to build and evolve the |
12 | framework in a design that won't get in your way. There are many entry |
13 | points to alter or extend the behaviour of the framework and the |
14 | applications built upon it, and it can be confusing at first to decide |
15 | on a place to start. This document is written to help you orient |
16 | yourself in the possibilities, current practices and their consequences. |
17 | |
18 | Please read the L<BEST PRACTICES> section before deciding on a design, |
19 | especially if you plan to release your code to the CPAN. The Catalyst |
20 | developer and user communities, which you naturally also belong to, will |
21 | benefit most if we all work together and coordinate. |
22 | |
23 | If you are unsure on an implementation or have an idea you would like to |
24 | have RfC'ed, it surely is a good idea to send your questions and |
25 | suggestions to the Catalyst mailing list (See L<Catalyst/SUPPORT>) or |
26 | come to the C<#catalyst> channel on the C<irc.perl.org> network. You |
27 | might also want to refer to those places for research if a module doing |
28 | what you're trying to implement already exists. This might give you |
29 | either a solution to your problem or a already built base, which gives |
30 | you at least a head start. |
31 | |
32 | =head1 BEST PRACTICES |
33 | |
34 | During Catalyst's early days, it was common to write plugins to provide |
35 | functionality application wide. Since then, Catalyst grew a lot more |
36 | flexible and powerful. It soon became a best practice to use some other |
37 | form of abstraction or interface, to keep the scope of its influence as |
38 | close as possible to where it belongs. |
39 | |
40 | For those in a hurry, here's a quick checklist of some fundamental |
41 | points. If you are going to read the whole thing anyway, you can jump |
42 | forward to L</Namespaces>. |
43 | |
44 | =head2 Quick Checklist |
45 | |
46 | =over |
47 | |
48 | =item Use the C<CatalystX::*> namespace if you can! |
49 | |
50 | Excluding plugins and of course your C<MyApp> code. B<Mind the X!> |
51 | |
52 | =item Don't make it a plugin unless you have to! |
53 | |
54 | A plugin should be careful as it declares in global namespace. |
55 | |
56 | =item There's a community. Use it! |
57 | |
58 | There are many experienced developers in the Catalyst community, there's |
59 | always the IRC channel and the mailing list to discuss things. |
60 | |
61 | =item Add tests and documentation! |
62 | |
63 | This gives a stable base for contribution, and even more important, |
64 | trust. The easiest way is a test application. See |
65 | L<Catalyst::Manual::Tutorial::Testing> for more information. |
66 | |
67 | =back |
68 | |
69 | =head2 Namespaces |
70 | |
71 | While some core extensions (engines, plugins, etc.) have to be placed in |
72 | the C<Catalyst::*> namespace, the Catalyst core would like to ask |
73 | developers to use the C<CatalystX::*> namespace if possible. |
74 | |
75 | When you try to put a base class for a C<Model>, C<View> or C<Controller> |
76 | directly under your C<MyApp> directory as, for example, |
77 | C<MyApp::Controller::Foo>, you will have the problem that Catalyst will |
78 | try to load that base class as a component of your application. The |
79 | solution is simple: Use another namespace. Common ones are |
80 | C<MyApp::Base::Controller::*> or C<MyApp::ControllerBase::*> as examples. |
81 | |
82 | =head2 Can it be a simple module? |
83 | |
84 | Sometimes you want to use functionality in your application that doesn't |
85 | require the framework at all. Remember that Catalyst is just Perl and you |
86 | always can just C<use> a module. If you have application specific code |
87 | that doesn't need the framework, there is no problem in putting it in |
88 | your C<MyApp::*> namespace. Just don't put it in C<Model>, C<Controller> |
89 | or C<View>, because that would make Catalyst try to load them as |
90 | components. |
91 | |
92 | =head2 Inheritance and overriding methods |
93 | |
94 | While Catalyst itself is still based on L<NEXT>, extension developers |
95 | are encouraged to use L<Class::C3>, which is what Catalyst will be |
96 | switching to in some point in the future. |
97 | |
98 | When overriding a method, keep in mind that some day additionally |
99 | arguments may be provided to the method, if the last parameter is not |
100 | a flat list. It is thus better to override a method by shifting the |
101 | invocant off of C<@_> and assign the rest of the used arguments, so |
102 | you can pass your complete arguments to the original method via C<@_>: |
103 | |
104 | use Class::C3; |
105 | ... |
106 | |
107 | sub foo { |
108 | my $self = shift; |
109 | my ($bar, $baz) = @_; |
110 | # ... |
111 | return $self->next::method(@_); |
112 | } |
113 | |
114 | If you would do the common |
115 | |
116 | my ($self, $foo, $bar) = @_; |
117 | |
118 | you'd have to use a much uglier construct to ensure that all arguments |
119 | will be passed along and the method is future proof: |
120 | |
121 | $self->next::method(@_[ 1 .. $#_ ]); |
122 | |
123 | =head2 Tests and documentation |
124 | |
125 | When you release your module to the CPAN, proper documentation and at |
126 | least a basic test suite (which means more than pod or even just |
127 | C<use_ok>, sorry) gives people a good base to contribute to the module. |
128 | It also shows that you care for your users. If you would like your |
129 | module to become a recommended addition, these things will prove |
130 | invaluable. |
131 | |
132 | =head2 Maintenance |
133 | |
134 | In planning to release a module to a broad community like those of |
135 | Catalyst or CPAN and Perl themselves, you should include beforehand if |
136 | you can spare the resources to keep it up to date, fix bugs and include |
137 | contributions. |
138 | |
139 | If you're not sure about this, you can always ask in the proper Catalyst |
140 | or Perl channels if someone else might be interested in the project, and |
141 | would jump in as co-maintainer. |
142 | |
143 | A public repository can further ease interaction with the community. Even |
144 | read only access enables people to provide you with patches to your |
145 | current development version. subversion, SVN and SVK, are broadly |
146 | preferred in the Catalyst community. |
147 | |
148 | If you're developing a Catalyst extension, please consider asking the |
149 | core team for space in Catalyst's own subversion repository. You can get |
150 | in touch about this via IRC or the Catalyst developers mailing list. |
151 | |
152 | =head2 The context object |
153 | |
154 | Sometimes you want to get a hold of the context object in a component |
155 | that was created on startup time, where no context existed yet. Often |
156 | this is about the model reading something out of the stash or other |
157 | context information (current language, for example). |
158 | |
159 | First it should be said that, if you use the context object in your |
160 | component and therefor tie it to an existing request, you might get into |
161 | problems when you try to use the component (e.g. the model, which would |
162 | be the most common case) outside of Catalyst, for example in cronjobs. |
163 | |
164 | A stable solution to this problem is to design the Catalyst model |
165 | separately from the underlying model logic. Let's take |
166 | L<Catalyst::Model::DBIC::Schema> as an example. You can create a |
167 | schema outside of Catalyst that knows nothing about the web. This kind |
168 | of design ensures encapsulation and makes development and maintenance |
169 | a whole lot easier. The you use the aforementioned model to tie your |
170 | schema to your application. This gives you a C<MyApp::DBIC> (the |
171 | name is of course just an example) model as well as |
172 | C<MyApp::DBIC::$ResultSource> models to access your result sources |
173 | directly. |
174 | |
175 | By creating such a thin layer between the actual model and the Catalyst |
176 | application the schema itself is not at all tied to any application, and |
177 | the layer in-between can access the model's API using information from |
178 | the context object. |
179 | |
180 | So the only question remaining is, how does a Catalyst component access |
181 | the context object at request time? The solution to this problem is |
182 | L<Catalyst::Component/"ACCEPT_CONTEXT($c, @args)">. |
183 | |
184 | =head1 CONFIGURATION |
185 | |
186 | The application and/or its developer have to interact with the extension |
187 | by configuring them. There is of course again more than one way to do it. |
188 | |
189 | =head2 Attributes |
190 | |
191 | You can specify any valid Perl attribute on Catalyst actions you like. |
192 | (See L<attributes/"Syntax of Attribute Lists"> for a description of what |
193 | is valid.) They will be available on the C<Catalyst::Action> instance via |
194 | its C<attributes> accessor. To give an example, this action |
195 | |
196 | sub foo : Local Bar('Baz') { |
197 | my ($self, $c) = @_; |
198 | my $attributes = $self->action_for('foo')->attributes; |
199 | $c->res->body( $attributes->{Bar}[0] ); |
200 | } |
201 | |
202 | will set the response body to C<Baz>. The values always come in an array |
203 | reference. As you can see, you can use attributes to configure your |
204 | actions. You can specify or alter these attributes via |
205 | L</"Component Configuration">, or even react on them as soon as Catalyst |
206 | encounters them by providing your own |
207 | L<component base class|/"Component Base Classes">. |
208 | |
209 | =head2 Creating custom accessors |
210 | |
211 | L<Catalyst::Component> uses L<Class::Accessor::Fast> for accessor |
212 | creation. Please refer to the modules documentation for usage |
213 | information. |
214 | |
215 | =head2 Component configuration |
216 | |
217 | On creation time, the class configuration of your component (the one |
218 | available via C<$self-E<gt>config>) will be merged with possible |
219 | configuration settings from the applications configuration (either |
220 | directly or via config file) and stored in the controller object's |
221 | hash reference. So, if you read possible configurations like |
222 | |
223 | my $model_name = $controller->{model_name}; |
224 | |
225 | you will get the right value. The C<config> accessor always only |
226 | contains the original class configuration and must not be used for |
227 | component configuration. |
228 | |
229 | You are advised to create accessors on your component class for your |
230 | configuration values. This is good practice and makes it easier to |
231 | capture configuration key typos. You can do this with the |
232 | C<mk_ro_accessors> method provided to L<Catalyst::Component> via |
233 | L<Class::Accessor::Fast>: |
234 | |
235 | use base 'Catalyst::Controller'; |
236 | __PACKAGE__->mk_ro_accessors('model_name'); |
237 | ... |
238 | my $model_name = $controller->model_name; |
239 | |
240 | =head1 IMPLEMENTATION |
241 | |
242 | This part contains the technical details of various implementation |
243 | methods. Please read the L</"BEST PRACTICES"> before you start your |
244 | implementation, if you haven't already. |
245 | |
246 | =head2 Action classes |
247 | |
248 | Usually, your action objects are of the class L<Catalyst::Action>. |
249 | You can override this with the C<ActionClass> attribute to influence |
250 | execution and/or dispatching of the action. A popular example is |
251 | L<Catalyst::Action::RenderView>, which is used in every newly created |
252 | Catalyst application in your root controller: |
253 | |
254 | sub end : ActionClass('RenderView') { } |
255 | |
256 | Usually, you want to override either the C<execute> or the C<match> |
257 | method, or both. The execute method of the action will naturally |
258 | call the methods code. You can surround this by overriding the method |
259 | in a subclass: |
260 | |
261 | package Catalyst::Action::MyFoo; |
262 | use strict; |
263 | |
264 | use Class::C3; |
265 | use base 'Catalyst::Action'; |
266 | |
267 | sub execute { |
268 | my $self = shift; |
269 | my ($controller, $c, @args) = @_; |
270 | |
271 | # put your 'before' code here |
272 | my $r = $self->next::method(@_); |
273 | # put your 'after' code here |
274 | |
275 | return $r; |
276 | } |
277 | |
278 | 1; |
279 | |
280 | We use L<Class::C3> to re-dispatch to the original C<execute> method in |
281 | the L<Catalyst::Action> class. |
282 | |
283 | When a request comes in, Catalyst's dispatcher searches, depending on |
284 | the dispatch type, the target action or chain to invoke. From time to |
285 | time it asks the actions themselves, or through the controller, if they |
286 | would match the current request. That's what the C<match> method does. |
287 | So with overriding this, you can change on what the action will match |
288 | and add new matching criteria. |
289 | |
290 | To give a totally bogus example, this action class will make the action |
291 | only match on Mondays: |
292 | |
293 | package Catalyst::Action::OnlyMondays; |
294 | use strict; |
295 | |
296 | use Class::C3; |
297 | use base 'Catalyst::Action'; |
298 | |
299 | sub match { |
300 | my $self = shift; |
301 | return 0 if ( localtime(time) )[6] == 1; |
302 | return $self->next::method(@_); |
303 | } |
304 | |
305 | 1; |
306 | |
307 | And this is how we'd use it: |
308 | |
309 | sub foo: Local ActionClass('OnlyMondays') { |
310 | my ($self, $c) = @_; |
311 | $c->res->body('I feel motivated!'); |
312 | } |
313 | |
314 | If you are using action classes often or have some specific base classes |
315 | that you want to specify more conveniently, you can implement a component |
316 | base class providing an attribute handler. |
317 | |
318 | For further information on action classes, please refer to |
319 | L<Catalyst::Action> and L<Catalyst::Manual::Actions>. |
320 | |
321 | =head2 Component base classes |
322 | |
323 | Many plugins that were written should really have been just controller |
324 | base classes. With such a class, you could provide functionality scoped |
325 | to a single controller, not polluting the global namespace in the context |
326 | object. |
327 | |
328 | You can provide regular Perl methods in a base class as well as actions |
329 | which will be inherited to the subclass. Please refer to L</Controllers> |
330 | for an example of this. |
331 | |
332 | You can introduce your own attributes by specifying a handler method in |
333 | the controller base. For example, to use a C<FullClass> attribute to |
334 | specify a fully qualified action class name, you could use the following |
335 | implementation. Note, however, that this functionality is already |
336 | provided via the C<+> prefix for action classes. A simple |
337 | |
338 | sub foo : Local ActionClass('+MyApp::Action::Bar') { ... } |
339 | |
340 | will use C<MyApp::Action::Bar> as action class. |
341 | |
342 | package MyApp::Base::Controller::FullClass; |
343 | use strict; |
344 | use base 'Catalyst::Controller'; |
345 | |
346 | sub _parse_FullClass_attr { |
347 | my ($self, $app_class, $action_name, $value, $attrs) = @_; |
348 | return( ActionClass => $value ); |
349 | } |
350 | |
351 | 1; |
352 | |
353 | Note that the full line of arguments is only provided for completeness |
354 | sake. We could use this attribute in a subclass like any other Catalyst |
355 | attribute: |
356 | |
357 | package MyApp::Controller::Foo; |
358 | use strict; |
359 | use base 'MyApp::Base::Controller::FullClass'; |
360 | |
361 | sub foo : Local FullClass('MyApp::Action::Bar') { ... } |
362 | |
363 | 1; |
364 | |
365 | =head2 Controllers |
366 | |
367 | Many things can happen in controllers, and it often improves |
368 | maintainability to abstract some of the code out into reusable base |
369 | classes. |
370 | |
371 | You can provide usual Perl methods that will be available via your |
372 | controller object, or you can even define Catalyst actions which will be |
373 | inherited by the subclasses. Consider this controller base class: |
374 | |
375 | package MyApp::Base::Controller::ModelBase; |
376 | use strict; |
377 | use base 'Catalyst::Controller'; |
378 | |
379 | sub list : Chained('base') PathPart('') Args(0) { |
380 | my ($self, $c) = @_; |
381 | my $model = $c->model( $self->{model_name} ); |
382 | my $condition = $self->{model_search_condition} || {}; |
383 | my $attrs = $self->{model_search_attrs} || {}; |
384 | $c->stash(rs => $model->search($condition, $attrs); |
385 | } |
386 | |
387 | sub load : Chained('base') PathPart('') CaptureArgs(1) { |
388 | my ($self, $c, $id) = @_; |
389 | my $model = $c->model( $self->{model_name} ); |
390 | $c->stash(row => $model->find($id)); |
391 | } |
392 | |
393 | 1; |
394 | |
395 | This example implements two simple actions. The C<list> action chains to |
396 | a (currently non-existent) C<base> action and puts a result-set into the |
397 | stash taking a configured C<model_name> as well as a search condition and |
398 | attributes. This action is a L<chained|Catalyst::DispatchType::Chained> |
399 | endpoint. The other action, called C<load> is a chain midpoint that takes |
400 | one argument. It takes the value as an ID and loads the row from the |
401 | configured model. Please not that the above code is simplified for |
402 | clarity. It misses error handling, input validation, and probably some |
403 | other things too. |
404 | |
405 | The class above is not very useful by its own, but we can combine it with |
406 | some custom actions by sub-classing it: |
407 | |
408 | package MyApp::Controller::Foo; |
409 | use strict; |
410 | use base 'MyApp::Base::Controller::ModelBase'; |
411 | |
412 | __PACKAGE__->config( |
413 | model_name => 'DB::Foo', |
414 | model_search_condition => { is_active => 1 }, |
415 | model_search_attrs => { order_by => 'name' }, |
416 | ); |
417 | |
418 | sub base : Chained PathPart('foo') CaptureArgs(0) { } |
419 | |
420 | sub view : Chained('load') Args(0) { |
421 | my ($self, $c) = @_; |
422 | my $row = $c->stash->{row}; |
423 | $c->res->body(join ': ', $row->name, $row->description); |
424 | } |
425 | |
426 | 1; |
427 | |
428 | This class uses the formerly created controller as a base class. First, |
429 | we see the configurations that were used in the parent class. Next comes |
430 | the C<base> action, where everything chains off of. |
431 | |
432 | Note that inherited actions act like they were declared in your |
433 | controller itself. You can therefor call them just by their name in |
434 | C<forward>s, C<detaches> and C<Chained(..)> specifications. This is an |
435 | important part of what makes this technique so useful. |
436 | |
437 | The new C<view> action ties itself to the C<load> action specified in the |
438 | base class and outputs the loaded row's C<name> and C<description> |
439 | columns. The controller C<MyApp::Controller::Foo> now has these publicly |
440 | available paths: |
441 | |
442 | =over |
443 | |
444 | =item /foo |
445 | |
446 | Will call the controller's C<base>, then the base classes C<list> action. |
447 | |
448 | =item /foo/$id/view |
449 | |
450 | First, the controller's C<base> will be called, then it will C<load> the |
451 | row with the corresponding C<$id>. After that, C<view> will display some |
452 | fields out of the object. |
453 | |
454 | =back |
455 | |
456 | =head2 Models and Views |
457 | |
458 | If the functionality you'd like to add is really a data-set you want to |
459 | manipulate, for example internal document types, images, files, it might |
460 | be better suited as a model. |
461 | |
462 | Same goes for views. If your code handles representation or deals with |
463 | the applications interface and should be universally available, it could |
464 | be a perfect candidate for a view. |
465 | |
466 | Please implement a C<process> method in your views. This method will be |
467 | called by Catalyst if it is asked to forward to a component without a |
468 | specified action. Note that C<process> is B<not a Catalyst action> but |
469 | a simple Perl method. |
470 | |
471 | You are also encouraged to implement a C<render> method corresponding |
472 | with the one in L<Catalyst::View::TT>. This has proven invaluable, |
473 | because people can use your view for much more fine-grained content |
474 | generation. |
475 | |
476 | Here is some example code for a fictional view: |
477 | |
478 | package CatalystX::View::MyView; |
479 | use strict; |
480 | use base 'Catalyst::View'; |
481 | |
482 | sub process { |
483 | my ($self, $c) = @_; |
484 | |
485 | my $template = $c->stash->{template}; |
486 | my $content = $self->render($c, $template, $c->stash); |
487 | $c->res->body( $content ); |
488 | } |
489 | |
490 | sub render { |
491 | my ($self, $c, $template, $args) = @_; |
492 | |
493 | # |
494 | # prepare content here |
495 | # |
496 | |
497 | return $content; |
498 | } |
499 | |
500 | 1; |
501 | |
502 | =head2 Plugins |
503 | |
504 | The first thing to say about plugins is that if you're not sure if your |
505 | module should be a plugin, it probably shouldn't. It once was common to |
506 | add features to Catalyst by writing plugins that provide accessors to |
507 | said functionality. As Catalyst grew more popular, it became obvious that |
508 | this qualifies as bad practice. |
509 | |
510 | By designing your module as a Catalyst plugin, every method you implement, |
511 | import or inherit will be available via your applications context object. |
512 | Said more harshly, you're polluting global namespace, and you should be |
513 | only doing that when you really need. |
514 | |
515 | Often, developers design extensions as plugins because they need to get |
516 | hold of the context object. Either to get at the stash or |
517 | request/response objects are the widely spread reasons. It is, however, |
518 | perfectly possible to implement a regular Catalyst component (read: |
519 | model, view or controller) that receives the current context object via |
520 | L<Catalyst::Component/"ACCEPT_CONTEXT($c, @args)">. |
521 | |
522 | So when is a plugin suited to your task? Your code needs to be a plugin |
523 | to act upon or alter specific parts of Catalyst's request lifecycle. If |
524 | your functionality needs to wrap some C<prepare_*> or C<finalize_*> |
525 | stages, you won't get around a plugin. |
526 | |
527 | Another valid target for a plugin architecture are things that B<really> |
528 | have to be globally available, like sessions or authentication. |
529 | |
530 | B<Please do not> release Catalyst extensions as plugins only to provide |
531 | some functionality application wide. Design it as a controller base class |
532 | or another suiting technique with a smaller scope, so that your code only |
533 | influences those parts of the application where it is needed, and |
534 | namespace clashes and conflicts are ruled out. |
535 | |
536 | The implementation is pretty easy. Your plugin will be inserted in the |
537 | application's inheritance list, above Catalyst itself. You can by this |
538 | alter Catalyst's request lifecycle behaviour. Every method you declare, |
539 | every import in your package will be available as method on the |
540 | application and the context object. As an example, let's say you want |
541 | Catalyst to warn you every time uri_for returned an undefined value, for |
542 | example because you specified the wrong number of captures for the |
543 | targeted action chain. You could do this with this simple |
544 | implementation (excuse the lame class name, it's just an example): |
545 | |
546 | package Catalyst::Plugin::UriforUndefWarning; |
547 | use strict; |
548 | use Class::C3; |
549 | |
550 | sub uri_for { |
551 | my $c = shift; |
552 | my $uri = $c->next::method(@_); |
553 | $c->log->warn( |
554 | 'uri_for returned undef for:', |
555 | join(', ', @_), |
556 | ); |
557 | return $uri; |
558 | } |
559 | |
560 | 1; |
561 | |
562 | This would override Catalyst's C<uri_for> method and emit a C<warn> log |
563 | entry containing the arguments that led to the undefined return value. |
564 | |
565 | =head2 Factory components with COMPONENT() |
566 | |
567 | Every component inheriting from L<Catalyst::Component> contains a |
568 | C<COMPONENT> method. It is used on application startup by |
569 | C<setup_components> to instantiate the component object for the Catalyst |
570 | application. By default, this will merge the components own |
571 | C<config>uration with the application wide overrides and call the class' |
572 | C<new> method to return the component object. |
573 | |
574 | You can override this method and do and return whatever you want. |
575 | However, you should use L<Class::C3> to forward to the original |
576 | C<COMPONENT> method to merge the configuration of your component. |
577 | |
578 | Here is a stub C<COMPONENT> method: |
579 | |
580 | package CatalystX::Component::Foo; |
581 | use strict; |
582 | use base 'Catalyst::Component'; |
583 | |
584 | use Class::C3; |
585 | |
586 | sub COMPONENT { |
587 | my $class = shift; |
588 | my ($app_class, $config) = @_; |
589 | |
590 | # do things here before instantiation |
591 | my $obj = $self->next::method(@_); |
592 | # do things to object after instantiation |
593 | |
594 | return $object; |
595 | } |
596 | |
597 | The arguments are the class name of the component, the class name of |
598 | the application instantiating the component, and a hash reference |
599 | with the controller's configuration. |
600 | |
601 | You are free to re-bless the object, instantiate a whole other component |
602 | or really do anything compatible with Catalyst's expectations on a |
603 | component. |
604 | |
605 | For more information, please see |
606 | L<Catalyst::Component/"COMPONENT($c, $arguments)">. |
607 | |
608 | =head1 SEE ALSO |
609 | |
610 | L<Catalyst>, |
611 | L<Catalyst::Manual::Actions>, |
612 | L<Catalyst::Component> |
613 | |
614 | =head1 AUTHOR |
615 | |
616 | Robert Sedlacek C<rs@474.at> |
617 | |
618 | =head1 LICENSE AND COPYRIGHT |
619 | |
620 | This document is free, you can redistribute it and/or modify it under |
621 | the same terms as Perl itself. |
622 | |
623 | =cut |
624 | |