1 package Catalyst::Action;
5 Catalyst::Action - Catalyst Action
9 <form action="[%c.uri_for(c.action)%]">
11 $c->forward( $action->private_path );
15 This class represents a Catalyst Action. You can access the object for the
16 currently dispatched action via $c->action. See the L<Catalyst::Dispatcher>
17 for more information on how actions are dispatched. Actions are defined in
18 L<Catalyst::Controller> subclasses.
23 use Scalar::Util 'looks_like_number', 'blessed';
24 use Moose::Util::TypeConstraints ();
25 with 'MooseX::Emulate::Class::Accessor::Fast';
26 use namespace::clean -except => 'meta';
28 has class => (is => 'rw');
29 has namespace => (is => 'rw');
30 has 'reverse' => (is => 'rw');
31 has attributes => (is => 'rw');
32 has name => (is => 'rw');
33 has code => (is => 'rw');
35 reader => 'private_path',
39 default => sub { '/'.shift->reverse },
42 has number_of_args => (
48 builder=>'_build_number_of_args');
50 sub _build_number_of_args {
52 if( ! exists $self->attributes->{Args} ) {
53 # When 'Args' does not exist, that means we want 'any number of args'.
55 } elsif(!defined($self->attributes->{Args}[0])) {
56 # When its 'Args' that internal cue for 'unlimited'
59 scalar(@{$self->attributes->{Args}}) == 1 &&
60 looks_like_number($self->attributes->{Args}[0])
62 # 'Old school' numbered args (is allowed to be undef as well)
63 return $self->attributes->{Args}[0];
65 # New hotness named arg constraints
66 return $self->number_of_args_constraints;
70 sub normalized_arg_number {
71 return $_[0]->number_of_args;
74 sub comparable_arg_number {
75 return defined($_[0]->number_of_args) ? $_[0]->number_of_args : ~0;
78 has number_of_args_constraints => (
84 builder=>'_build_number_of_args_constraints');
86 sub _build_number_of_args_constraints {
88 return unless $self->has_args_constraints;
90 # If there is one constraint and its a ref, we need to decide
91 # if this number 'unknown' number or if the ref allows us to
94 if(scalar @{$self->args_constraints} == 1) {
95 my $tc = $self->args_constraints->[0];
97 $tc->can('is_strictly_a_type_of') &&
98 $tc->is_strictly_a_type_of('Tuple'))
100 my @parameters = @{ $tc->parameters||[] };
101 my $final = $parameters[-1];
102 if ( defined $final ) {
103 if ( blessed $final ) {
104 # modern form of slurpy
105 if ($final->can('is_strictly_a_type_of') && $final->is_strictly_a_type_of('Slurpy')) {
111 if (ref $final eq 'HASH' && $final->{slurpy}) {
116 return scalar @parameters;
117 } elsif($tc->is_a_type_of('Ref')) {
120 return 1; # Its a normal 1 arg type constraint.
123 # We need to loop through and error on ref types. We don't allow a ref type
126 foreach my $tc( @{$self->args_constraints}) {
127 if($tc->is_a_type_of('Ref')) {
128 die "$tc is a Ref type constraint. You cannot mix Ref and non Ref type constraints in Args for action ${\$self->reverse}";
137 has args_constraints => (
144 builder=>'_build_args_constraints',
146 has_args_constraints => 'count',
147 args_constraint_count => 'count',
148 all_args_constraints => 'elements',
151 sub _build_args_constraints {
153 my @arg_protos = @{$self->attributes->{Args}||[]};
155 return [] unless scalar(@arg_protos);
156 return [] unless defined($arg_protos[0]);
158 # If there is only one arg and it looks like a number
159 # we assume its 'classic' and the number is the number of
163 scalar(@arg_protos) == 1 &&
164 looks_like_number($arg_protos[0])
169 map { my @tc = $self->resolve_type_constraint($_); scalar(@tc) ? @tc : die "$_ is not a constraint!" }
175 has number_of_captures_constraints => (
181 builder=>'_build_number_of_capture_constraints');
183 sub _build_number_of_capture_constraints {
185 return unless $self->has_captures_constraints;
187 # If there is one constraint and its a ref, we need to decide
188 # if this number 'unknown' number or if the ref allows us to
189 # determine a length.
191 if(scalar @{$self->captures_constraints} == 1) {
192 my $tc = $self->captures_constraints->[0];
194 $tc->can('is_strictly_a_type_of') &&
195 $tc->is_strictly_a_type_of('Tuple'))
197 my @parameters = @{ $tc->parameters||[]};
198 if( defined($parameters[-1]) and exists($parameters[-1]->{slurpy})) {
201 return my $total_params = scalar(@parameters);
203 } elsif($tc->is_a_type_of('Ref')) {
204 die "You cannot use CaptureArgs($tc) in ${\$self->reverse} because we cannot determined the number of its parameters";
206 return 1; # Its a normal 1 arg type constraint.
209 # We need to loop through and error on ref types. We don't allow a ref type
212 foreach my $tc( @{$self->captures_constraints}) {
213 if($tc->is_a_type_of('Ref')) {
214 die "$tc is a Ref type constraint. You cannot mix Ref and non Ref type constraints in CaptureArgs for action ${\$self->reverse}";
223 has captures_constraints => (
230 builder=>'_build_captures_constraints',
232 has_captures_constraints => 'count',
233 captures_constraints_count => 'count',
234 all_captures_constraints => 'elements',
237 sub _build_captures_constraints {
239 my @arg_protos = @{$self->attributes->{CaptureArgs}||[]};
241 return [] unless scalar(@arg_protos);
242 return [] unless defined($arg_protos[0]);
243 # If there is only one arg and it looks like a number
244 # we assume its 'classic' and the number is the number of
248 scalar(@arg_protos) == 1 &&
249 looks_like_number($arg_protos[0])
254 map { my @tc = $self->resolve_type_constraint($_); scalar(@tc) ? @tc : die "$_ is not a constraint!" }
261 sub resolve_type_constraint {
262 my ($self, $name) = @_;
264 if(defined($name) && blessed($name) && $name->can('check')) {
265 # Its already a TC, good to go.
269 # This is broken for when there is more than one constraint
271 eval "use Type::Registry; 1" || die "Can't resolve type constraint $name without installing Type::Tiny";
272 my $tc = Type::Registry->new->foreign_lookup($name);
273 return defined $tc ? $tc : die "'$name' not a full namespace type constraint in ${\$self->private_path}";
276 my @tc = grep { defined $_ } (eval("package ${\$self->class}; $name"));
279 # ok... so its not defined in the package. we need to look at all the roles
280 # and superclasses, look for attributes and figure it out.
281 # Superclasses take precedence;
283 my @supers = $self->class->can('meta') ? map { $_->meta } $self->class->meta->superclasses : ();
284 my @roles = $self->class->can('meta') ? $self->class->meta->calculate_all_roles : ();
286 # So look through all the super and roles in order and return the
287 # first type constraint found. We should probably find all matching
288 # type constraints and try to do some sort of resolution.
290 foreach my $parent (@roles, @supers) {
291 if(my $m = $parent->get_method($self->name)) {
292 if($m->can('attributes')) {
293 my ($key, $value) = map { $_ =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ }
294 grep { $_=~/^Args\(/ or $_=~/^CaptureArgs\(/ }
296 next unless $value eq $name;
297 my @tc = eval "package ${\$parent->name}; $name";
299 return map { ref($_) ? $_ : Moose::Util::TypeConstraints::find_or_parse_type_constraint($_) } @tc;
307 my $classes = join(',', $self->class, @roles, @supers);
308 die "'$name' not a type constraint in '${\$self->private_path}', Looked in: $classes";
312 return map { ref($_) ? $_ : Moose::Util::TypeConstraints::find_or_parse_type_constraint($_) } @tc;
318 has number_of_captures => (
324 builder=>'_build_number_of_captures');
326 sub _build_number_of_captures {
328 if( ! exists $self->attributes->{CaptureArgs} ) {
329 # If there are no defined capture args, thats considered 0.
331 } elsif(!defined($self->attributes->{CaptureArgs}[0])) {
332 # If you fail to give a defined value, that's also 0
335 scalar(@{$self->attributes->{CaptureArgs}}) == 1 &&
336 looks_like_number($self->attributes->{CaptureArgs}[0])
338 # 'Old school' numbered captures
339 return $self->attributes->{CaptureArgs}[0];
341 # New hotness named arg constraints
342 return $self->number_of_captures_constraints;
349 # Stringify to reverse for debug output etc.
350 q{""} => sub { shift->{reverse} },
352 # Codulate to execute to invoke the encapsulated action coderef
353 '&{}' => sub { my $self = shift; sub { $self->execute(@_); }; },
355 # Make general $stuff still work
360 no warnings 'recursion';
362 sub dispatch { # Execute ourselves against a context
363 my ( $self, $c ) = @_;
364 return $c->execute( $self->class, $self );
373 my ( $self, $c ) = @_;
374 return $self->match_args($c, $c->req->args);
378 my ($self, $c, $args) = @_;
379 my @args = @{$args||[]};
381 # There there are arg constraints, we must see to it that the constraints
382 # check positive for each arg in the list.
383 if($self->has_args_constraints) {
384 # If there is only one type constraint, and its a Ref or subtype of Ref,
385 # That means we expect a reference, so use the full args arrayref.
387 $self->args_constraint_count == 1 &&
389 $self->args_constraints->[0]->is_a_type_of('Ref') ||
390 $self->args_constraints->[0]->is_a_type_of('ClassName')
393 # Ok, the the type constraint is a ref type, which is allowed to have
394 # any number of args. We need to check the arg length, if one is defined.
395 # If we had a ref type constraint that allowed us to determine the allowed
396 # number of args, we need to match that number. Otherwise if there was an
397 # undetermined number (~0) then we allow all the args. This is more of an
398 # Optimization since Tuple[Int, Int] would fail on 3,4,5 anyway, but this
399 # way we can avoid calling the constraint when the arg length is incorrect.
401 $self->comparable_arg_number == ~0 ||
402 scalar( @args ) == $self->comparable_arg_number
404 return $self->args_constraints->[0]->check($args);
408 # Removing coercion stuff for the first go
409 #if($self->args_constraints->[0]->coercion && $self->attributes->{Coerce}) {
410 # my $coerced = $self->args_constraints->[0]->coerce($c) || return 0;
411 # $c->req->args([$coerced]);
415 # Because of the way chaining works, we can expect args that are totally not
416 # what you'd expect length wise. When they don't match length, thats a fail
417 return 0 unless scalar( @args ) == $self->comparable_arg_number;
419 for my $i(0..$#args) {
420 $self->args_constraints->[$i]->check($args[$i]) || return 0;
425 # If infinite args with no constraints, we always match
426 return 1 if $self->comparable_arg_number == ~0;
428 # Otherwise, we just need to match the number of args.
429 return scalar( @args ) == $self->comparable_arg_number;
434 my ($self, $c, $captures) = @_;
435 my @captures = @{$captures||[]};
437 return 1 unless scalar(@captures); # If none, just say its ok
438 return $self->has_captures_constraints ?
439 $self->match_captures_constraints($c, $captures) : 1;
444 sub match_captures_constraints {
445 my ($self, $c, $captures) = @_;
446 my @captures = @{$captures||[]};
448 # Match is positive if you don't have any.
449 return 1 unless $self->has_captures_constraints;
452 $self->captures_constraints_count == 1 &&
454 $self->captures_constraints->[0]->is_a_type_of('Ref') ||
455 $self->captures_constraints->[0]->is_a_type_of('ClassName')
458 return $self->captures_constraints->[0]->check($captures);
460 for my $i(0..$#captures) {
461 $self->captures_constraints->[$i]->check($captures[$i]) || return 0;
471 return $a1->comparable_arg_number <=> $a2->comparable_arg_number;
475 my ($self, $target) = @_;
476 return $self->private_path eq $target->private_path ? $self : 0;
480 return exists $_[0]->attributes->{Scheme} ? $_[0]->attributes->{Scheme}[0] : undef;
483 sub list_extra_info {
486 Args => $self->normalized_arg_number,
487 CaptureArgs => $self->number_of_captures,
491 __PACKAGE__->meta->make_immutable;
501 The sub attributes that are set for this action, like Local, Path, Private
502 and so on. This determines how the action is dispatched to.
506 Returns the name of the component where this action is defined.
507 Derived by calling the L<catalyst_component_name|Catalyst::Component/catalyst_component_name>
508 method on each component.
512 Returns a code reference to this action.
514 =head2 dispatch( $c )
516 Dispatch this action against a context.
518 =head2 execute( $controller, $c, @args )
520 Execute this action's coderef against a given controller with a given
521 context and arguments
525 Check Args attribute, and makes sure number of args matches the setting.
526 Always returns true if Args is omitted.
528 =head2 match_captures ($c, $captures)
530 Can be implemented by action class and action role authors. If the method
531 exists, then it will be called with the request context and an array reference
532 of the captures for this action.
534 Returning true from this method causes the chain match to continue, returning
535 makes the chain not match (and alternate, less preferred chains will be attempted).
537 =head2 match_captures_constraints ($c, \@captures);
539 Does the \@captures given match any constraints (if any constraints exist). Returns
540 true if you ask but there are no constraints.
542 =head2 match_args($c, $args)
544 Does the Args match or not?
546 =head2 resolve_type_constraint
548 Tries to find a type constraint if you have on on a type constrained method.
552 Compares 2 actions based on the value of the C<Args> attribute, with no C<Args>
553 having the highest precedence.
557 if( $action->equal($other_action) ) { ... }
559 Returns true if the two actions are equal.
563 Returns the private namespace this action lives in.
567 Returns the private path for this action.
571 Returns absolute private path for this action. Unlike C<reverse>, the
572 C<private_path> of an action is always suitable for passing to C<forward>.
576 Returns the sub name of this action.
578 =head2 number_of_args
580 Returns the number of args this action expects. This is 0 if the action doesn't
581 take any arguments and undef if it will take any number of arguments.
583 =head2 normalized_arg_number
585 The number of arguments (starting with zero) that the current action defines, or
586 undefined if there is not defined number of args (which is later treated as, "
587 as many arguments as you like").
589 =head2 comparable_arg_number
591 For the purposes of comparison we normalize 'number_of_args' so that if it is
592 undef we mean ~0 (as many args are we can think of).
594 =head2 number_of_captures
596 Returns the number of captures this action expects for L<Chained|Catalyst::DispatchType::Chained> actions.
598 =head2 list_extra_info
600 A HashRef of key-values that an action can provide to a debugging screen
604 Any defined scheme for the action
612 Catalyst Contributors, see Catalyst.pm
616 This library is free software. You can redistribute it and/or modify it under
617 the same terms as Perl itself.