uses to determine how to match in incoming request to an action (or action chain)
in a controller.
+=head2 Request to Controller/Action Matching
+
+L<Catalyst> maps requests to action using a 'longest path wins' approach. That means
+that if the request is '/foo/bar/baz' That means the action 'baz' matches:
+
+ package MyApp::Controller::Foo;
+
+ use Moose;
+ use MooseX::MethodAttributes
+
+ extends 'Catalyst::Controller';
+
+ sub bar :Path('bar') Args(1) { ...}
+ sub baz :Path('bar/baz') Args(0) { ... }
+
+Path length matches take precidence over all other types of matches (included HTTP
+Method, Scheme, etc.). The same holds true for Chained actions. Generally the
+chain that matches the most PathParts wins.
+
+=head2 Args(N) versus Args
+
+'Args' matches any number of args. Because this functions as a sort of catchall, we
+treat 'Args' as the lowest precedence of any Args(N) when N is 0 to infinity. An
+action with 'Args' always get the last chance to match.
+
+=head2 When two or more actions match a given Path
+
+Sometimes two or more actions match the same path and all have the same pathpart
+length. For example:
+
+ package MyApp::Controller::Root;
+
+ use Moose;
+ use MooseX::MethodAttributes
+
+ extends 'Catalyst::Controller';
+
+ sub root :Chained(/) CaptureArgs(0) { }
+
+ sub one :Chained(root) PathPart('') Args(0) { }
+ sub two :Chained(root) PathPart('') Args(0) { }
+ sub three :Chained(root) PathPart('') Args(0) { }
+
+ __PACKAGE__->meta->make_immutable;
+
+In this case the last defined action wins (for the example that is action 'three').
+
+This is most common to happen when you are using action matching beyond paths, such as
+when using method matching:
+
+ package MyApp::Controller::Root;
+
+ use Moose;
+ use MooseX::MethodAttributes
+
+ extends 'Catalyst::Controller';
+
+ sub root :Chained(/) CaptureArgs(0) { }
+
+ sub any :Chained(root) PathPart('') Args(0) { }
+ sub get :GET Chained(root) PathPart('') Args(0) { }
+
+ __PACKAGE__->meta->make_immutable;
+
+In the above example GET /root could match both actions. In this case you should define
+your 'catchall' actions higher in the controller.
+
=head2 Type Constraints in Args and Capture Args
Beginning in Version 5.90090+ you may use L<Moose>, L<MooseX::Types> or L<Type::Tiny>
use Moose;
use MooseX::MethodAttributes;
+ use MooseX::Types::Moose qw(Int);
extends 'Catalyst::Controller';
More than one argument may be added by comma separating your type constraint names, for
example:
+ use Types::Standard qw/Int Str/;
+
sub find :Path('') Args(Int,Int,Str) {
my ($self, $c, $int1, $int2, $str) = @_;
}
-Would require three arguments, an integer, integer and a string.
+Would require three arguments, an integer, integer and a string. Note in this example we
+constrained the args using imported types via L<Types::Standard>. Although you may use
+stringy Moose types, we recommend imported types since this is less ambiguous to your readers.
+If you want to use Moose stringy types. you must quote them (either "Int" or 'Int' is fine).
+
+Conversely, you should not quote types that are imported!
=head3 Using type constraints in a controller
use Moose;
use MooseX::MethodAttributes;
- use Types::Standard qw/StrMatch/;
+ use Types::Standard qw/StrMatch Int/;
extends 'Catalyst::Controller';
recommend looking at the copious documentation in L<Type::Tiny> or in L<MooseX::Types> if
you prefer that system. The author recommends L<Type::Tiny> if you are unsure which to use.
+=head3 Type constraint namespace.
+
+By default we assume the namespace which defines the type constraint is in the package
+which contains the action declaring the arg or capture arg. However if you do not wish
+to import type constraints into you package, you may use a fully qualified namespace for
+your type constraint. If you do this you must install L<Type::Tiny> which defines the
+code used to lookup and normalize the various types of Type constraint libraries.
+
+Example:
+
+ package MyApp::Example;
+
+ use Moose;
+ use MooseX::MethodAttributes;
+
+ extends 'Catalyst::Controller';
+
+ sub an_int_ns :Local Args(MyApp::Types::Int) {
+ my ($self, $c, $int) = @_;
+ $c->res->body('an_int (withrole)');
+ }
+
+Would basically work the same as:
+
+ package MyApp::Example;
+
+ use Moose;
+ use MooseX::MethodAttributes;
+ use MyApp::Types 'Int';
+
+ extends 'Catalyst::Controller';
+
+ sub an_int_ns :Local Args(Int) {
+ my ($self, $c, $int) = @_;
+ $c->res->body('an_int (withrole)');
+ }
+
+=head3 namespace::autoclean
+
+If you want to use L<namespace::autoclean> in your controllers you must 'except' imported
+type constraints since the code that resolves type constraints in args / capture args
+run after the cleaning. For example:
+
+ package MyApp::Controller::Autoclean;
+
+ use Moose;
+ use MooseX::MethodAttributes;
+ use namespace::autoclean -except => 'Int';
+ use MyApp::Types qw/Int/;
+
+ extends 'Catalyst::Controller';
+
+ sub an_int :Local Args(Int) {
+ my ($self, $c, $int) = @_;
+ $c->res->body('an_int (autoclean)');
+ }
+
+=head3 Using roles and base controller with type constraints
+
+If your controller is using a base class or a role that has an action with a type constraint
+you should declare your use of the type constraint in that role or base controller in the
+same way as you do in main controllers. Catalyst will try to find the package with declares
+the type constraint first by looking in any roles and then in superclasses. It will use the
+first package that defines the type constraint. For example:
+
+ package MyApp::Role;
+
+ use Moose::Role;
+ use MooseX::MethodAttributes::Role;
+ use MyApp::Types qw/Int/;
+
+ sub an_int :Local Args(Int) {
+ my ($self, $c, $int) = @_;
+ $c->res->body('an_int (withrole)');
+ }
+
+ sub an_int_ns :Local Args(MyApp::Types::Int) {
+ my ($self, $c, $int) = @_;
+ $c->res->body('an_int (withrole)');
+ }
+
+ package MyApp::BaseController;
+
+ use Moose;
+ use MooseX::MethodAttributes;
+ use MyApp::Types qw/Int/;
+
+ extends 'Catalyst::Controller';
+
+ sub from_parent :Local Args(Int) {
+ my ($self, $c, $id) = @_;
+ $c->res->body('from_parent $id');
+ }
+
+ package MyApp::Controller::WithRole;
+
+ use Moose;
+ use MooseX::MethodAttributes;
+
+ extends 'MyApp::BaseController';
+
+ with 'MyApp::Role';
+
+If you have complex controller hierarchy, we
+do not at this time attempt to look for all packages with a match type constraint, but instead
+take the first one found. In the future we may add code that attempts to insure a sane use
+of subclasses with type constraints but right now there are no clear use cases so report issues
+and interests.
+
=head3 Match order when more than one Action matches a path.
As previously described, L<Catalyst> will match 'the longest path', which generally means
actions. The only difference is that you may declare type constraints on CaptureArgs as
well as Args. For Example:
+ use Types::Standard qw/Int Tuple/;
+
sub chain_base :Chained(/) CaptureArgs(1) { }
sub any_priority_chain :GET Chained(chain_base) PathPart('') Args(1) { }
Please note that your declared type constraint names will now appear in the debug console.
-=head1 Conclusion
-
- TBD
-
=head1 Author
John Napiorkowski L<jjnapiork@cpan.org|email:jjnapiork@cpan.org>