X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst%2FController.pm;h=b50a2ffbbb34c89b64688235d44b33d3d104db15;hp=3e3dd79b941657c76dab42fb5d348691e9e8fce4;hb=161710a39409d2afd256463471b8014a1c69adf4;hpb=aaa85094fac33621a80f5fde138e195c3fdee0b0 diff --git a/lib/Catalyst/Controller.pm b/lib/Catalyst/Controller.pm index 3e3dd79..b50a2ff 100644 --- a/lib/Catalyst/Controller.pm +++ b/lib/Catalyst/Controller.pm @@ -120,7 +120,7 @@ for more info about how Catalyst dispatches to actions. =cut #I think both of these could be attributes. doesn't really seem like they need -#to ble class data. i think that attributes +default would work just fine +#to be class data. i think that attributes +default would work just fine __PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class _action_role_prefix/; __PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] ); @@ -368,7 +368,16 @@ sub gather_default_action_roles { my @roles = (); push @roles, 'Catalyst::ActionRole::HTTPMethods' if $args{attributes}->{Method}; - return @roles; + + push @roles, 'Catalyst::ActionRole::ConsumesContent' + if $args{attributes}->{Consumes}; + + push @roles, 'Catalyst::ActionRole::Scheme' + if $args{attributes}->{Scheme}; + + push @roles, 'Catalyst::ActionRole::QueryMatching' + if $args{attributes}->{Query}; + return @roles; } sub _parse_attrs { @@ -380,7 +389,7 @@ sub _parse_attrs { # Parse out :Foo(bar) into Foo => bar etc (and arrayify) - if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) ) + if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.*?)\s*\))?$/ ) ) { if ( defined $value ) { @@ -538,12 +547,13 @@ sub _parse_Does_attr { return Does => $self->_expand_role_shortname($value); } -sub _parse_GET_attr { Method => 'GET' } -sub _parse_POST_attr { Method => 'POST' } -sub _parse_PUT_attr { Method => 'PUT' } -sub _parse_DELETE_attr { Method => 'DELETE' } -sub _parse_OPTION_attr { Method => 'OPTION' } -sub _parse_HEAD_attr { Method => 'HEAD' } +sub _parse_GET_attr { Method => 'GET' } +sub _parse_POST_attr { Method => 'POST' } +sub _parse_PUT_attr { Method => 'PUT' } +sub _parse_DELETE_attr { Method => 'DELETE' } +sub _parse_OPTIONS_attr { Method => 'OPTIONS' } +sub _parse_HEAD_attr { Method => 'HEAD' } +sub _parse_PATCH_attr { Method => 'PATCH' } sub _expand_role_shortname { my ($self, @shortnames) = @_; @@ -780,7 +790,29 @@ Like L but scoped under the namespace of the containing controller =head2 CaptureArgs -Please see L +Allowed values for CaptureArgs is a single integer (CaptureArgs(2), meaning two +allowed) or you can declare a L, L or L +named constraint such as CaptureArgs(Int,Str) would require two args with +the first being a Integer and the second a string. You may declare your own +custom type constraints and import them into the controller namespace: + + package MyApp::Controller::Root; + + use Moose; + use MooseX::MethodAttributes; + use MyApp::Types qw/Int/; + + extends 'Catalyst::Controller'; + + sub chain_base :Chained(/) CaptureArgs(1) { } + + sub any_priority_chain :Chained(chain_base) PathPart('') Args(1) { } + + sub int_priority_chain :Chained(chain_base) PathPart('') Args(Int) { } + +See L for more. + +Please see L for more. =head2 ActionClass @@ -830,6 +862,141 @@ When used with L indicates the number of arguments expected in the path. However if no Args value is set, assumed to 'slurp' all remaining path pars under this namespace. +Allowed values for Args is a single integer (Args(2), meaning two allowed) or you +can declare a L, L or L named constraint such +as Args(Int,Str) would require two args with the first being a Integer and the +second a string. You may declare your own custom type constraints and import +them into the controller namespace: + + package MyApp::Controller::Root; + + use Moose; + use MooseX::MethodAttributes; + use MyApp::Types qw/Tuple Int Str StrMatch UserId/; + + extends 'Catalyst::Controller'; + + sub user :Local Args(UserId) { + my ($self, $c, $int) = @_; + } + + sub an_int :Local Args(Int) { + my ($self, $c, $int) = @_; + } + + sub many_ints :Local Args(ArrayRef[Int]) { + my ($self, $c, @ints) = @_; + } + + sub match :Local Args(StrMatch[qr{\d\d-\d\d-\d\d}]) { + my ($self, $c, $int) = @_; + } + +If you choose not to use imported type constraints (like L, or +you may use L 'stringy' types however just like when you use these types in your +declared attributes you must quote them: + + sub my_moose_type :Local Args('Int') { ... } + +If you use 'reference' type constraints (such as ArrayRef[Int]) that have an unknown +number of allowed matches, we set this the same way "Args" is. Please keep in mind +that actions with an undetermined number of args match at lower precedence than those +with a fixed number. You may use reference types such as Tuple from L +that allows you to fix the number of allowed args. For example Args(Tuple[Int,Int]) +would be determined to be two args (or really the same as Args(Int,Int).) You may +find this useful for creating custom subtypes with complex matching rules that you +wish to reuse over many actions. + +See L for more. + +=head2 Consumes('...') + +Matches the current action against the content-type of the request. Typically +this is used when the request is a POST or PUT and you want to restrict the +submitted content type. For example, you might have an HTML for that either +returns classic url encoded form data, or JSON when Javascript is enabled. In +this case you may wish to match either incoming type to one of two different +actions, for properly processing. + +Examples: + + sub is_json : Chained('start') Consumes('application/json') { ... } + sub is_urlencoded : Chained('start') Consumes('application/x-www-form-urlencoded') { ... } + sub is_multipart : Chained('start') Consumes('multipart/form-data') { ... } + +To reduce boilerplate, we include the following content type shortcuts: + +Examples + + sub is_json : Chained('start') Consume(JSON) { ... } + sub is_urlencoded : Chained('start') Consumes(UrlEncoded) { ... } + sub is_multipart : Chained('start') Consumes(Multipart) { ... } + +You may specify more than one match: + + sub is_more_than_one + : Chained('start') + : Consumes('application/x-www-form-urlencoded') + : Consumes('multipart/form-data') + + sub is_more_than_one + : Chained('start') + : Consumes(UrlEncoded) + : Consumes(Multipart) + +Since it is a common case the shortcut C matches both +'application/x-www-form-urlencoded' and 'multipart/form-data'. Here's the full +list of available shortcuts: + + JSON => 'application/json', + JS => 'application/javascript', + PERL => 'application/perl', + HTML => 'text/html', + XML => 'text/XML', + Plain => 'text/plain', + UrlEncoded => 'application/x-www-form-urlencoded', + Multipart => 'multipart/form-data', + HTMLForm => ['application/x-www-form-urlencoded','multipart/form-data'], + +Please keep in mind that when dispatching, L will match the first most +relevant case, so if you use the C attribute, you should place your +most accurate matches early in the Chain, and your 'catchall' actions last. + +See L for more. + +=head2 Scheme(...) + +Allows you to specify a URI scheme for the action or action chain. For example +you can required that a given path be C or that it is a websocket endpoint +C or C. For an action chain you may currently only have one defined +Scheme. + + package MyApp::Controller::Root; + + use base 'Catalyst::Controller'; + + sub is_http :Path(scheme) Scheme(http) Args(0) { + my ($self, $c) = @_; + $c->response->body("is_http"); + } + + sub is_https :Path(scheme) Scheme(https) Args(0) { + my ($self, $c) = @_; + $c->response->body("is_https"); + } + +In the above example http://localhost/root/scheme would match the first +action (is_http) but https://localhost/root/scheme would match the second. + +As an added benefit, if an action or action chain defines a Scheme, when using +$c->uri_for the scheme of the generated URL will use what you define in the action +or action chain (the current behavior is to set the scheme based on the current +incoming request). This makes it easier to use uri_for on websites where some +paths are secure and others are not. You may also use this to other schemes +like websockets. + +See L for more. + =head1 OPTIONAL METHODS =head2 _parse_[$name]_attr