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