From: Matt S Trout Date: Thu, 22 Jun 2006 14:52:42 +0000 (+0000) Subject: New Chained documentation for Intro X-Git-Tag: 5.7099_04~484 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=commitdiff_plain;h=e5c7adea1c2115b30ff57b2f6e6035020c5216d3 New Chained documentation for Intro r10074@cain (orig r4404): phaylon | 2006-06-18 14:35:47 +0000 --- diff --git a/lib/Catalyst/Manual/Intro.pod b/lib/Catalyst/Manual/Intro.pod index 21a701d..3eb9316 100644 --- a/lib/Catalyst/Manual/Intro.pod +++ b/lib/Catalyst/Manual/Intro.pod @@ -419,88 +419,237 @@ L below. =item * B - sub section :PathPart('section') :Chained('/') :Captures(1) { } - -Chained 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') :Chained('/') :Captures(1) { - my ( $self, $c ) = @_; - $c->stash->{'section'} = - $c->Model('Sections')->find($c->req->captures->[0]); - } - - sub item_handler :PathPart('item') :Chained('/section_handler') :Args(1) { - my ( $self, $c ) = @_; - $c->stash->{'item'} = - $c->stash->{'section'}->find_related('item',$c->args->[0]); +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('/') Captures(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 Chained('xyz') - -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<:Chained('foo')>. + # this is our endpoint, because it has no :Captures + sub world : PathPart('world') Chained('hello') Args(1) { + my ( $self, $c, $integer ) = @_; + $c->stash->{ message } .= "World!"; + $c->stash->{ arg_sum } += $integer; -=item PathPart('xyz') - -The name of this path section in the Chained 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') :Chained('bar') :Args(1) { - ... + $c->response->body( join "
\n" => + $c->stash->{ message }, $c->stash->{ arg_sum } ); } -and - - sub foo :PathPart :Chained('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<:Captures>. + +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<:Captures(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<:Captures> 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('/') Captures(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') :Chained('/') :Captures(1) { - ... + sub rev : PathPart('rev') Chained('wiki') Captures(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) - -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. - -=item Args(int) + 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. + } -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>. +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<:Captures(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 Captures(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 Captures + +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<:Captures(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-Esnippets-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<:Captures> 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<:Captures>. 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<:Captures>, the arguments get passed to the action in +C<@_> after the context object. They can also be reached through +C<$c-Erequest-Earguments>. -A specification of C is seen as endpoint in regard to an additional -C specification. +=back =item * B (B)