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