From: Ian Wells Date: Tue, 14 Apr 2009 22:42:13 +0000 (+0000) Subject: Revised action descriptions - should come in a more logical order and information... X-Git-Tag: v5.8005~170 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Manual.git;a=commitdiff_plain;h=d6ea2bcb7726541f21947bcc70a01de71773f2ea Revised action descriptions - should come in a more logical order and information added --- diff --git a/lib/Catalyst/Manual/Intro.pod b/lib/Catalyst/Manual/Intro.pod index 182b14a..1cea34f 100644 --- a/lib/Catalyst/Manual/Intro.pod +++ b/lib/Catalyst/Manual/Intro.pod @@ -1,4 +1,4 @@ -=head1 NAME +head1 NAME Catalyst::Manual::Intro - Introduction to Catalyst @@ -730,21 +730,83 @@ Catalyst-friendly session-handling tools. =head3 Actions -A Catalyst controller is defined by its actions. An action is a -subroutine with a special attribute. You've already seen some examples -of actions in this document. The URL (for example -http://localhost.3000/foo/bar) consists of two parts, the base -(http://localhost:3000/ in this example) and the path (foo/bar). Please -note that the trailing slash after the hostname[:port] always belongs to -base and not to the action. +You've already seen some examples of actions in this document: +subroutines with C<:Path> and C<:Local> attributes attached. +Here, we explain what actions are and how these attributes affect +what's happening. + +When Catalyst processes a webpage request, it looks for actions to +take that will deal with the incoming request and produce a response +such as a webpage. You create these actions for your application by +writing subroutines within your controller and marking them with +special attributes. The attributes, the namespace, and the function +name determine when Catalyst will call the subroutine. + +These action subroutines call certain functions to say what response +the webserver will give to the web request. They can also tell +Catalyst to run other actions on the request (one example of this is +called forwarding the request; this is discussed later). + +Action subroutines must have a special attribute on to show that they +are actions - as well as marking when to call them, this shows that +they take a specific set of arguments and behave in a specific way. +At startup, Catalyst looks for all the actions in controllers, +registers them and creates L objects describing +them. When requests come in, Catalyst chooses which actions should be +called to handle the request. + +(Occasionally, you might use the action objects directly, but in +general, when we talk about actions, we're talking about the +subroutines in your application that do things to process a request.) + +You can choose one of several attributes for action subroutines; these +specify which requests are processed by that subroutine. Catalyst +will look at the URL it is processing, and the actions that it has +found, and automatically call the actions it finds that match the +circumstances of the request. + +The URL (for example http://localhost.3000/foo/bar) consists of two +parts, the base, describing how to connect to the server +(http://localhost:3000/ in this example) and the path, which the +server uses to decide what to return (foo/bar). Please note that the +trailing slash after the hostname[:port] always belongs to base and +not to the path. Catalyst uses only the path part when trying to find +actions to process. + +Depending on the type of action used, the URLs may match a combination +of the controller namespace, the arguments passed to the action +attribute, and the name of the subroutine. =over 4 +=item * B + +The namespace is a modified form of the component's class (package) +name. This modified class name excludes the parts that have a +pre-defined meaning in Catalyst ("MyApp::Controller" in the above +example), replaces "::" with "/", and converts the name to lower case. +See L for a full explanation of the pre-defined meaning +of Catalyst component class names. + +=item * B + +Note that __PACKAGE__->config->{namespace} can be used to override the +current namespace when matching. So: + + package MyApp::Controller::Example; + +would normally use 'example' as its namespace for matching, but if +this is specially overridden with + + __PACKAGE__->config->{namespace}='thing'; + +it matches using the namespace 'thing' instead. + =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: +MyApp::Controller::Root, as created by the catalyst.pl script, will +typically contain actions which are called for the top level of the +application (e.g. http://localhost:3000/ ): package MyApp::Controller::Root; use base 'Catalyst::Controller'; @@ -758,107 +820,149 @@ this: } 1; + +The code + + __PACKAGE__->config->{namespace} = ''; + +makes the controller act as if its namespace is empty. As you'll see +below, an empty namespace makes many of the URL-matching attributes, +such as :Path, :Local and :Global matches, match at the start of the +URL path. + =back =head4 Action types -Catalyst supports several types of actions: +Catalyst supports several types of actions. These mainly correspond +to ways of matching a URL to an action subroutine. Internally, these +matching types are implemented by L-derived +classes; the documentation there can be helpful in seeing how they +work. + +They will all attempt to match the start of the path. The remainder +of the path is passed as arguments. =over 4 -=item * B (B actions) +=item * Namespace-prefixed (C<:Local>) + + package MyApp::Controller::My::Controller; + sub foo : Local { } + +Matches any URL beginning with> http://localhost:3000/my/controller/foo. The namespace and +subroutine name together determine the path. + +=item * Namespace-level (C<:Global>) + + package MyApp::Controller::Foo; + sub foo : Global { } + +Matches http://localhost:3000/foo - that is, the action is mapped +directly to the controller namespace, ignoring the function name. + +C<:Global> is equivalent C<:Local> one level higher in +the namespace. + + package MyApp::Controller::Root; + __PACKAGE__->config->{namespace}=''; + sub foo : Local { } + +Use whichever makes the most sense for your application. + +=item * Changing handler behaviour: adding arguments (C<:Args>) + +Args is not an action type per se, but an action modifier - it adds a +match restriction to any action it's provided to, additionally +requiring as many path parts as are specified for the action to be +matched. For example, in MyApp::Controller::Foo, + + sub bar :Local + +would match any URL starting /foo/bar. To restrict this you can do + + sub bar :Local :Args(1) + +to only match URLs starting /foo/bar/* - with one additional path +element required after 'bar'. + +=item * Literal match (C<:Path>) + +C actions match things starting with a precise specified path, +and nothing else. + +C actions without a leading forward slash match a specified path +relative to their current namespace. This example matches URLs +starting http://localhost:3000/my/controller/foo/bar : package MyApp::Controller::My::Controller; sub bar : Path('foo/bar') { } -Literal C actions will act relative to their current -namespace. The above example matches only -http://localhost:3000/my/controller/foo/bar. If you start your path with -a forward slash, it will match from the root. Example: +C actions B a leading slash ignore their namespace, and +match from the start of the URL path. Example: package MyApp::Controller::My::Controller; sub bar : Path('/foo/bar') { } -Matches only http://localhost:3000/foo/bar. +This matches URLs beginning http://localhost:3000/foo/bar. + +Empty C definitions match on the namespace only, exactly like +C<:Global>. package MyApp::Controller::My::Controller; sub bar : Path { } -By leaving the C definition empty, it will match on the namespace -root. The above code matches http://localhost:3000/my/controller. +The above code matches http://localhost:3000/my/controller. + +Actions with the C<:Local> attribute are similarly equivalent to +C<:Path('action_name')>: -=item * B + sub foo : Local { } +is equivalent to + + sub foo : Path('foo') { } + +=item * Pattern-match (C<:Regex> and C<:LocalRegex>) + + package MyApp::Controller::My::Controller; sub bar : Regex('^item(\d+)/order(\d+)$') { } -Matches any URL that matches the pattern in the action key, e.g. +This matches any URL that matches the pattern in the action key, e.g. http://localhost:3000/item23/order42. The '' around the regexp is optional, but perltidy likes it. :) -Regex matches act globally, i.e. without reference to the namespace from -which it is called, so that a C method in the -C namespace won't match any -form of C, C, C, or C unless you -explicitly put this in the regex. To achieve the above, you should -consider using a C action. - -=item * B +C<:Regex> matches act globally, i.e. without reference to the namespace +from which they are called. So the above will B match +http://localhost:3000/my/controller/item23/order42 - use a +C<:LocalRegex> action instead. + package MyApp::Controller::My::Controller; sub bar : LocalRegex('^widget(\d+)$') { } -LocalRegex actions act locally. If you were to use C in -C, the above example would match urls like -http://localhost:3000/catalog/widget23. +C<:LocalRegex> actions act locally, i.e. the namespace is matched +first. The above example would match urls like +http://localhost:3000/my/controller/widget23. -If you omit the "C<^>" from your regex, then it will match any depth -from the controller and not immediately off of the controller name. The -following example differs from the above code in that it will match -http://localhost:3000/catalog/foo/widget23 as well. +If you omit the "C<^>" from either sort of regex, then it will match any depth +from the base path: package MyApp::Controller::Catalog; sub bar : LocalRegex('widget(\d+)$') { } -For both LocalRegex and Regex actions, if you use capturing parentheses -to extract values within the matching URL, those values are available in -the C<$c-Ereq-Ecaptures> array. In the above example, "widget23" -would capture "23" in the above example, and -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. +This differs from the previous example in that it will match +http://localhost:3000/my/controller/foo/widget23 - and a number of +other paths. -=item * B (B) +For both C<:LocalRegex> and C<:Regex> actions, if you use capturing +parentheses to extract values within the matching URL, those values +are available in the C<$c-Ereq-Ecaptures> array. In the above +example, "widget23" would capture "23" in the above example, and +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. - package MyApp::Controller::Foo; - sub foo : Global { } - -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) - - package MyApp::Controller::My::Controller; - sub foo : Local { } - -Matches http://localhost:3000/my/controller/foo. - -This action type indicates that the matching URL must be prefixed with a -modified form of the component's class (package) name. This modified -class name excludes the parts that have a pre-defined meaning in -Catalyst ("MyApp::Controller" in the above example), replaces "::" with -"/", and converts the name to lower case. See L for a full -explanation of the pre-defined meaning of Catalyst component class -names. - -Note that actions with the C< :Local > attribute are equivalent to the -<:Path('action_name') > so sub foo : Local { } is equivalent to - - - sub foo : Path('foo') { } - -=item * B +=item * Chained handlers (C<:Chained>) Catalyst also provides a method to build and dispatch chains of actions, like @@ -873,67 +977,66 @@ like ... } -to handle a C path. For further information about this -dispatch type, please see L. +to handle a C path. Matching actions are called +one after another - C gets called and handed one path +element, then C gets called with another one. For further +information about this dispatch type, please see +L. =item * B sub foo : Private { } -Matches no URL, and cannot be executed by requesting a URL that -corresponds to the action key. Catalyst's :Private attribute is -exclusive and doesn't work with other attributes (so will not work -combined with Path or Chained attributes). With the exception of the -C< index >, C< auto > and C< default > actions, Private actions can -only be executed from inside a Catalyst application, by calling the -C or C methods: +This will never match a URL - it provides a private action which can +be called programmatically from within Catalyst, but is never called +automatically due to the URL being requested. + +Catalyst's C<:Private> attribute is exclusive and doesn't work with other +attributes (so will not work combined with C<:Path> or C<:Chained> +attributes, for instance). + +Private actions can only be executed explicitly from inside a Catalyst +application. You might do this in your controllers by calling +catalyst methods such as C or C to fire them: $c->forward('foo'); # or $c->detach('foo'); -See L for a full explanation of C. Note that, as -discussed there, when forwarding from another component, you must use -the absolute path to the method, so that a private C method in your -C controller must, if called -from elsewhere, be reached with +See L for a full explanation of how you can pass +requests on to other actions. Note that, as discussed there, when +forwarding from another component, you must use the absolute path to +the method, so that a private C method in your +C controller must, if +called from elsewhere, be reached with C<$c-Eforward('/catalog/order/process/bar')>. -=item * B - -Args is not an action type per se, but an action modifier - it adds a -match restriction to any action it's provided to, requiring only as many -path parts as are specified for the action to be valid - for example in -MyApp::Controller::Foo, - - sub bar :Local - -would match any URL starting /foo/bar/. To restrict this you can do - - sub bar :Local :Args(1) - -to only match /foo/bar/*/ - =back -B After seeing these examples, you probably wonder what the point -is of defining names for regex and path actions. Every public action is -also a private one, so you have one unified way of addressing components -in your Cs. +B After seeing these examples, you probably wonder what the +point is of defining subroutine names for regex and path +actions. However, every public action is also a private one with a +path corresponding to its namespace and subroutine name, so you have +one unified way of addressing components in your Cs. + +=head4 Built-in special actions -=head4 Built-in Private Actions +If present, the special actions C< index >, C< auto >, C, +C and C< default > are called at certain points in the request +cycle. In response to specific application states, Catalyst will automatically -call these built-in private actions in your application class: +call these built-in actions in your application class: =over 4 =item * B -Called when no other action matches. Could be used, for example, for -displaying a generic frontpage for the main app, or an error page for -individual controllers. B: in older Catalyst applications you -will see C which is roughly speaking equivalent. +This is called when no other action matches. It could be used, for +example, for displaying a generic frontpage for the main app, or an +error page for individual controllers. B: in older Catalyst +applications you will see C which is roughly +speaking equivalent. =item * B @@ -949,12 +1052,25 @@ roughly speaking equivalent. =item * B -Called at the beginning of a request, before any matching actions are -called. +Called at the beginning of a request, once the controller that will +run has been identified, but before any URL-matching actions are +called. Catalyst will call the C function in the controller +which contains the action matching the URL. =item * B -Called at the end of a request, after all matching actions are called. +Called at the end of a request, after all URL-matching actions are called. +Catalyst will call the C function in the controller +which contains the action matching the URL. + +=item * B + +In addition to the normal built-in actions, you have a special action +for making chains, C. C actions will be run after any +C, but before your URL-matching action is processed. Unlike the other +built-ins, multiple C actions can be called; they will be +called in turn, starting with the application class and going through +to the most specific class. =back @@ -963,30 +1079,21 @@ Called at the end of a request, after all matching actions are called. package MyApp::Controller::Foo; sub begin : Private { } sub default : Path { } - sub auto : Private { } - -You can define built-in private actions within your controllers as -well. The actions will override the ones in less-specific controllers, -or your application class. In other words, for each of the three -built-in private actions, only one will be run in any request -cycle. Thus, if C exists, it will be -run in place of C if you're in the C namespace, -and C would override this in + sub end : Path { } + +You can define built-in actions within your controllers as well as on +your application class. In other words, for each of the three built-in +actions above, only one will be run in any request cycle. Thus, if +C exists, it will be run in place +of C if you're in the C namespace, and +C would override this in turn. -=over 4 - -=item * B - -In addition to the normal built-in actions, you have a special action -for making chains, C. Such C actions will be run after any -C, but before your action is processed. Unlike the other -built-ins, C actions I override each other; they will be -called in turn, starting with the application class and going through to -the I specific class. I. + sub auto : Private { } -=back +C, however, doesn't override like this: providing they exist, +C, C and +C would be called in turn. Here are some examples of the order in which the various built-ins would be called: @@ -1021,27 +1128,33 @@ like this: false MyApp::Controller::Foo::Bar::begin + MyApp::Controller::Foo::auto # returns false, skips some calls: + # MyApp::Controller::Foo::Bar::auto - never called + # MyApp::Controller::Foo::Bar::foo - never called MyApp::Controller::Foo::Bar::end +You can also C in the auto action; in that case, the request will +go straight to the finalize stage, without processing further +actions. So in the above example, C +is skipped as well. + =back -An example of why one might use this is an authentication action: you -could set up a C action to handle authentication in your +An example of why one might use C is an authentication action: +you could set up a C action to handle authentication in your application class (which will always be called first), and if -authentication fails, returning 0 would skip any remaining methods -for that URL. +authentication fails, returning 0 would skip any remaining methods for +that URL. B Looking at it another way, C actions have to return a -true value to continue processing! You can also C in the auto -action; in that case, the request will go straight to the finalize -stage, without processing further actions. +true value to continue processing! =head4 URL Path Handling -You can pass variable arguments as part of the URL path, separated with -forward slashes (/). If the action is a Regex or LocalRegex, the '$' anchor -must be used. For example, suppose you want to handle C, -where C<$bar> and C<$baz> may vary: +You can pass arguments as part of the URL path, separated with forward +slashes (/). If the action is a Regex or LocalRegex, the '$' anchor +must be used. For example, suppose you want to handle +C, where C<$bar> and C<$baz> may vary: sub foo : Regex('^foo$') { my ($self, $context, $bar, $baz) = @_; } @@ -1050,7 +1163,7 @@ But what if you also defined actions for C and C? sub boo : Path('foo/boo') { .. } sub hoo : Path('foo/boo/hoo') { .. } -Catalyst matches actions in most specific to least specific order: +Catalyst matches actions in most specific to least specific order - that is, whatever matches the most pieces of the path wins: /foo/boo/hoo /foo/boo @@ -1060,10 +1173,27 @@ So Catalyst would never mistakenly dispatch the first two URLs to the '^foo$' action. If a Regex or LocalRegex action doesn't use the '$' anchor, the action will -still match a URL containing arguments, however the arguments won't be -available via C<@_>. +still match a URL containing arguments; however the arguments won't be +available via C<@_>, because the Regex will 'eat' them. + +Beware! If you write two matchers, that match the same path, with the +same specificity (that is, they match the same quantity of the path), +there's no guarantee which will actually get called. Non-regex +matchers get tried first, followed by regex ones, but if you have, for +instance: + + package MyApp::Controller::Root; + + sub match1 :Path('/a/b') { } + + package MyApp::Controller::A; + + sub b :Local { } # Matches /a/b + +then Catalyst will call the one it finds first. In summary, Don't Do +This. -=head4 Parameter Processing +=head4 Query Parameter Processing Parameters passed in the URL query string are handled with methods in the L class. The C method is functionally