X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst%2FManual%2FIntro.pod;h=32e2ba18e04088e87789341c325c2075c80b2393;hp=002640263af9383b1748dd911826be1b8616fd99;hb=1c34f703cbd82cddceea95593001a579e1d5f646;hpb=baf5120b807f1be08f251e0916046ccec0cdc2d4 diff --git a/lib/Catalyst/Manual/Intro.pod b/lib/Catalyst/Manual/Intro.pod index 0026402..32e2ba1 100644 --- a/lib/Catalyst/Manual/Intro.pod +++ b/lib/Catalyst/Manual/Intro.pod @@ -8,12 +8,22 @@ This is a brief introduction to Catalyst. It explains the most important features of how Catalyst works and shows how to get a simple application up and running quickly. For an introduction (without code) to Catalyst itself, and why you should be using it, see L. +For a systematic step-by-step introduction to writing an application +with Catalyst, see L. =head2 What is Catalyst? Catalyst is an elegant web application framework, extremely flexible yet extremely simple. It's similar to Ruby on Rails, Spring (Java), and -L, upon which it was originally based. +L, upon which it was originally based. Its most important +design philosphy is to provide easy access to all the tools you need to +develop web applications, with few restrictions on how you need to use +these tools. Under Catalyst, it is always possible to do things in a +different way. However, this does mean that it is always possible to do +things in a different way. Other web frameworks are simpler to use and +easy to get up and running, but achieve this by locking the programmer +into a single set of tools. Catalyst's emphasis on flexibility means +that you have to think more to use it. We view this as a feature. =head3 MVC @@ -32,7 +42,7 @@ well-known Perl modules you may want to use for each. =item * B Access and modify content (data). L, L, -L, L... +L, L... =item * B @@ -46,11 +56,11 @@ control. Catalyst itself! =back -If you're unfamiliar with MVC and design patterns, you may want to check -out the original book on the subject, I, by Gamma, -Helm, Johnson, and Vlissides, also known as the Gang of Four (GoF). -Many, many web application frameworks are based on MVC, including all -those listed above. +If you're unfamiliar with MVC and design patterns, you may want to +check out the original book on the subject, I, by +Gamma, Helm, Johnson, and Vlissides, also known as the Gang of Four +(GoF). Many, many web application frameworks are based on MVC, which +is becoming a popular design method for web applications. =head3 Flexibility @@ -184,8 +194,7 @@ and other parts of a Catalyst application. In addition to the Model, View, and Controller components, there's a single class that represents your application itself. This is where you -configure your application, load plugins, define application-wide -actions, and extend Catalyst. +configure your application, load plugins, and extend Catalyst. package MyApp; @@ -198,16 +207,12 @@ actions, and extend Catalyst. # You can put anything else you want in here: my_configuration_variable => 'something', ); - - sub default : Private { - my ( $self, $context ) = @_; - $context->response->body('Catalyst rocks!'); - } - 1; -For most applications, Catalyst requires you to define only one config -parameter: +In older versions of Catalyst, the application class was where you put +global actions. However, as of version 5.66, the recommended practice is +to place such actions in a special Root controller (see #####, below), +to avoid namespace collisions. =over 4 @@ -223,6 +228,8 @@ location. You can define as many parameters as you want for plugins or whatever you need. You can access them anywhere in your application via C<$context-Econfig-E{$param_name}>. +###### We need a short section on configuration here. + =head3 Context Catalyst automatically blesses a Context object into your application @@ -288,6 +295,10 @@ information. $c->stash $c->stash->{foo} = 'bar'; + $c->stash->{baz} = {baz => 'qox'}; + $c->stash->{fred} = [qw/ wilma pebbles/]; + +and so on. =back @@ -319,6 +330,29 @@ http://localhost.3000/foo/bar) consists of two parts, the base note that the trailing slash after the hostname[:port] always belongs to base and not to the action. +=over 4 + +=item * B + +Actions which are called at the root level of the application +(e.g. http://localhost:3000/ ) go in MyApp::Controller::Root, like +this: + + package MyApp::Controller::Root; + use base 'Catalyst::Controller'; + # Sets the actions in this controller to be registered with no prefix + # so they function identically to actions created in MyApp.pm + __PACKAGE__->config->{namespace} = ''; + sub default : Private { + my ( $self, $context ) = @_; + $context->response->body('Catalyst rocks!'); + } + 1; + +=back + +=head4 Action types + Catalyst supports several types of actions: =over 4 @@ -383,13 +417,260 @@ C<$c-Ereq-Ecaptures-E[0]> would be "23". If you want to pass arguments at the end of your URL, you must use regex action keys. See L below. +=item * B + +The C attribute allows you to chain public path parts together +by their private names. A chain part's path can be specified with C +and can be declared to expect an arbitrary number of arguments. The endpoint +of the chain specifies how many arguments it gets through the C +attribute. C<:Args(0)> would be none at all, C<:Args> without an integer +would be unlimited. The path parts that aren't endpoints are using +C to specify how many parameters they expect to receive. As an +example setup: + + package MyApp::Controller::Greeting; + use base qw/ Catalyst::Controller /; + + # this is the beginning of our chain + sub hello : PathPart('hello') Chained('/') CaptureArgs(1) { + my ( $self, $c, $integer ) = @_; + $c->stash->{ message } = "Hello "; + $c->stash->{ arg_sum } = $integer; + } + + # this is our endpoint, because it has no :CaptureArgs + sub world : PathPart('world') Chained('hello') Args(1) { + my ( $self, $c, $integer ) = @_; + $c->stash->{ message } .= "World!"; + $c->stash->{ arg_sum } += $integer; + + $c->response->body( join "
\n" => + $c->stash->{ message }, $c->stash->{ arg_sum } ); + } + +The debug output provides a separate table for chained actions, showing +the whole chain as it would match and the actions it contains. Here's +an example of the startup output with our actions above: + + ... + [debug] Loaded Path Part actions: + .-----------------------+------------------------------. + | Path Spec | Private | + +-----------------------+------------------------------+ + | /hello/*/world/* | /greeting/hello (1) | + | | => /greeting/world | + '-----------------------+------------------------------' + ... + +As you can see, Catalyst only deals with chains as whole path and +builds one for each endpoint, which are the actions with C<:Chained> +but without C<:CaptureArgs>. + +Let's assume this application gets a request at the path +C, what happens then? First, Catalyst will dispatch +to the C action and pass the value C<23> as argument to it after +the context. It does so because we have previously used C<:CaptureArgs(1)> +to declare that it has one path part after itself as it's argument. We +told Catalyst that this is the beginning of the chain by specifying +C<:Chained('/')>. Also note that instead of saying C<:PathPart('hello')> +we could also just have said C<:PathPart>, as it defaults to the name of +the action. + +After C has run, Catalyst goes on to dispatch to the C +action. This is the last action to be called, as Catalyst knows this +is an endpoint because we specified no C<:CaptureArgs> attribute. Nevertheless +we specify that this action expects an argument, but at this point we're +using C<:Args(1)> to do that. We could also have said C<:Args> or leave +it out alltogether, which would mean this action gets all arguments that +are there. This action's C<:Chained> attribute says C and tells +Catalyst that the C action in the current controller is it's +parent. + +With this we have built a chain consisting of two public path parts. +C captures one part of the path as it's argument, and also specifies +the path root as it's parent. So this part is C. The next part +is the endpoint C, expecting one argument. It sums up to the path +part C. This leads to a complete chain of +C which is matched against the requested paths. + +This example application would, if run and called by e.g. +C, set the stash value C to C and +the value C to C<23>. The C action would then append +C to C and add C<12> to the stash's C value. +For the sake of simplicity no view is shown. Instead we just put the +values of the stash into our body. So the output would look like: + + Hello World! + 35 + +And our test server would've given us this debugging output for the +request: + + ... + [debug] "GET" request for "hello/23/world/12" from "127.0.0.1" + [debug] Path is "/greeting/world" + [debug] Arguments are "12" + [info] Request took 0.164113s (6.093/s) + .------------------------------------------+-----------. + | Action | Time | + +------------------------------------------+-----------+ + | /greeting/hello | 0.000029s | + | /greeting/world | 0.000024s | + '------------------------------------------+-----------' + ... + +What would be common usecases of this dispatching technique? It gives the +possibility to split up logic that contains steps that each depend on each +other. An example would be, for example, a wiki path like +C. This chain can be easily built with +these actions: + + sub wiki : PathPart('wiki') Chained('/') CaptureArgs(1) { + my ( $self, $c, $page_name ) = @_; + # load the page named $page_name and put the object + # into the stash + } + + sub rev : PathPart('rev') Chained('wiki') CaptureArgs(1) { + my ( $self, $c, $revision_id ) = @_; + # use the page object in the stash to get at it's + # revision with number $revision_id + } + + sub view : PathPart Chained('rev') Args(0) { + my ( $self, $c ) = @_; + # display the revision in our stash. An other option + # would be to forward a compatible object to the action + # that displays the default wiki pages, unless we want + # a different interface here, for example restore + # functionality. + } + +It would now be possible to add other endpoints. For example C to +restore this specific revision as current state. + +Also, you of course don't have to put all the chained actions in one +controller. The specification of the parent through C<:Chained> also takes +an absolute action path as it's argument. Just specify it with a leading +C. + +If you want, for example, to have actions for the public paths +C and C, just specify two actions with +C<:PathPart('foo')> and C<:Chained('/')>. The handler for the former +path needs a C<:CaptureArgs(1)> attribute and a endpoint with +C<:PathPart('edit')> and C<:Chained('foo')>. For the latter path give +the action just a C<:Args(1)> to mark it as endpoint. This sums up to +this debugging output: + + ... + [debug] Loaded Path Part actions: + .-----------------------+------------------------------. + | Path Spec | Private | + +-----------------------+------------------------------+ + | /foo/* | /controller/foo_view | + | /foo/*/edit | /controller/foo_load (1) | + | | => /controller/edit | + '-----------------------+------------------------------' + ... + +Here's a more detailed specification of the attributes belonging to +C<:Chained>: + +=over 8 + +=item PathPart + +Sets the name of this part of the chain. If it is specified without +arguments, it takes the name of the action as default. So basically +C and C are identical. +This can also contain slashes to bind to a deeper level. An action +with C would bind to +C. If you don't specify C<:PathPart> it has the same +effect as using C<:PathPart>, it would default to the action name. + +=item Chained + +Has to be specified for every child in the chain. Possible values are +absolute and relative private action paths, with the relatives pointing +to the current controller, or a single slash C to tell Catalyst that +this is the root of a chain. The attribute C<:Chained> without aguments +also defaults to the C behaviour. + +Due to the fact that you can specify an absolute path to the parent +action, it doesn't matter to Catalyst where that parent is located. So, +if your design requests it, you can redispatch a chain through every +controller or namespace you want. + +Another interesting possibility gives C<:Chained('.')>, which chains +itself to an action with the path of the current controllers namespace. +For example: + + # in MyApp::Controller::Foo + sub bar : Chained CaptureArgs(1) { ... } + + # in MyApp::Controller::Foo::Bar + sub baz : Chained('.') Args(1) { ... } + +This builds up a chain like C. The specification of C<.> +as argument to Chained here chains the C action to an action with +the path of the current controller namespace, namely C. That +action chains directly to C, so the above chain comes out as end +product. + +=item CaptureArgs + +Also has to be specified for every part of the chain that is not an +endpoint. With this attribute Catalyst knows how many of the following +parts of the path (separated by C) this action wants to captures as +it's arguments. If it doesn't expect any, just specify C<:CaptureArgs(0)>. +The captures get passed to the action's C<@_> right after the context, +but you can also find them as array reference in +C<$c-Erequest-Ecaptures-E[$level]>. The C<$level> is the +level of the action in the chain that captured the parts of the path. + +An action that is part of a chain (read: that has a C<:Chained> attribute) +but has no C<:CaptureArgs> attribute is treated by Catalyst as a chain end. + +=item Args + +By default, endpoints receive the rest of the arguments in the path. You +can tell Catalyst through C<:Args> explicitly how many arguments your +endpoint expects, just like you can with C<:CaptureArgs>. Note that this +also influences if this chain is invoked on a request. A chain with an +endpoint specifying one argument will only match if exactly one argument +exists in the path. + +You can specify an exact number of arguments like C<:Args(3)>, including +C<0>. If you just say C<:Args> without any arguments, it is the same as +leaving it out alltogether: The chain is matched independent of the number +of path parts after the endpoint. + +Just like with C<:CaptureArgs>, the arguments get passed to the action in +C<@_> after the context object. They can also be reached through +C<$c-Erequest-Earguments>. + +=back + +Note that the list of C actions called depends on the private path +of the endpoint of the chain, not on the chained actions way. The C +actions will be run before the chain dispatching begins. In every other +aspect, C actions behave as documented. + +The Cing to other actions does just what you would expect. But if +you C out of a chain, the rest of the chain will not get called +after the C returned. + =item * B (B) - package MyApp; + package MyApp::Controller::Foo; sub foo : Global { } -Matches http://localhost:3000/foo. The function name is mapped directly -to the application base. +Matches http://localhost:3000/foo. The function name is mapped +directly to the application base. You can provide an equivalent +function in this case by doing the following: + + package MyApp::Controller::Root + sub foo : Local { } =item * B (B) @@ -632,10 +913,9 @@ debugging enabled). $c->res->body( $c->stash->{message} ); } -A C does not create a new request, so your request -object (C<$c-Ereq>) will remain unchanged. This is a -key difference between using C and issuing a -redirect. +A C does not create a new request, so your request object +(C<$c-Ereq>) will remain unchanged. This is a key difference between +using C and issuing a redirect. You can pass new arguments to a C by adding them in an anonymous array. In this case C<$c-Ereq-Eargs> @@ -717,7 +997,7 @@ You don't have to C or otherwise register Models, Views, and Controllers. Catalyst automatically discovers and instantiates them when you call C in the main application. All you need to do is put them in directories named for each Component type. Notice that you -can use some very terse aliases for each one. +can use a terse alias for each one. =over 4 @@ -735,6 +1015,11 @@ can use some very terse aliases for each one. =back +In older versions of Catalyst, the recommended practice (and the one +automatically created by helper scripts) was to name the directories +C, C, and C. Though these still work, we now recommend +the use of the full names. + =head4 Views To show how to define views, we'll use an already-existing base class for the @@ -878,26 +1163,150 @@ application. package MyApp::Controller::Login; - sub sign-in : Local { } - sub new-password : Local { } - sub sign-out : Local { } + use base qw/Catalyst::Controller/; + + sub sign_in : Path("sign-in") { } + sub new_password : Path("new-password") { } + sub sign_out : Path("sign-out") { } package MyApp::Controller::Catalog; + use base qw/Catalyst::Controller/; + sub view : Local { } sub list : Local { } package MyApp::Controller::Cart; + use base qw/Catalyst::Controller/; + sub add : Local { } sub update : Local { } sub order : Local { } +Note that you can also supply attributes via the Controller's config so long +as you have at least one attribute on a subref to be exported (:Action is +commonly used for this) - for example the following is equivalent to the same +controller above + + package MyApp::Controller::Login; + + use base qw/Catalyst::Controller/; + + __PACKAGE__->config( + actions => { + 'sign_in' => { Path => 'sign-in' }, + 'new_password' => { Path => 'new-password' }, + 'sign_out' => { Path => 'sign-out' }, + }, + ); + + sub sign_in : Action { } + sub new_password : Action { } + sub sign_out : Action { } + +=head3 Models + +Models are providers of data. This data could come from anywhere - a search +engine index, a database table, etc. Typically the data source does not have +much to do with web applications or Catalyst - it could be used to write an +offline report generator or a command line tool just the same. + +The common approach to writing a Catalyst-style model for your application is +wrapping a generic model (e.g. L, a bunch of XMLs, or +anything really) with an object that contains configuration data, convenience +methods, and so forth. + +#### editor: move this part to =head3 Components somehow, right after this +#### section - this will require deeply rephrasing this paragraph. + +Technically, within Catalyst a model is a B - an instance of the +model's class belonging to the application. It is important to stress that the +lifetime of these objects is per application, not per request. + +While the model base class (L) provides things like C +and stuff to better integrate the model into the application, sometimes this is +not enough, and the model requires access to C<$c> itself. + +Situations where this need might arise include: + +=over 4 + +=item * + +Interacting with another model + +=item * + +Using per-request data to control behavior + +=item * + +Using plugins in (for example L). + +=back + +From a style perspective usually it's bad to make your model "too smart" +about things - it should worry about business logic and leave the +integration details to the controllers. If, however, you find that it +does not make sense at all to use an auxillary controller around the +model, and the model's need to access C<$c> cannot be sidestepped, there +exists a power tool called C. + +#### editor note: this part is "generic" - it also applies to views and +#### controllers. + +=head3 ACCEPT_CONTEXT + +Whenever you call $c->component("Foo") you get back an object - the +instance of the model. If the component supports the C +method instead of returning the model itself, the return value of C<< +$model->ACCEPT_CONTEXT( $c ) >> will be used. + +This means that whenever your model/view/controller needs to talk to C<$c> it +gets a chance to do this when it's needed. + +A typical C method will either clone the model and return one +with the context object set, or it will return a thin wrapper that contains +C<$c> and delegates to the per-application model object. + +A typical C method could look like this: + + sub ACCEPT_CONTEXT { + my ( $self, $c, @extra_arguments ) = @_; + bless { %$self, c => $c }, ref($self); + } + +effectively treating $self as a B that gets a new parameter. +C<@extra_arguments> comes from any trailing arguments to +C<< $c->component( $bah, @extra_arguments ) >> (or C<< $c->model(...) >>, +C<< $c->view(...) >> etc). + +The life time of this value is B, and not per request. To make this +per request you can use the following technique: + +Add a field to C<$c>, like C. Then write your +C method to look like this: + + sub ACCEPT_CONTEXT { + my ( $self, $c ) = @_; + + if ( my $per_request = $c->my_model_instance ) { + return $per_request; + } else { + my $new_instance = bless { %$self, c => $c }, ref($self); + Scalar::Util::weaken($new_instance->{c}); # or we have a circular reference + $c->my_model_instance( $new_instance ); + return $new_instance; + } + } + + =head3 Testing -Catalyst has a built-in http server for testing! (Later, you can easily -use a more powerful server, e.g. Apache/mod_perl, in a production -environment.) +Catalyst has a built-in http server for testing. (Later, you can easily +use a more powerful server, e.g. Apache/mod_perl or FastCGI, in a +production environment.) Start your application on the command line... @@ -929,6 +1338,8 @@ David Naughton, C Marcus Ramberg, C Jesse Sheidlower, C Danijel Milicevic, C +Kieren Diment, C +Yuval Kogman, C =head1 COPYRIGHT