1Catalyst::DispatchType:U:sCehraiCnoendt(r3i)buted Perl DCoactuamleynstta:t:iDoinspatchType::Chained(3)
2
3
4
6 Catalyst::DispatchType::Chained - Path Part DispatchType
7
9 # root action - captures one argument after it
10 sub foo_setup : Chained('/') PathPart('foo') CaptureArgs(1) {
11 my ( $self, $c, $foo_arg ) = @_;
12 ...
13 }
14
15 # child action endpoint - takes one argument
16 sub bar : Chained('foo_setup') Args(1) {
17 my ( $self, $c, $bar_arg ) = @_;
18 ...
19 }
20
22 See "USAGE".
23
25 $self->list($c)
26
27 Debug output for Path Part dispatch points
28
29 $self->match( $c, $path )
30
31 Calls "recurse_match" to see if a chain matches the $path.
32
33 $self->recurse_match( $c, $parent, \@path_parts )
34
35 Recursive search for a matching chain.
36
37 $self->register( $c, $action )
38
39 Calls register_path for every Path attribute for the given $action.
40
41 $self->uri_for_action($action, $captures)
42
43 Get the URI part for the action, using $captures to fill the capturing
44 parts.
45
47 Introduction
48
49 The "Chained" attribute allows you to chain public path parts together
50 by their private names. A chain part's path can be specified with
51 "PathPart" and can be declared to expect an arbitrary number of argu‐
52 ments. The endpoint of the chain specifies how many arguments it gets
53 through the "Args" attribute. :Args(0) would be none at all, ":Args"
54 without an integer would be unlimited. The path parts that aren't end‐
55 points are using "CaptureArgs" to specify how many parameters they
56 expect to receive. As an example setup:
57
58 package MyApp::Controller::Greeting;
59 use base qw/ Catalyst::Controller /;
60
61 # this is the beginning of our chain
62 sub hello : PathPart('hello') Chained('/') CaptureArgs(1) {
63 my ( $self, $c, $integer ) = @_;
64 $c->stash->{ message } = "Hello ";
65 $c->stash->{ arg_sum } = $integer;
66 }
67
68 # this is our endpoint, because it has no :CaptureArgs
69 sub world : PathPart('world') Chained('hello') Args(1) {
70 my ( $self, $c, $integer ) = @_;
71 $c->stash->{ message } .= "World!";
72 $c->stash->{ arg_sum } += $integer;
73
74 $c->response->body( join "<br/>\n" =>
75 $c->stash->{ message }, $c->stash->{ arg_sum } );
76 }
77
78 The debug output provides a separate table for chained actions, showing
79 the whole chain as it would match and the actions it contains. Here's
80 an example of the startup output with our actions above:
81
82 ...
83 [debug] Loaded Path Part actions:
84 .-----------------------+------------------------------.
85 ⎪ Path Spec ⎪ Private ⎪
86 +-----------------------+------------------------------+
87 ⎪ /hello/*/world/* ⎪ /greeting/hello (1) ⎪
88 ⎪ ⎪ => /greeting/world ⎪
89 '-----------------------+------------------------------'
90 ...
91
92 As you can see, Catalyst only deals with chains as whole paths and
93 builds one for each endpoint, which are the actions with ":Chained" but
94 without ":CaptureArgs".
95
96 Let's assume this application gets a request at the path
97 "/hello/23/world/12". What happens then? First, Catalyst will dispatch
98 to the "hello" action and pass the value 23 as an argument to it after
99 the context. It does so because we have previously used :CaptureArgs(1)
100 to declare that it has one path part after itself as its argument. We
101 told Catalyst that this is the beginning of the chain by specifying
102 ":Chained('/')". Also note that instead of saying ":PathPart('hello')"
103 we could also just have said ":PathPart", as it defaults to the name of
104 the action.
105
106 After "hello" has run, Catalyst goes on to dispatch to the "world"
107 action. This is the last action to be called: Catalyst knows this is an
108 endpoint because we did not specify a ":CaptureArgs" attribute. Never‐
109 theless we specify that this action expects an argument, but at this
110 point we're using :Args(1) to do that. We could also have said ":Args"
111 or left it out altogether, which would mean this action would get all
112 arguments that are there. This action's ":Chained" attribute says
113 "hello" and tells Catalyst that the "hello" action in the current con‐
114 troller is its parent.
115
116 With this we have built a chain consisting of two public path parts.
117 "hello" captures one part of the path as its argument, and also speci‐
118 fies the path root as its parent. So this part is "/hello/$arg". The
119 next part is the endpoint "world", expecting one argument. It sums up
120 to the path part "world/$arg". This leads to a complete chain of
121 "/hello/$arg/world/$arg" which is matched against the requested paths.
122
123 This example application would, if run and called by e.g.
124 "/hello/23/world/12", set the stash value "message" to "Hello" and the
125 value "arg_sum" to "23". The "world" action would then append "World!"
126 to "message" and add 12 to the stash's "arg_sum" value. For the sake
127 of simplicity no view is shown. Instead we just put the values of the
128 stash into our body. So the output would look like:
129
130 Hello World!
131 35
132
133 And our test server would have given us this debugging output for the
134 request:
135
136 ...
137 [debug] "GET" request for "hello/23/world/12" from "127.0.0.1"
138 [debug] Path is "/greeting/world"
139 [debug] Arguments are "12"
140 [info] Request took 0.164113s (6.093/s)
141 .------------------------------------------+-----------.
142 ⎪ Action ⎪ Time ⎪
143 +------------------------------------------+-----------+
144 ⎪ /greeting/hello ⎪ 0.000029s ⎪
145 ⎪ /greeting/world ⎪ 0.000024s ⎪
146 '------------------------------------------+-----------'
147 ...
148
149 What would be common uses of this dispatch technique? It gives the pos‐
150 sibility to split up logic that contains steps that each depend on each
151 other. An example would be, for example, a wiki path like "/wiki/Foo‐
152 BarPage/rev/23/view". This chain can be easily built with these
153 actions:
154
155 sub wiki : PathPart('wiki') Chained('/') CaptureArgs(1) {
156 my ( $self, $c, $page_name ) = @_;
157 # load the page named $page_name and put the object
158 # into the stash
159 }
160
161 sub rev : PathPart('rev') Chained('wiki') CaptureArgs(1) {
162 my ( $self, $c, $revision_id ) = @_;
163 # use the page object in the stash to get at its
164 # revision with number $revision_id
165 }
166
167 sub view : PathPart Chained('rev') Args(0) {
168 my ( $self, $c ) = @_;
169 # display the revision in our stash. Another option
170 # would be to forward a compatible object to the action
171 # that displays the default wiki pages, unless we want
172 # a different interface here, for example restore
173 # functionality.
174 }
175
176 It would now be possible to add other endpoints, for example "restore"
177 to restore this specific revision as the current state.
178
179 You don't have to put all the chained actions in one controller. The
180 specification of the parent through ":Chained" also takes an absolute
181 action path as its argument. Just specify it with a leading "/".
182
183 If you want, for example, to have actions for the public paths
184 "/foo/12/edit" and "/foo/12", just specify two actions with ":Path‐
185 Part('foo')" and ":Chained('/')". The handler for the former path needs
186 a :CaptureArgs(1) attribute and a endpoint with ":PathPart('edit')" and
187 ":Chained('foo')". For the latter path give the action just a :Args(1)
188 to mark it as endpoint. This sums up to this debugging output:
189
190 ...
191 [debug] Loaded Path Part actions:
192 .-----------------------+------------------------------.
193 ⎪ Path Spec ⎪ Private ⎪
194 +-----------------------+------------------------------+
195 ⎪ /foo/* ⎪ /controller/foo_view ⎪
196 ⎪ /foo/*/edit ⎪ /controller/foo_load (1) ⎪
197 ⎪ ⎪ => /controller/edit ⎪
198 '-----------------------+------------------------------'
199 ...
200
201 Here's a more detailed specification of the attributes belonging to
202 ":Chained":
203
204 Attributes
205
206 PathPart
207 Sets the name of this part of the chain. If it is specified
208 without arguments, it takes the name of the action as default.
209 So basically "sub foo :PathPart" and "sub foo :PathPart('foo')"
210 are identical. This can also contain slashes to bind to a
211 deeper level. An action with "sub bar :PathPart('foo/bar')
212 :Chained('/')" would bind to "/foo/bar/...". If you don't spec‐
213 ify ":PathPart" it has the same effect as using ":PathPart", it
214 would default to the action name.
215
216 Chained Has to be specified for every child in the chain. Possible val‐
217 ues are absolute and relative private action paths, with the
218 relatives pointing to the current controller, or a single slash
219 "/" to tell Catalyst that this is the root of a chain. The
220 attribute ":Chained" without arguments also defaults to the "/"
221 behavior.
222
223 Because you can specify an absolute path to the parent action,
224 it doesn't matter to Catalyst where that parent is located. So,
225 if your design requests it, you can redispatch a chain through
226 any controller or namespace you want.
227
228 Another interesting possibility gives ":Chained('.')", which
229 chains itself to an action with the path of the current con‐
230 troller's namespace. For example:
231
232 # in MyApp::Controller::Foo
233 sub bar : Chained CaptureArgs(1) { ... }
234
235 # in MyApp::Controller::Foo::Bar
236 sub baz : Chained('.') Args(1) { ... }
237
238 This builds up a chain like "/bar/*/baz/*". The specification
239 of "." as the argument to Chained here chains the "baz" action
240 to an action with the path of the current controller namespace,
241 namely "/foo/bar". That action chains directly to "/", so the
242 "/bar/*/baz/*" chain comes out as the end product.
243
244 CaptureArgs
245 Must be specified for every part of the chain that is not an
246 endpoint. With this attribute Catalyst knows how many of the
247 following parts of the path (separated by "/") this action
248 wants to capture as its arguments. If it doesn't expect any,
249 just specify :CaptureArgs(0). The captures get passed to the
250 action's @_ right after the context, but you can also find them
251 as array references in "$c->request->captures->[$level]". The
252 $level is the level of the action in the chain that captured
253 the parts of the path.
254
255 An action that is part of a chain (that is, one that has a
256 ":Chained" attribute) but has no ":CaptureArgs" attribute is
257 treated by Catalyst as a chain end.
258
259 Args By default, endpoints receive the rest of the arguments in the
260 path. You can tell Catalyst through ":Args" explicitly how many
261 arguments your endpoint expects, just like you can with ":Cap‐
262 tureArgs". Note that this also affects whether this chain is
263 invoked on a request. A chain with an endpoint specifying one
264 argument will only match if exactly one argument exists in the
265 path.
266
267 You can specify an exact number of arguments like :Args(3),
268 including 0. If you just say ":Args" without any arguments, it
269 is the same as leaving it out altogether: The chain is matched
270 regardless of the number of path parts after the endpoint.
271
272 Just as with ":CaptureArgs", the arguments get passed to the
273 action in @_ after the context object. They can also be reached
274 through "$c->request->arguments".
275
276 Auto actions, dispatching and forwarding
277
278 Note that the list of "auto" actions called depends on the private path
279 of the endpoint of the chain, not on the chained actions way. The
280 "auto" actions will be run before the chain dispatching begins. In
281 every other aspect, "auto" actions behave as documented.
282
283 The "forward"ing to other actions does just what you would expect. But
284 if you "detach" out of a chain, the rest of the chain will not get
285 called after the "detach".
286
288 Matt S Trout <mst@shadowcatsystems.co.uk>
289
291 This program is free software, you can redistribute it and/or modify it
292 under the same terms as Perl itself.
293
294
295
296perl v5.8.8 2007-09-20Catalyst::DispatchType::Chained(3)