1Catalyst::Manual::ExtenUdsienrgCCaotnatlryisbtu(t3e)d PeCraltaDloycsutm:e:nMtaantuiaoln::ExtendingCatalyst(3)
2
3
4

NAME

6       Catalyst::Manual::ExtendingCatalyst - Extending The Framework
7

DESCRIPTION

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

BEST PRACTICES

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

CONFIGURATION

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

IMPLEMENTATION

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

SEE ALSO

561       Catalyst, Catalyst::Manual::Actions, Catalyst::Component
562

AUTHOR

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)
Impressum