allow refs
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Action.pm
CommitLineData
fbcc39ad 1package Catalyst::Action;
2
b2ddf6d7 3=head1 NAME
4
5Catalyst::Action - Catalyst Action
6
7=head1 SYNOPSIS
8
804fb55d 9 <form action="[%c.uri_for(c.action)%]">
85d9fce6 10
009b5b23 11 $c->forward( $action->private_path );
12
b2ddf6d7 13=head1 DESCRIPTION
14
43c58153 15This class represents a Catalyst Action. You can access the object for the
b2ddf6d7 16currently dispatched action via $c->action. See the L<Catalyst::Dispatcher>
17for more information on how actions are dispatched. Actions are defined in
18L<Catalyst::Controller> subclasses.
19
20=cut
21
059c085b 22use Moose;
05b47f2e 23use Scalar::Util 'looks_like_number';
6d62355b 24use Moose::Util::TypeConstraints ();
241edc9b 25with 'MooseX::Emulate::Class::Accessor::Fast';
05b47f2e 26use namespace::clean -except => 'meta';
241edc9b 27
5fb12dbb 28has class => (is => 'rw');
29has namespace => (is => 'rw');
30has 'reverse' => (is => 'rw');
31has attributes => (is => 'rw');
32has name => (is => 'rw');
33has code => (is => 'rw');
009b5b23 34has private_path => (
35 reader => 'private_path',
36 isa => 'Str',
37 lazy => 1,
38 required => 1,
39 default => sub { '/'.shift->reverse },
40);
059c085b 41
81436df9 42has number_of_args => (
43 is=>'ro',
44 init_arg=>undef,
45 isa=>'Int|Undef',
46 required=>1,
47 lazy=>1,
48 builder=>'_build_number_of_args');
49
50 sub _build_number_of_args {
51 my $self = shift;
d4e8996f 52 if( ! exists $self->attributes->{Args} ) {
53 # When 'Args' does not exist, that means we want 'any number of args'.
54 return undef;
55 } elsif(!defined($self->attributes->{Args}[0])) {
81436df9 56 # When its 'Args' that internal cue for 'unlimited'
57 return undef;
4a0218ca 58 } elsif(
59 scalar(@{$self->attributes->{Args}}) == 1 &&
60 looks_like_number($self->attributes->{Args}[0])
61 ) {
d4e8996f 62 # 'Old school' numberd args (is allowed to be undef as well)
81436df9 63 return $self->attributes->{Args}[0];
64 } else {
d4e8996f 65 # New hotness named arg constraints
81436df9 66 return $self->number_of_args_constraints;
67 }
68 }
69
d4e8996f 70sub normalized_arg_number {
71 return defined($_[0]->number_of_args) ? $_[0]->number_of_args : ~0;
72}
73
6d62355b 74has args_constraints => (
75 is=>'ro',
81436df9 76 init_arg=>undef,
6d62355b 77 traits=>['Array'],
78 isa=>'ArrayRef',
79 required=>1,
80 lazy=>1,
81 builder=>'_build_args_constraints',
82 handles => {
83 has_args_constraints => 'count',
81436df9 84 number_of_args_constraints => 'count',
6d62355b 85 });
86
87 sub _build_args_constraints {
88 my $self = shift;
89 my @arg_protos = @{$self->attributes->{Args}||[]};
90
91 return [] unless scalar(@arg_protos);
92 # If there is only one arg and it looks like a number
93 # we assume its 'classic' and the number is the number of
94 # constraints.
95 my @args = ();
96 if(
97 scalar(@arg_protos) == 1 &&
98 looks_like_number($arg_protos[0])
99 ) {
81436df9 100 return \@args;
6d62355b 101 } else {
4a0218ca 102 # Allows Args(Int,Str) and Args(Str,2,Int) == Args(Str,Any,Any,Int)
103 @args =
104 map { Moose::Util::TypeConstraints::find_or_parse_type_constraint($_) || die "$_ is not a constraint!" }
105 map { looks_like_number($_) ? ('Any' x $_) : $_ } @arg_protos;
6d62355b 106 }
107
108 return \@args;
109 }
110
2055d9ad 111use overload (
112
113 # Stringify to reverse for debug output etc.
114 q{""} => sub { shift->{reverse} },
115
116 # Codulate to execute to invoke the encapsulated action coderef
117 '&{}' => sub { my $self = shift; sub { $self->execute(@_); }; },
118
119 # Make general $stuff still work
120 fallback => 1,
121
122);
123
059c085b 124no warnings 'recursion';
125
b2ddf6d7 126sub dispatch { # Execute ourselves against a context
127 my ( $self, $c ) = @_;
049f82e2 128 return $c->execute( $self->class, $self );
b2ddf6d7 129}
fbcc39ad 130
b2ddf6d7 131sub execute {
132 my $self = shift;
059c085b 133 $self->code->(@_);
b2ddf6d7 134}
fbcc39ad 135
b2ddf6d7 136sub match {
60034b8c 137 my ( $self, $c ) = @_;
81436df9 138
d4e8996f 139 # If infinite args, we always match
140 return 1 if $self->normalized_arg_number == ~0;
141
142 # There there are arg constraints, we must see to it that the constraints
143 # check positive for each arg in the list.
81436df9 144 if($self->has_args_constraints) {
4a0218ca 145 # If there is only one type constraint, and its a Ref or subtype of Ref,
146 # That means we expect a reference, so use the full args arrayref.
147 if(
148 $self->number_of_args_constraints == 1 &&
149 $self->args_constraints->[0]->is_a_type_of('Ref')
150 ) {
151 return $self->args_constraints->[0]->check($c->req->args);
152 } else {
153 for my $i($#{ $c->req->args }) {
154 $self->args_constraints->[$i]->check($c->req->args->[$i]) || return 0;
155 }
156 return 1;
6d62355b 157 }
6d62355b 158 } else {
d4e8996f 159 # Otherwise, we just need to match the number of args.
160 return scalar( @{ $c->req->args } ) == $self->normalized_arg_number;
6d62355b 161 }
760d121e 162}
163
60034b8c 164sub match_captures { 1 }
fbcc39ad 165
05b47f2e 166sub compare {
167 my ($a1, $a2) = @_;
d4e8996f 168 return $a1->normalized_arg_number <=> $a2->normalized_arg_number;
05b47f2e 169}
170
0cff119a 171sub number_of_captures {
172 my ( $self ) = @_;
173
174 return 0 unless exists $self->attributes->{CaptureArgs};
175 return $self->attributes->{CaptureArgs}[0] || 0;
176}
177
342d2169 178sub scheme {
179 return exists $_[0]->attributes->{Scheme} ? $_[0]->attributes->{Scheme}[0] : undef;
180}
181
ffca3e96 182sub list_extra_info {
183 my $self = shift;
184 return {
185 Args => $self->attributes->{Args}[0],
186 CaptureArgs => $self->number_of_captures,
187 }
188}
3c0da3ec 189
e5ecd5bc 190__PACKAGE__->meta->make_immutable;
191
b2ddf6d7 1921;
fbcc39ad 193
b2ddf6d7 194__END__
4ab87e27 195
fbcc39ad 196=head1 METHODS
197
b5ecfcf0 198=head2 attributes
fbcc39ad 199
4ab87e27 200The sub attributes that are set for this action, like Local, Path, Private
b2ddf6d7 201and so on. This determines how the action is dispatched to.
4ab87e27 202
b5ecfcf0 203=head2 class
b96f127f 204
4d38cb07 205Returns the name of the component where this action is defined.
f9818250 206Derived by calling the L<catalyst_component_name|Catalyst::Component/catalyst_component_name>
fb0c5b21 207method on each component.
4ab87e27 208
b5ecfcf0 209=head2 code
11bd4e3e 210
b2ddf6d7 211Returns a code reference to this action.
4ab87e27 212
b8f669f3 213=head2 dispatch( $c )
4ab87e27 214
18a9655c 215Dispatch this action against a context.
fbcc39ad 216
b8f669f3 217=head2 execute( $controller, $c, @args )
218
219Execute this action's coderef against a given controller with a given
220context and arguments
221
649fd1fa 222=head2 match( $c )
4ab87e27 223
649fd1fa 224Check Args attribute, and makes sure number of args matches the setting.
b2ddf6d7 225Always returns true if Args is omitted.
4082e678 226
760d121e 227=head2 match_captures ($c, $captures)
228
229Can be implemented by action class and action role authors. If the method
230exists, then it will be called with the request context and an array reference
231of the captures for this action.
232
233Returning true from this method causes the chain match to continue, returning
234makes the chain not match (and alternate, less preferred chains will be attempted).
235
236
91955398 237=head2 compare
238
cbe555e8 239Compares 2 actions based on the value of the C<Args> attribute, with no C<Args>
240having the highest precedence.
91955398 241
b5ecfcf0 242=head2 namespace
fbcc39ad 243
4ab87e27 244Returns the private namespace this action lives in.
245
b5ecfcf0 246=head2 reverse
6b239949 247
4ab87e27 248Returns the private path for this action.
249
009b5b23 250=head2 private_path
251
252Returns absolute private path for this action. Unlike C<reverse>, the
253C<private_path> of an action is always suitable for passing to C<forward>.
254
b5ecfcf0 255=head2 name
fbcc39ad 256
18a9655c 257Returns the sub name of this action.
4ab87e27 258
0cff119a 259=head2 number_of_args
260
d4e8996f 261Returns the number of args this action expects. This is 0 if the action doesn't
262take any arguments and undef if it will take any number of arguments.
263
264=head2 normalized_arg_number
265
266For the purposes of comparison we normalize 'number_of_args' so that if it is
267undef we mean ~0 (as many args are we can think of).
0cff119a 268
269=head2 number_of_captures
270
271Returns the number of captures this action expects for L<Chained|Catalyst::DispatchType::Chained> actions.
272
3c0da3ec 273=head2 list_extra_info
274
ffca3e96 275A HashRef of key-values that an action can provide to a debugging screen
3c0da3ec 276
342d2169 277=head2 scheme
278
279Any defined scheme for the action
280
059c085b 281=head2 meta
282
18a9655c 283Provided by Moose.
059c085b 284
2f381252 285=head1 AUTHORS
fbcc39ad 286
2f381252 287Catalyst Contributors, see Catalyst.pm
fbcc39ad 288
289=head1 COPYRIGHT
290
536bee89 291This library is free software. You can redistribute it and/or modify it under
fbcc39ad 292the same terms as Perl itself.
293
85d9fce6 294=cut
81436df9 295
296