Fix spelling errors and update spelling exceptions
[catagits/Catalyst-Runtime.git] / lib / Catalyst / RouteMatching.pod
CommitLineData
480d94b5 1=encoding UTF-8
2
3=head1 Name
4
5Catalyst::RouteMatching - How Catalyst maps an incoming URL to actions in controllers.
6
7=head1 Description
8
9This is a WIP document intended to help people understand the logic that L<Catalyst>
10uses to determine how to match in incoming request to an action (or action chain)
11in a controller.
12
2234c98a 13=head2 Request to Controller/Action Matching
14
15L<Catalyst> maps requests to action using a 'longest path wins' approach. That means
16that if the request is '/foo/bar/baz' That means the action 'baz' matches:
17
18 package MyApp::Controller::Foo;
19
20 use Moose;
21 use MooseX::MethodAttributes
22
23 extends 'Catalyst::Controller';
24
25 sub bar :Path('bar') Args(1) { ...}
26 sub baz :Path('bar/baz') Args(0) { ... }
27
79fb8f95 28Path length matches take precedence over all other types of matches (included HTTP
2234c98a 29Method, Scheme, etc.). The same holds true for Chained actions. Generally the
30chain that matches the most PathParts wins.
31
32=head2 Args(N) versus Args
33
34'Args' matches any number of args. Because this functions as a sort of catchall, we
35treat 'Args' as the lowest precedence of any Args(N) when N is 0 to infinity. An
36action with 'Args' always get the last chance to match.
37
38=head2 When two or more actions match a given Path
39
79fb8f95 40Sometimes two or more actions match the same path and all have the same PathPart
2234c98a 41length. For example:
42
43 package MyApp::Controller::Root;
44
45 use Moose;
46 use MooseX::MethodAttributes
47
48 extends 'Catalyst::Controller';
49
50 sub root :Chained(/) CaptureArgs(0) { }
51
52 sub one :Chained(root) PathPart('') Args(0) { }
53 sub two :Chained(root) PathPart('') Args(0) { }
54 sub three :Chained(root) PathPart('') Args(0) { }
55
56 __PACKAGE__->meta->make_immutable;
57
58In this case the last defined action wins (for the example that is action 'three').
59
60This is most common to happen when you are using action matching beyond paths, such as
61when using method matching:
62
63 package MyApp::Controller::Root;
64
65 use Moose;
66 use MooseX::MethodAttributes
67
68 extends 'Catalyst::Controller';
69
70 sub root :Chained(/) CaptureArgs(0) { }
71
72 sub any :Chained(root) PathPart('') Args(0) { }
73 sub get :GET Chained(root) PathPart('') Args(0) { }
74
75 __PACKAGE__->meta->make_immutable;
76
77In the above example GET /root could match both actions. In this case you should define
78your 'catchall' actions higher in the controller.
79
480d94b5 80=head2 Type Constraints in Args and Capture Args
81
82Beginning in Version 5.90090+ you may use L<Moose>, L<MooseX::Types> or L<Type::Tiny>
79fb8f95 83type constraints to further declare allowed matching for Args or CaptureArgs. Here
480d94b5 84is a simple example:
85
86 package MyApp::Controller::User;
87
88 use Moose;
89 use MooseX::MethodAttributes;
6a226ee3 90 use MooseX::Types::Moose qw(Int);
480d94b5 91
92 extends 'Catalyst::Controller';
93
6a226ee3 94 sub find :Path('') Args(Int) {
480d94b5 95 my ($self, $c, $int) = @_;
96 }
97
98 __PACKAGE__->meta->make_immutable;
99
100In this case the incoming request "http://localhost:/user/100" would match the action
101C<find> but "http://localhost:/user/not_a_number" would not. You may find declaring
d249a614 102constraints in this manner aids with debugging, automatic generation of documentation
103and reducing the amount of manual checking you might need to do in your actions. For
104example if the argument in the given action was going to be used to lookup a row
105in a database, if the matching field expected an integer, a string might cause a database
480d94b5 106exception, prompting you to add additional checking of the argument prior to using it.
d249a614 107In general it is hoped this feature can lead to reduced validation boilerplate and more
108easily understood and declarative actions.
480d94b5 109
110More than one argument may be added by comma separating your type constraint names, for
111example:
112
75ce30d0 113 use Types::Standard qw/Int Str/;
114
480d94b5 115 sub find :Path('') Args(Int,Int,Str) {
116 my ($self, $c, $int1, $int2, $str) = @_;
117 }
118
75ce30d0 119Would require three arguments, an integer, integer and a string. Note in this example we
120constrained the args using imported types via L<Types::Standard>. Although you may use
121stringy Moose types, we recommend imported types since this is less ambiguous to your readers.
122If you want to use Moose stringy types. you must quote them (either "Int" or 'Int' is fine).
123
124Conversely, you should not quote types that are imported!
480d94b5 125
126=head3 Using type constraints in a controller
127
128By default L<Catalyst> allows all the standard, built-in, named type constraints that come
129bundled with L<Moose>. However it is trivial to create your own Type constraint libraries
d249a614 130and export them to a controller that wishes to use them. We recommend using L<Type::Tiny> or
480d94b5 131L<MooseX::Types> for this. Here is an example using some extended type constraints via
132the L<Types::Standard> library that is packaged with L<Type::Tiny>:
133
134 package MyApp::Controller::User;
135
136 use Moose;
137 use MooseX::MethodAttributes;
75ce30d0 138 use Types::Standard qw/StrMatch Int/;
480d94b5 139
140 extends 'Catalyst::Controller';
141
142 sub looks_like_a_date :Path('') Args(StrMatch[qr{\d\d-\d\d-\d\d}]) {
143 my ($self, $c, $int) = @_;
144 }
145
146 __PACKAGE__->meta->make_immutable;
147
148This would match URLs like "http://localhost/user/11-11-2015" for example. If you've been
149missing the old RegExp matching, this can emulate a good chunk of that ability, and more.
150
151A tutorial on how to make custom type libraries is outside the scope of this document. I'd
152recommend looking at the copious documentation in L<Type::Tiny> or in L<MooseX::Types> if
153you prefer that system. The author recommends L<Type::Tiny> if you are unsure which to use.
154
59051400 155=head3 Type constraint namespace.
156
157By default we assume the namespace which defines the type constraint is in the package
158which contains the action declaring the arg or capture arg. However if you do not wish
159to import type constraints into you package, you may use a fully qualified namespace for
160your type constraint. If you do this you must install L<Type::Tiny> which defines the
161code used to lookup and normalize the various types of Type constraint libraries.
162
163Example:
164
165 package MyApp::Example;
166
167 use Moose;
168 use MooseX::MethodAttributes;
169
170 extends 'Catalyst::Controller';
171
172 sub an_int_ns :Local Args(MyApp::Types::Int) {
173 my ($self, $c, $int) = @_;
174 $c->res->body('an_int (withrole)');
175 }
176
177Would basically work the same as:
178
179 package MyApp::Example;
180
181 use Moose;
182 use MooseX::MethodAttributes;
183 use MyApp::Types 'Int';
184
185 extends 'Catalyst::Controller';
186
187 sub an_int_ns :Local Args(Int) {
188 my ($self, $c, $int) = @_;
189 $c->res->body('an_int (withrole)');
190 }
191
192=head3 namespace::autoclean
193
194If you want to use L<namespace::autoclean> in your controllers you must 'except' imported
195type constraints since the code that resolves type constraints in args / capture args
196run after the cleaning. For example:
197
198 package MyApp::Controller::Autoclean;
199
200 use Moose;
201 use MooseX::MethodAttributes;
202 use namespace::autoclean -except => 'Int';
203 use MyApp::Types qw/Int/;
204
205 extends 'Catalyst::Controller';
206
207 sub an_int :Local Args(Int) {
208 my ($self, $c, $int) = @_;
209 $c->res->body('an_int (autoclean)');
210 }
211
212=head3 Using roles and base controller with type constraints
213
214If your controller is using a base class or a role that has an action with a type constraint
215you should declare your use of the type constraint in that role or base controller in the
216same way as you do in main controllers. Catalyst will try to find the package with declares
217the type constraint first by looking in any roles and then in superclasses. It will use the
218first package that defines the type constraint. For example:
219
220 package MyApp::Role;
221
222 use Moose::Role;
223 use MooseX::MethodAttributes::Role;
224 use MyApp::Types qw/Int/;
225
226 sub an_int :Local Args(Int) {
227 my ($self, $c, $int) = @_;
228 $c->res->body('an_int (withrole)');
229 }
230
231 sub an_int_ns :Local Args(MyApp::Types::Int) {
232 my ($self, $c, $int) = @_;
233 $c->res->body('an_int (withrole)');
234 }
235
236 package MyApp::BaseController;
237
238 use Moose;
239 use MooseX::MethodAttributes;
240 use MyApp::Types qw/Int/;
241
242 extends 'Catalyst::Controller';
243
244 sub from_parent :Local Args(Int) {
245 my ($self, $c, $id) = @_;
246 $c->res->body('from_parent $id');
247 }
248
249 package MyApp::Controller::WithRole;
250
251 use Moose;
252 use MooseX::MethodAttributes;
253
254 extends 'MyApp::BaseController';
255
256 with 'MyApp::Role';
257
258If you have complex controller hierarchy, we
259do not at this time attempt to look for all packages with a match type constraint, but instead
260take the first one found. In the future we may add code that attempts to insure a sane use
261of subclasses with type constraints but right now there are no clear use cases so report issues
262and interests.
263
480d94b5 264=head3 Match order when more than one Action matches a path.
265
266As previously described, L<Catalyst> will match 'the longest path', which generally means
79fb8f95 267that named path / path_parts will take precedence over Args or CaptureArgs. However, what
480d94b5 268will happen if two actions match the same path with equal args? For example:
269
270 sub an_int :Path(user) Args(Int) {
271 }
272
273 sub an_any :Path(user) Args(1) {
274 }
275
276In this case L<Catalyst> will check actions starting from the LAST one defined. Generally
277this means you should put your most specific action rules LAST and your 'catch-alls' first.
278In the above example, since Args(1) will match any argument, you will find that that 'an_int'
279action NEVER gets hit. You would need to reverse the order:
280
281 sub an_any :Path(user) Args(1) {
282 }
283
284 sub an_int :Path(user) Args(Int) {
285 }
286
d249a614 287Now requests that match this path would first hit the 'an_int' action and will check to see if
288the argument is an integer. If it is, then the action will execute, otherwise it will pass and
79fb8f95 289the dispatcher will check the next matching action (in this case we fall through to the 'an_any'
d249a614 290action).
480d94b5 291
292=head3 Type Constraints and Chained Actions
293
294Using type constraints in Chained actions works the same as it does for Path and Local or Global
d249a614 295actions. The only difference is that you may declare type constraints on CaptureArgs as
480d94b5 296well as Args. For Example:
297
75ce30d0 298 use Types::Standard qw/Int Tuple/;
299
480d94b5 300 sub chain_base :Chained(/) CaptureArgs(1) { }
301
9228a8ec 302 sub any_priority_chain :GET Chained(chain_base) PathPart('') Args(1) { }
480d94b5 303
304 sub int_priority_chain :Chained(chain_base) PathPart('') Args(Int) { }
305
306 sub link_any :Chained(chain_base) PathPart('') CaptureArgs(1) { }
307
308 sub any_priority_link_any :Chained(link_any) PathPart('') Args(1) { }
309
9228a8ec 310 sub int_priority_link_any :Chained(link_any) PathPart('') Args(Int) { }
480d94b5 311
312 sub link_int :Chained(chain_base) PathPart('') CaptureArgs(Int) { }
313
9228a8ec 314 sub any_priority_link :Chained(link_int) PathPart('') Args(1) { }
480d94b5 315
9228a8ec 316 sub int_priority_link :Chained(link_int) PathPart('') Args(Int) { }
317
318 sub link_int_int :Chained(chain_base) PathPart('') CaptureArgs(Int,Int) { }
319
320 sub any_priority_link2 :Chained(link_int_int) PathPart('') Args(1) { }
321
322 sub int_priority_link2 :Chained(link_int_int) PathPart('') Args(Int) { }
323
324 sub link_tuple :Chained(chain_base) PathPart('') CaptureArgs(Tuple[Int,Int,Int]) { }
325
326 sub any_priority_link3 :Chained(link_tuple) PathPart('') Args(1) { }
327
328 sub int_priority_link3 :Chained(link_tuple) PathPart('') Args(Int) { }
480d94b5 329
79fb8f95 330These chained actions might create match tables like the following:
480d94b5 331
332 [debug] Loaded Chained actions:
9228a8ec 333 .-------------------------------------+--------------------------------------.
334 | Path Spec | Private |
335 +-------------------------------------+--------------------------------------+
336 | /chain_base/*/* | /chain_base (1) |
337 | | => GET /any_priority_chain (1) |
338 | /chain_base/*/*/* | /chain_base (1) |
339 | | -> /link_int (Int) |
340 | | => /any_priority_link (1) |
341 | /chain_base/*/*/*/* | /chain_base (1) |
342 | | -> /link_int_int (Int,Int) |
343 | | => /any_priority_link2 (1) |
344 | /chain_base/*/*/*/*/* | /chain_base (1) |
345 | | -> /link_tuple (Tuple[Int,Int,Int]) |
346 | | => /any_priority_link3 (1) |
347 | /chain_base/*/*/* | /chain_base (1) |
348 | | -> /link_any (1) |
349 | | => /any_priority_link_any (1) |
350 | /chain_base/*/*/*/*/*/* | /chain_base (1) |
351 | | -> /link_tuple (Tuple[Int,Int,Int]) |
352 | | -> /link2_int (UserId) |
353 | | => GET /finally (Int) |
354 | /chain_base/*/*/*/*/*/... | /chain_base (1) |
355 | | -> /link_tuple (Tuple[Int,Int,Int]) |
356 | | -> /link2_int (UserId) |
357 | | => GET /finally2 (...) |
358 | /chain_base/*/* | /chain_base (1) |
359 | | => /int_priority_chain (Int) |
360 | /chain_base/*/*/* | /chain_base (1) |
361 | | -> /link_int (Int) |
362 | | => /int_priority_link (Int) |
363 | /chain_base/*/*/*/* | /chain_base (1) |
364 | | -> /link_int_int (Int,Int) |
365 | | => /int_priority_link2 (Int) |
366 | /chain_base/*/*/*/*/* | /chain_base (1) |
367 | | -> /link_tuple (Tuple[Int,Int,Int]) |
368 | | => /int_priority_link3 (Int) |
369 | /chain_base/*/*/* | /chain_base (1) |
370 | | -> /link_any (1) |
371 | | => /int_priority_link_any (Int) |
372 '-------------------------------------+--------------------------------------'
480d94b5 373
374As you can see the same general path could be matched by various action chains. In this case
375the rule described in the previous section should be followed, which is that L<Catalyst>
376will start with the last defined action and work upward. For example the action C<int_priority_chain>
377would be checked before C<any_priority_chain>. The same applies for actions that are midway links
378in a longer chain. In this case C<link_int> would be checked before C<link_any>. So as always we
79fb8f95 379recommend that you place you priority or most constrained actions last and you least or catch-all
480d94b5 380actions first.
381
382Although this reverse order checking may seen counter intuitive it does have the added benefit that
383when inheriting controllers any new actions added would take check precedence over those in your
384parent controller or consumed role.
385
9228a8ec 386Please note that your declared type constraint names will now appear in the debug console.
387
480d94b5 388=head1 Author
389
390John Napiorkowski L<jjnapiork@cpan.org|email:jjnapiork@cpan.org>
391
392=cut
393