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';
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 defined($_[0]->number_of_args) ? $_[0]->number_of_args : ~0;
74 has number_of_args_constraints => (
80 builder=>'_build_number_of_args_constraints');
82 sub _build_number_of_args_constraints {
84 return unless $self->has_args_constraints;
87 foreach my $tc( @{$self->args_constraints}) {
88 if($tc->is_a_type_of('Ref')) {
89 if($tc->can('parameters') && $tc->has_parameters) {
90 my $total_params = scalar(@{ $tc->parameters||[] });
91 $total = $total + $total_params;
93 # Its a Reftype but we don't know the number of params it
105 has args_constraints => (
112 builder=>'_build_args_constraints',
114 has_args_constraints => 'count',
115 args_constraint_count => 'count',
118 sub _build_args_constraints {
120 my @arg_protos = @{$self->attributes->{Args}||[]};
122 return [] unless scalar(@arg_protos);
123 # If there is only one arg and it looks like a number
124 # we assume its 'classic' and the number is the number of
128 scalar(@arg_protos) == 1 &&
129 looks_like_number($arg_protos[0])
134 map { my @tc = $self->resolve_type_constraint($_); scalar(@tc) ? @tc : die "$_ is not a constraint!" }
140 has number_of_captures_constraints => (
146 builder=>'_build_number_of_capture_constraints');
148 sub _build_number_of_capture_constraints {
150 return unless $self->has_captures_constraints;
153 foreach my $tc( @{$self->captures_constraints}) {
154 if($tc->is_a_type_of('Ref')) {
155 if($tc->can('parameters') && $tc->has_parameters) {
156 my $total_params = scalar(@{ $tc->parameters||[] });
157 $total = $total + $total_params;
159 # Its a Reftype but we don't know the number of params it
160 # actually validates. This is not currently permitted in
162 die "You cannot use CaptureArgs($tc) in ${\$self->reverse} because we cannot determined the number of its parameters";
172 has captures_constraints => (
179 builder=>'_build_captures_constraints',
181 has_captures_constraints => 'count',
182 captures_constraints_count => 'count',
185 sub _build_captures_constraints {
187 my @arg_protos = @{$self->attributes->{CaptureArgs}||[]};
189 return [] unless scalar(@arg_protos);
190 # If there is only one arg and it looks like a number
191 # we assume its 'classic' and the number is the number of
195 scalar(@arg_protos) == 1 &&
196 looks_like_number($arg_protos[0])
201 map { my @tc = $self->resolve_type_constraint($_); scalar(@tc) ? @tc : die "$_ is not a constraint!" }
208 sub resolve_type_constraint {
209 my ($self, $name) = @_;
210 my @tc = eval "package ${\$self->class}; $name";
211 return @tc if $tc[0];
212 return Moose::Util::TypeConstraints::find_or_parse_type_constraint($name);
215 has number_of_captures => (
221 builder=>'_build_number_of_captures');
223 sub _build_number_of_captures {
225 if( ! exists $self->attributes->{CaptureArgs} ) {
226 # If there are no defined capture args, thats considered 0.
228 } elsif(!defined($self->attributes->{CaptureArgs}[0])) {
229 # If you fail to give a defined value, that's also 0
232 scalar(@{$self->attributes->{CaptureArgs}}) == 1 &&
233 looks_like_number($self->attributes->{CaptureArgs}[0])
235 # 'Old school' numbered captures
236 return $self->attributes->{CaptureArgs}[0];
238 # New hotness named arg constraints
239 return $self->number_of_captures_constraints;
246 # Stringify to reverse for debug output etc.
247 q{""} => sub { shift->{reverse} },
249 # Codulate to execute to invoke the encapsulated action coderef
250 '&{}' => sub { my $self = shift; sub { $self->execute(@_); }; },
252 # Make general $stuff still work
257 no warnings 'recursion';
259 sub dispatch { # Execute ourselves against a context
260 my ( $self, $c ) = @_;
261 return $c->execute( $self->class, $self );
270 my ( $self, $c ) = @_;
272 # If infinite args, we always match
273 return 1 if $self->normalized_arg_number == ~0;
275 # There there are arg constraints, we must see to it that the constraints
276 # check positive for each arg in the list.
277 if($self->has_args_constraints) {
278 # If there is only one type constraint, and its a Ref or subtype of Ref,
279 # That means we expect a reference, so use the full args arrayref.
281 $self->args_constraint_count == 1 &&
283 $self->args_constraints->[0]->is_a_type_of('Ref') ||
284 $self->args_constraints->[0]->is_a_type_of('ClassName')
287 return $self->args_constraints->[0]->check($c->req->args);
288 # Removing coercion stuff for the first go
289 #if($self->args_constraints->[0]->coercion && $self->attributes->{Coerce}) {
290 # my $coerced = $self->args_constraints->[0]->coerce($c) || return 0;
291 # $c->req->args([$coerced]);
295 # Because of the way chaining works, we can expect args that are totally not
296 # what you'd expect length wise. When they don't match length, thats a fail
297 return 0 unless scalar( @{ $c->req->args } ) == $self->normalized_arg_number;
299 for my $i(0..$#{ $c->req->args }) {
300 $self->args_constraints->[$i]->check($c->req->args->[$i]) || return 0;
305 # Otherwise, we just need to match the number of args.
306 return scalar( @{ $c->req->args } ) == $self->normalized_arg_number;
311 my ($self, $c, $captures) = @_;
312 my @captures = @{$captures||[]};
314 return 1 unless scalar(@captures); # If none, just say its ok
316 if($self->has_captures_constraints) {
318 $self->captures_constraints_count == 1 &&
320 $self->captures_constraints->[0]->is_a_type_of('Ref') ||
321 $self->captures_constraints->[0]->is_a_type_of('ClassName')
324 return $self->captures_constraints->[0]->check($captures);
326 for my $i(0..$#captures) {
327 $self->captures_constraints->[$i]->check($captures[$i]) || return 0;
339 return $a1->normalized_arg_number <=> $a2->normalized_arg_number;
343 return exists $_[0]->attributes->{Scheme} ? $_[0]->attributes->{Scheme}[0] : undef;
346 sub list_extra_info {
349 Args => $self->normalized_arg_number,
350 CaptureArgs => $self->number_of_captures,
354 __PACKAGE__->meta->make_immutable;
364 The sub attributes that are set for this action, like Local, Path, Private
365 and so on. This determines how the action is dispatched to.
369 Returns the name of the component where this action is defined.
370 Derived by calling the L<catalyst_component_name|Catalyst::Component/catalyst_component_name>
371 method on each component.
375 Returns a code reference to this action.
377 =head2 dispatch( $c )
379 Dispatch this action against a context.
381 =head2 execute( $controller, $c, @args )
383 Execute this action's coderef against a given controller with a given
384 context and arguments
388 Check Args attribute, and makes sure number of args matches the setting.
389 Always returns true if Args is omitted.
391 =head2 match_captures ($c, $captures)
393 Can be implemented by action class and action role authors. If the method
394 exists, then it will be called with the request context and an array reference
395 of the captures for this action.
397 Returning true from this method causes the chain match to continue, returning
398 makes the chain not match (and alternate, less preferred chains will be attempted).
400 =head2 resolve_type_constraint
402 Trys to find a type constraint if you have on on a type constrained method.
406 Compares 2 actions based on the value of the C<Args> attribute, with no C<Args>
407 having the highest precedence.
411 Returns the private namespace this action lives in.
415 Returns the private path for this action.
419 Returns absolute private path for this action. Unlike C<reverse>, the
420 C<private_path> of an action is always suitable for passing to C<forward>.
424 Returns the sub name of this action.
426 =head2 number_of_args
428 Returns the number of args this action expects. This is 0 if the action doesn't
429 take any arguments and undef if it will take any number of arguments.
431 =head2 normalized_arg_number
433 For the purposes of comparison we normalize 'number_of_args' so that if it is
434 undef we mean ~0 (as many args are we can think of).
436 =head2 number_of_captures
438 Returns the number of captures this action expects for L<Chained|Catalyst::DispatchType::Chained> actions.
440 =head2 list_extra_info
442 A HashRef of key-values that an action can provide to a debugging screen
446 Any defined scheme for the action
454 Catalyst Contributors, see Catalyst.pm
458 This library is free software. You can redistribute it and/or modify it under
459 the same terms as Perl itself.