docs
[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
13=head2 Type Constraints in Args and Capture Args
14
15Beginning in Version 5.90090+ you may use L<Moose>, L<MooseX::Types> or L<Type::Tiny>
16type constraints to futher declare allowed matching for Args or CaptureArgs. Here
17is a simple example:
18
19 package MyApp::Controller::User;
20
21 use Moose;
22 use MooseX::MethodAttributes;
23
24 extends 'Catalyst::Controller';
25
26 sub find :Path('') Args(Int) {
27 my ($self, $c, $int) = @_;
28 }
29
30 __PACKAGE__->meta->make_immutable;
31
32In this case the incoming request "http://localhost:/user/100" would match the action
33C<find> but "http://localhost:/user/not_a_number" would not. You may find declaring
34constraints in this manner aids with debuggin, automatic generation of documentation
35and reduces the amount of manual checking you might need to do in your actions. For
36example if the argument in the example action was going to be used to lookup a row
37in a database, if the matching field expected an integer a string might cause a database
38exception, prompting you to add additional checking of the argument prior to using it.
39
40More than one argument may be added by comma separating your type constraint names, for
41example:
42
43 sub find :Path('') Args(Int,Int,Str) {
44 my ($self, $c, $int1, $int2, $str) = @_;
45 }
46
47Would require three arguments, an integer, integer and a string.
48
49=head3 Using type constraints in a controller
50
51By default L<Catalyst> allows all the standard, built-in, named type constraints that come
52bundled with L<Moose>. However it is trivial to create your own Type constraint libraries
53and export them to controller that wish to use them. We recommend using L<Type::Tiny> or
54L<MooseX::Types> for this. Here is an example using some extended type constraints via
55the L<Types::Standard> library that is packaged with L<Type::Tiny>:
56
57 package MyApp::Controller::User;
58
59 use Moose;
60 use MooseX::MethodAttributes;
61 use Types::Standard qw/StrMatch/;
62
63 extends 'Catalyst::Controller';
64
65 sub looks_like_a_date :Path('') Args(StrMatch[qr{\d\d-\d\d-\d\d}]) {
66 my ($self, $c, $int) = @_;
67 }
68
69 __PACKAGE__->meta->make_immutable;
70
71This would match URLs like "http://localhost/user/11-11-2015" for example. If you've been
72missing the old RegExp matching, this can emulate a good chunk of that ability, and more.
73
74A tutorial on how to make custom type libraries is outside the scope of this document. I'd
75recommend looking at the copious documentation in L<Type::Tiny> or in L<MooseX::Types> if
76you prefer that system. The author recommends L<Type::Tiny> if you are unsure which to use.
77
78=head3 Match order when more than one Action matches a path.
79
80As previously described, L<Catalyst> will match 'the longest path', which generally means
81that named path / path_parts will take precidence over Args or CaptureArgs. However, what
82will happen if two actions match the same path with equal args? For example:
83
84 sub an_int :Path(user) Args(Int) {
85 }
86
87 sub an_any :Path(user) Args(1) {
88 }
89
90In this case L<Catalyst> will check actions starting from the LAST one defined. Generally
91this means you should put your most specific action rules LAST and your 'catch-alls' first.
92In the above example, since Args(1) will match any argument, you will find that that 'an_int'
93action NEVER gets hit. You would need to reverse the order:
94
95 sub an_any :Path(user) Args(1) {
96 }
97
98 sub an_int :Path(user) Args(Int) {
99 }
100
101Now requests that match this path would first hit the 'an_int' action, and then the 'an_any'
102action, which it likely what you are looking for!
103
104=head3 Type Constraints and Chained Actions
105
106Using type constraints in Chained actions works the same as it does for Path and Local or Global
107actions. The only difference is that you maybe declare type constraints on CaptureArgs as
108well as Args. For Example:
109
110 sub chain_base :Chained(/) CaptureArgs(1) { }
111
112 sub any_priority_chain :Chained(chain_base) PathPart('') Args(1) { }
113
114 sub int_priority_chain :Chained(chain_base) PathPart('') Args(Int) { }
115
116 sub link_any :Chained(chain_base) PathPart('') CaptureArgs(1) { }
117
118 sub any_priority_link_any :Chained(link_any) PathPart('') Args(1) { }
119
120 sub int_priority_link_any :Chained(link_any) PathPart('') Args(Int) { }
121
122 sub link_int :Chained(chain_base) PathPart('') CaptureArgs(Int) { }
123
124 sub any_priority_link :Chained(link_int) PathPart('') Args(1) { }
125
126 sub int_priority_link :Chained(link_int) PathPart('') Args(Int) { }
127
128These chained actions migth create match tables like the following:
129
130 [debug] Loaded Chained actions:
131 .----------------------------------------------+----------------------------------------------.
132 | Path Spec | Private |
133 +----------------------------------------------+----------------------------------------------+
134 | /chain_base/*/* | /chain_base (1) |
135 | | => /any_priority_chain |
136 | /chain_base/*/*/* | /chain_base (1) |
137 | | -> /link_int (1) |
138 | | => /any_priority_link |
139 | /chain_base/*/*/* | /chain_base (1) |
140 | | -> /link_any (1) |
141 | | => /any_priority_link_any |
142 | /chain_base/*/* | /chain_base (1) |
143 | | => /int_priority_chain |
144 | /chain_base/*/*/* | /chain_base (1) |
145 | | -> /link_int (1) |
146 | | => /int_priority_link |
147 | /chain_base/*/*/* | /chain_base (1) |
148 | | -> /link_any (1) |
149 | | => /int_priority_link_any |
150 '----------------------------------------------+----------------------------------------------'
151
152As you can see the same general path could be matched by various action chains. In this case
153the rule described in the previous section should be followed, which is that L<Catalyst>
154will start with the last defined action and work upward. For example the action C<int_priority_chain>
155would be checked before C<any_priority_chain>. The same applies for actions that are midway links
156in a longer chain. In this case C<link_int> would be checked before C<link_any>. So as always we
157recommend that you place you priority or most constrainted actions last and you least or catch-all
158actions first.
159
160Although this reverse order checking may seen counter intuitive it does have the added benefit that
161when inheriting controllers any new actions added would take check precedence over those in your
162parent controller or consumed role.
163
164=head1 Conclusion
165
166 TBD
167
168=head1 Author
169
170John Napiorkowski L<jjnapiork@cpan.org|email:jjnapiork@cpan.org>
171
172=cut
173