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=5bdac66b35c17fa897f4c383c7ae71d014a9da58;hb=1c34f703cbd82cddceea95593001a579e1d5f646;hpb=2c23b46d57d31a415a6676656ea86d26599af52a diff --git a/lib/Catalyst/Manual/Intro.pod b/lib/Catalyst/Manual/Intro.pod index 5bdac66..32e2ba1 100644 --- a/lib/Catalyst/Manual/Intro.pod +++ b/lib/Catalyst/Manual/Intro.pod @@ -417,90 +417,248 @@ 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 - - sub section :PathPart('section') :ChildOf('/') :Captures(1) { } - -ChildOf is a powerful way to handle canonical URIs of the form -C. Using this URI as an example, -in Controller::Root you can do the following: - - sub section_handler :PathPart('section') :ChildOf('/') :Captures(1) { - my ( $self, $c ) = @_; - $c->stash->{'section'} = - $c->Model('Sections')->find($c->req->captures->[0]); - } - - sub item_handler :PathPart('item') :ChildOf('/section') :Args(1) { - my ( $self, $c ) = @_; - $c->stash->{'item'} = - $c->stash->{'section'}->find_related('item',$c->args->[0]); +=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; } -The subroutine C matches the path segment "section" as -a child of "/". It then takes the next path segment, as referenced by -C<:Captures(1)>, and stashes it in the arrayref -C<$c-Ereq-Ecaptures>. Since there is also a child of this -handler, it also gets run, functioning in the same way. However, the -C subroutine has the C attribute which means this -particular routine will only run if there is exactly one argument. See -L below for more options. - -A parent action can be in any controller or namespace. - -Multiple actions can specify the same parent action in their C; -that is, one action can have multiple children. - -=item ChildOf('xyz') + # 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; -The action of the parent. For instance, if you have a method -C in the controller C, the action -would be C. For a Root handler this -would be '/'. For an action in the same controller namespace you can use -a relative name like C<:ChildOf('foo')>. - -=item PathPart('xyz') - -The name of this path section in the ChildOf tree mapping to the URI. If -you specify C<:PathPart> without arguments, it takes the name of the -action specifying the argument. For example, these two declarations -have the same effect: - - sub foo :PathPart('foo') :ChildOf('bar') :Args(1) { - ... + $c->response->body( join "
\n" => + $c->stash->{ message }, $c->stash->{ arg_sum } ); } -and - - sub foo :PathPart :ChildOf('bar') :Args(1) { - ... +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 } -The value can also contain a slash, for example: - - sub baz :PathPart('bar/baz') :ChildOf('/') :Captures(1) { - ... + 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 } -would be involved in matches on C paths. - -=item Captures(integer) + 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. + } -Will 'collapse' the next C path segments in the request URI and -push them into the arrayref C<$c-Ereq-Ecaptures>. An action -specifying C is thought to be used as target for C -specifications. Also see the C attribute below, which is used for -endpoints. +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>. -=item Args(int) +=back -The number of path segments to capture at the end of a request URI. This -B be included in your leaf nodes. You can use C for an -equivalent of the index action. Args with no parameters will capture -every postfixed segment into C<$c-Ereq-Eargs>. +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. -A specification of C is seen as endpoint in regard to an additional -C specification. +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)