In this case the incoming request "http://localhost:/user/100" would match the action
C<find> but "http://localhost:/user/not_a_number" would not. You may find declaring
-constraints in this manner aids with debuggin, automatic generation of documentation
-and reduces the amount of manual checking you might need to do in your actions. For
-example if the argument in the example action was going to be used to lookup a row
-in a database, if the matching field expected an integer a string might cause a database
+constraints in this manner aids with debugging, automatic generation of documentation
+and reducing the amount of manual checking you might need to do in your actions. For
+example if the argument in the given action was going to be used to lookup a row
+in a database, if the matching field expected an integer, a string might cause a database
exception, prompting you to add additional checking of the argument prior to using it.
+In general it is hoped this feature can lead to reduced validation boilerplate and more
+easily understood and declarative actions.
More than one argument may be added by comma separating your type constraint names, for
example:
By default L<Catalyst> allows all the standard, built-in, named type constraints that come
bundled with L<Moose>. However it is trivial to create your own Type constraint libraries
-and export them to controller that wish to use them. We recommend using L<Type::Tiny> or
+and export them to a controller that wishes to use them. We recommend using L<Type::Tiny> or
L<MooseX::Types> for this. Here is an example using some extended type constraints via
the L<Types::Standard> library that is packaged with L<Type::Tiny>:
sub an_int :Path(user) Args(Int) {
}
-Now requests that match this path would first hit the 'an_int' action, and then the 'an_any'
-action, which it likely what you are looking for!
+Now requests that match this path would first hit the 'an_int' action and will check to see if
+the argument is an integer. If it is, then the action will execute, otherwise it will pass and
+the dispatcher will check the next matching action (in this case we fall thru to the 'an_any'
+action).
=head3 Type Constraints and Chained Actions
Using type constraints in Chained actions works the same as it does for Path and Local or Global
-actions. The only difference is that you maybe declare type constraints on CaptureArgs as
+actions. The only difference is that you may declare type constraints on CaptureArgs as
well as Args. For Example:
sub chain_base :Chained(/) CaptureArgs(1) { }
- sub any_priority_chain :Chained(chain_base) PathPart('') Args(1) { }
+ sub any_priority_chain :GET Chained(chain_base) PathPart('') Args(1) { }
sub int_priority_chain :Chained(chain_base) PathPart('') Args(Int) { }
sub any_priority_link_any :Chained(link_any) PathPart('') Args(1) { }
- sub int_priority_link_any :Chained(link_any) PathPart('') Args(Int) { }
+ sub int_priority_link_any :Chained(link_any) PathPart('') Args(Int) { }
sub link_int :Chained(chain_base) PathPart('') CaptureArgs(Int) { }
- sub any_priority_link :Chained(link_int) PathPart('') Args(1) { }
+ sub any_priority_link :Chained(link_int) PathPart('') Args(1) { }
- sub int_priority_link :Chained(link_int) PathPart('') Args(Int) { }
+ sub int_priority_link :Chained(link_int) PathPart('') Args(Int) { }
+
+ sub link_int_int :Chained(chain_base) PathPart('') CaptureArgs(Int,Int) { }
+
+ sub any_priority_link2 :Chained(link_int_int) PathPart('') Args(1) { }
+
+ sub int_priority_link2 :Chained(link_int_int) PathPart('') Args(Int) { }
+
+ sub link_tuple :Chained(chain_base) PathPart('') CaptureArgs(Tuple[Int,Int,Int]) { }
+
+ sub any_priority_link3 :Chained(link_tuple) PathPart('') Args(1) { }
+
+ sub int_priority_link3 :Chained(link_tuple) PathPart('') Args(Int) { }
These chained actions migth create match tables like the following:
[debug] Loaded Chained actions:
- .----------------------------------------------+----------------------------------------------.
- | Path Spec | Private |
- +----------------------------------------------+----------------------------------------------+
- | /chain_base/*/* | /chain_base (1) |
- | | => /any_priority_chain |
- | /chain_base/*/*/* | /chain_base (1) |
- | | -> /link_int (1) |
- | | => /any_priority_link |
- | /chain_base/*/*/* | /chain_base (1) |
- | | -> /link_any (1) |
- | | => /any_priority_link_any |
- | /chain_base/*/* | /chain_base (1) |
- | | => /int_priority_chain |
- | /chain_base/*/*/* | /chain_base (1) |
- | | -> /link_int (1) |
- | | => /int_priority_link |
- | /chain_base/*/*/* | /chain_base (1) |
- | | -> /link_any (1) |
- | | => /int_priority_link_any |
- '----------------------------------------------+----------------------------------------------'
+ .-------------------------------------+--------------------------------------.
+ | Path Spec | Private |
+ +-------------------------------------+--------------------------------------+
+ | /chain_base/*/* | /chain_base (1) |
+ | | => GET /any_priority_chain (1) |
+ | /chain_base/*/*/* | /chain_base (1) |
+ | | -> /link_int (Int) |
+ | | => /any_priority_link (1) |
+ | /chain_base/*/*/*/* | /chain_base (1) |
+ | | -> /link_int_int (Int,Int) |
+ | | => /any_priority_link2 (1) |
+ | /chain_base/*/*/*/*/* | /chain_base (1) |
+ | | -> /link_tuple (Tuple[Int,Int,Int]) |
+ | | => /any_priority_link3 (1) |
+ | /chain_base/*/*/* | /chain_base (1) |
+ | | -> /link_any (1) |
+ | | => /any_priority_link_any (1) |
+ | /chain_base/*/*/*/*/*/* | /chain_base (1) |
+ | | -> /link_tuple (Tuple[Int,Int,Int]) |
+ | | -> /link2_int (UserId) |
+ | | => GET /finally (Int) |
+ | /chain_base/*/*/*/*/*/... | /chain_base (1) |
+ | | -> /link_tuple (Tuple[Int,Int,Int]) |
+ | | -> /link2_int (UserId) |
+ | | => GET /finally2 (...) |
+ | /chain_base/*/* | /chain_base (1) |
+ | | => /int_priority_chain (Int) |
+ | /chain_base/*/*/* | /chain_base (1) |
+ | | -> /link_int (Int) |
+ | | => /int_priority_link (Int) |
+ | /chain_base/*/*/*/* | /chain_base (1) |
+ | | -> /link_int_int (Int,Int) |
+ | | => /int_priority_link2 (Int) |
+ | /chain_base/*/*/*/*/* | /chain_base (1) |
+ | | -> /link_tuple (Tuple[Int,Int,Int]) |
+ | | => /int_priority_link3 (Int) |
+ | /chain_base/*/*/* | /chain_base (1) |
+ | | -> /link_any (1) |
+ | | => /int_priority_link_any (Int) |
+ '-------------------------------------+--------------------------------------'
As you can see the same general path could be matched by various action chains. In this case
the rule described in the previous section should be followed, which is that L<Catalyst>
when inheriting controllers any new actions added would take check precedence over those in your
parent controller or consumed role.
+Please note that your declared type constraint names will now appear in the debug console.
+
=head1 Conclusion
TBD