update distar url
[catagits/Catalyst-Runtime.git] / lib / Catalyst / RouteMatching.pod
1 =encoding UTF-8
2
3 =head1 Name
4
5 Catalyst::RouteMatching - How Catalyst maps an incoming URL to actions in controllers.
6
7 =head1 Description
8
9 This is a WIP document intended to help people understand the logic that L<Catalyst>
10 uses to determine how to match in incoming request to an action (or action chain)
11 in a controller.
12
13 =head2 Request to Controller/Action Matching
14
15 L<Catalyst> maps requests to action using a 'longest path wins' approach.  That means
16 that 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
28 Path length matches take precedence over all other types of matches (included HTTP
29 Method, Scheme, etc.).  The same holds true for Chained actions.  Generally the
30 chain 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
35 treat 'Args' as the lowest precedence of any Args(N) when N is 0 to infinity.  An
36 action with 'Args' always get the last chance to match.
37
38 =head2 When two or more actions match a given Path
39
40 Sometimes two or more actions match the same path and all have the same PathPart
41 length.  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
58 In this case the last defined action wins (for the example that is action 'three').
59
60 This is most common to happen when you are using action matching beyond paths, such as
61 when 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
77 In the above example GET /root could match both actions.  In this case you should define
78 your 'catchall' actions higher in the controller.
79
80 =head2 Type Constraints in Args and Capture Args
81
82 Beginning in Version 5.90090+ you may use L<Moose>, L<MooseX::Types> or L<Type::Tiny>
83 type constraints to further declare allowed matching for Args or CaptureArgs.  Here
84 is a simple example:
85
86     package MyApp::Controller::User;
87
88     use Moose;
89     use MooseX::MethodAttributes;
90     use MooseX::Types::Moose qw(Int);
91
92     extends 'Catalyst::Controller';
93
94     sub find :Path('') Args(Int) {
95       my ($self, $c, $int) = @_;
96     }
97
98     __PACKAGE__->meta->make_immutable;
99
100 In this case the incoming request "http://localhost:/user/100" would match the action
101 C<find> but "http://localhost:/user/not_a_number" would not. You may find declaring
102 constraints in this manner aids with debugging, automatic generation of documentation
103 and reducing the amount of manual checking you might need to do in your actions.  For
104 example if the argument in the given action was going to be used to lookup a row
105 in a database, if the matching field expected an integer, a string might cause a database
106 exception, prompting you to add additional checking of the argument prior to using it.
107 In general it is hoped this feature can lead to reduced validation boilerplate and more
108 easily understood and declarative actions.
109
110 More than one argument may be added by comma separating your type constraint names, for
111 example:
112
113     use Types::Standard qw/Int Str/;
114
115     sub find :Path('') Args(Int,Int,Str) {
116       my ($self, $c, $int1, $int2, $str) = @_;
117     }
118
119 Would require three arguments, an integer, integer and a string.  Note in this example we
120 constrained the args using imported types via L<Types::Standard>.  Although you may use
121 stringy Moose types, we recommend imported types since this is less ambiguous to your readers.
122 If you want to use Moose stringy types. you must quote them (either "Int" or 'Int' is fine).
123
124 Conversely, you should not quote types that are imported!
125
126 =head3 Using type constraints in a controller
127
128 By default L<Catalyst> allows all the standard, built-in, named type constraints that come
129 bundled with L<Moose>.  However it is trivial to create your own Type constraint libraries
130 and export them to a controller that wishes to use them.  We recommend using L<Type::Tiny> or
131 L<MooseX::Types> for this.  Here is an example using some extended type constraints via
132 the 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;
138     use Types::Standard qw/StrMatch Int/;
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
148 This would match URLs like "http://localhost/user/11-11-2015" for example.  If you've been
149 missing the old RegExp matching, this can emulate a good chunk of that ability, and more.
150
151 A tutorial on how to make custom type libraries is outside the scope of this document.  I'd
152 recommend looking at the copious documentation in L<Type::Tiny> or in L<MooseX::Types> if
153 you prefer that system.  The author recommends L<Type::Tiny> if you are unsure which to use.
154
155 =head3 Type constraint namespace.
156
157 By default we assume the namespace which defines the type constraint is in the package
158 which contains the action declaring the arg or capture arg.  However if you do not wish
159 to import type constraints into you package, you may use a fully qualified namespace for
160 your type constraint.  If you do this you must install L<Type::Tiny> which defines the
161 code used to lookup and normalize the various types of Type constraint libraries.
162
163 Example:
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
177 Would 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
194 If you want to use L<namespace::autoclean> in your controllers you must 'except' imported
195 type constraints since the code that resolves type constraints in args / capture args
196 run 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
214 If your controller is using a base class or a role that has an action with a type constraint
215 you should declare your use of the type constraint in that role or base controller in the
216 same way as you do in main controllers.  Catalyst will try to find the package with declares
217 the type constraint first by looking in any roles and then in superclasses.  It will use the
218 first 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
258 If you have complex controller hierarchy, we
259 do not at this time attempt to look for all packages with a match type constraint, but instead
260 take the first one found.  In the future we may add code that attempts to insure a sane use
261 of subclasses with type constraints but right now there are no clear use cases so report issues
262 and interests.
263
264 =head3 Match order when more than one Action matches a path.
265
266 As previously described, L<Catalyst> will match 'the longest path', which generally means
267 that named path / path_parts will take precedence over Args or CaptureArgs.  However, what
268 will 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
276 In this case L<Catalyst> will check actions starting from the LAST one defined.  Generally
277 this means you should put your most specific action rules LAST and your 'catch-alls' first.
278 In the above example, since Args(1) will match any argument, you will find that that 'an_int'
279 action 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
287 Now requests that match this path would first hit the 'an_int' action and will check to see if
288 the argument is an integer.  If it is, then the action will execute, otherwise it will pass and
289 the dispatcher will check the next matching action (in this case we fall through to the 'an_any'
290 action).
291
292 =head3 Type Constraints and Chained Actions
293
294 Using type constraints in Chained actions works the same as it does for Path and Local or Global
295 actions.  The only difference is that you may declare type constraints on CaptureArgs as
296 well as Args.  For Example:
297
298   use Types::Standard qw/Int Tuple/;
299
300   sub chain_base :Chained(/) CaptureArgs(1) { }
301
302     sub any_priority_chain :GET Chained(chain_base) PathPart('') Args(1) {  }
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
310       sub int_priority_link_any :Chained(link_any) PathPart('') Args(Int) {  }
311
312     sub link_int :Chained(chain_base) PathPart('') CaptureArgs(Int) { }
313
314       sub any_priority_link :Chained(link_int) PathPart('') Args(1) {  }
315
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) {  }
329
330 These chained actions might create match tables like the following:
331
332     [debug] Loaded Chained actions:
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     '-------------------------------------+--------------------------------------'
373
374 As you can see the same general path could be matched by various action chains.  In this case
375 the rule described in the previous section should be followed, which is that L<Catalyst>
376 will start with the last defined action and work upward.  For example the action C<int_priority_chain>
377 would be checked before C<any_priority_chain>.  The same applies for actions that are midway links
378 in a longer chain.  In this case C<link_int> would be checked before C<link_any>.  So as always we
379 recommend that you place you priority or most constrained actions last and you least or catch-all
380 actions first.
381
382 Although this reverse order checking may seen counter intuitive it does have the added benefit that
383 when inheriting controllers any new actions added would take check precedence over those in your
384 parent controller or consumed role.
385
386 Please note that your declared type constraint names will now appear in the debug console.
387
388 =head1 Author
389
390 John Napiorkowski L<jjnapiork@cpan.org|email:jjnapiork@cpan.org>
391
392 =cut
393