X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FController.pm;h=653363f640f49388f718d50e74cb83a13aa43793;hb=dd4530ecdc4684838d9c0e9dc00adebb6100b022;hp=02db77a562df015d3e07195811a62e562b5be3a6;hpb=c396cfc71169fc01ede2b891c197be02791ee2aa;p=catagits%2FCatalyst-Runtime.git diff --git a/lib/Catalyst/Controller.pm b/lib/Catalyst/Controller.pm index 02db77a..653363f 100644 --- a/lib/Catalyst/Controller.pm +++ b/lib/Catalyst/Controller.pm @@ -7,6 +7,7 @@ use String::RewritePrefix; use Moose::Util qw/find_meta/; use List::Util qw/first/; use List::MoreUtils qw/uniq/; +use Ref::Util qw(is_plain_arrayref); use namespace::clean -except => 'meta'; BEGIN { @@ -143,17 +144,27 @@ sub _BEGIN : Private { my $begin = ( $c->get_actions( 'begin', $c->namespace ) )[-1]; return 1 unless $begin; $begin->dispatch( $c ); - return !@{ $c->error }; + #If there is an error, all bets off + if( @{ $c->error }) { + return !@{ $c->error }; + } else { + return $c->state || 1; + } } sub _AUTO : Private { my ( $self, $c ) = @_; my @auto = $c->get_actions( 'auto', $c->namespace ); foreach my $auto (@auto) { + # We FORCE the auto action user to explicitly return + # true. We need to do this since there's some auto + # users (Catalyst::Authentication::Credential::HTTP) that + # actually do a detach instead. + $c->state(0); $auto->dispatch( $c ); return 0 unless $c->state; } - return 1; + return $c->state || 1; } sub _ACTION : Private { @@ -164,7 +175,12 @@ sub _ACTION : Private { { $c->action->dispatch( $c ); } - return !@{ $c->error }; + #If there is an error, all bets off + if( @{ $c->error }) { + return !@{ $c->error }; + } else { + return $c->state || 1; + } } sub _END : Private { @@ -372,6 +388,11 @@ sub gather_default_action_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; } @@ -384,7 +405,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 ) { @@ -409,7 +430,7 @@ sub _parse_attrs { %raw_attributes, # Note we deep copy array refs here to stop crapping on config # when attributes are parsed. RT#65463 - exists $actions_config->{$name} ? map { ref($_) eq 'ARRAY' ? [ @$_ ] : $_ } %{ $actions_config->{$name } } : (), + exists $actions_config->{$name} ? map { is_plain_arrayref($_) ? [ @$_ ] : $_ } %{ $actions_config->{$name } } : (), ); # Private actions with additional attributes will raise a warning and then @@ -435,7 +456,7 @@ sub _parse_attr { my ($self, $c, $name, $key, $values) = @_; my %final_attributes; - foreach my $value (ref($values) eq 'ARRAY' ? @$values : $values) { + foreach my $value (is_plain_arrayref($values) ? @$values : $values) { my $meth = "_parse_${key}_attr"; if ( my $code = $self->can($meth) ) { my %new_attrs = $self->$code( $c, $name, $value ); @@ -542,12 +563,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) = @_; @@ -641,10 +663,25 @@ arguments, when it is instantiated: From L, stashes the application instance as $self->_application. -=head2 $self->action_for('name') +=head2 $self->action_for($action_name) + +Returns the Catalyst::Action object (if any) for a given action in this +controller or relative to it. You may refer to actions in controllers +nested under the current controllers namespace, or in controllers 'up' +from the current controller namespace. For example: + + package MyApp::Controller::One::Two; + use base 'Catalyst::Controller'; -Returns the Catalyst::Action object (if any) for a given method name -in this component. + sub foo :Local { + my ($self, $c) = @_; + $self->action_for('foo'); # action 'foo' in Controller 'One::Two' + $self->action_for('three/bar'); # action 'bar' in Controller 'One::Two::Three' + $self->action_for('../boo'); # action 'boo' in Controller 'One' + } + +This returns 'undef' if there is no action matching the requested action +name (after any path normalization) so you should check for this as needed. =head2 $self->action_namespace($c) @@ -784,7 +821,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 @@ -834,6 +893,57 @@ 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. + +B: It is highly recommended to use L for your type constraints over +other options. L exposed a better meta data interface which allows us to +do more and better types of introspection driving tests and debugging. + =head2 Consumes('...') Matches the current action against the content-type of the request. Typically @@ -889,6 +999,39 @@ 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