remove some potentially confusing syntax
[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 @args =
103 map { Moose::Util::TypeConstraints::find_or_parse_type_constraint($_) || die "$_ is not a constraint!" }
337a627a 104 @arg_protos;
6d62355b 105 }
106
107 return \@args;
108 }
109
2055d9ad 110use overload (
111
112 # Stringify to reverse for debug output etc.
113 q{""} => sub { shift->{reverse} },
114
115 # Codulate to execute to invoke the encapsulated action coderef
116 '&{}' => sub { my $self = shift; sub { $self->execute(@_); }; },
117
118 # Make general $stuff still work
119 fallback => 1,
120
121);
122
059c085b 123no warnings 'recursion';
124
b2ddf6d7 125sub dispatch { # Execute ourselves against a context
126 my ( $self, $c ) = @_;
049f82e2 127 return $c->execute( $self->class, $self );
b2ddf6d7 128}
fbcc39ad 129
b2ddf6d7 130sub execute {
131 my $self = shift;
059c085b 132 $self->code->(@_);
b2ddf6d7 133}
fbcc39ad 134
b2ddf6d7 135sub match {
60034b8c 136 my ( $self, $c ) = @_;
81436df9 137
d4e8996f 138 # If infinite args, we always match
139 return 1 if $self->normalized_arg_number == ~0;
140
141 # There there are arg constraints, we must see to it that the constraints
142 # check positive for each arg in the list.
81436df9 143 if($self->has_args_constraints) {
4a0218ca 144 # If there is only one type constraint, and its a Ref or subtype of Ref,
145 # That means we expect a reference, so use the full args arrayref.
146 if(
147 $self->number_of_args_constraints == 1 &&
148 $self->args_constraints->[0]->is_a_type_of('Ref')
149 ) {
150 return $self->args_constraints->[0]->check($c->req->args);
151 } else {
152 for my $i($#{ $c->req->args }) {
153 $self->args_constraints->[$i]->check($c->req->args->[$i]) || return 0;
154 }
155 return 1;
6d62355b 156 }
6d62355b 157 } else {
d4e8996f 158 # Otherwise, we just need to match the number of args.
159 return scalar( @{ $c->req->args } ) == $self->normalized_arg_number;
6d62355b 160 }
760d121e 161}
162
60034b8c 163sub match_captures { 1 }
fbcc39ad 164
05b47f2e 165sub compare {
166 my ($a1, $a2) = @_;
d4e8996f 167 return $a1->normalized_arg_number <=> $a2->normalized_arg_number;
05b47f2e 168}
169
0cff119a 170sub number_of_captures {
171 my ( $self ) = @_;
172
173 return 0 unless exists $self->attributes->{CaptureArgs};
174 return $self->attributes->{CaptureArgs}[0] || 0;
175}
176
342d2169 177sub scheme {
178 return exists $_[0]->attributes->{Scheme} ? $_[0]->attributes->{Scheme}[0] : undef;
179}
180
ffca3e96 181sub list_extra_info {
182 my $self = shift;
183 return {
184 Args => $self->attributes->{Args}[0],
185 CaptureArgs => $self->number_of_captures,
186 }
187}
3c0da3ec 188
e5ecd5bc 189__PACKAGE__->meta->make_immutable;
190
b2ddf6d7 1911;
fbcc39ad 192
b2ddf6d7 193__END__
4ab87e27 194
fbcc39ad 195=head1 METHODS
196
b5ecfcf0 197=head2 attributes
fbcc39ad 198
4ab87e27 199The sub attributes that are set for this action, like Local, Path, Private
b2ddf6d7 200and so on. This determines how the action is dispatched to.
4ab87e27 201
b5ecfcf0 202=head2 class
b96f127f 203
4d38cb07 204Returns the name of the component where this action is defined.
f9818250 205Derived by calling the L<catalyst_component_name|Catalyst::Component/catalyst_component_name>
fb0c5b21 206method on each component.
4ab87e27 207
b5ecfcf0 208=head2 code
11bd4e3e 209
b2ddf6d7 210Returns a code reference to this action.
4ab87e27 211
b8f669f3 212=head2 dispatch( $c )
4ab87e27 213
18a9655c 214Dispatch this action against a context.
fbcc39ad 215
b8f669f3 216=head2 execute( $controller, $c, @args )
217
218Execute this action's coderef against a given controller with a given
219context and arguments
220
649fd1fa 221=head2 match( $c )
4ab87e27 222
649fd1fa 223Check Args attribute, and makes sure number of args matches the setting.
b2ddf6d7 224Always returns true if Args is omitted.
4082e678 225
760d121e 226=head2 match_captures ($c, $captures)
227
228Can be implemented by action class and action role authors. If the method
229exists, then it will be called with the request context and an array reference
230of the captures for this action.
231
232Returning true from this method causes the chain match to continue, returning
233makes the chain not match (and alternate, less preferred chains will be attempted).
234
235
91955398 236=head2 compare
237
cbe555e8 238Compares 2 actions based on the value of the C<Args> attribute, with no C<Args>
239having the highest precedence.
91955398 240
b5ecfcf0 241=head2 namespace
fbcc39ad 242
4ab87e27 243Returns the private namespace this action lives in.
244
b5ecfcf0 245=head2 reverse
6b239949 246
4ab87e27 247Returns the private path for this action.
248
009b5b23 249=head2 private_path
250
251Returns absolute private path for this action. Unlike C<reverse>, the
252C<private_path> of an action is always suitable for passing to C<forward>.
253
b5ecfcf0 254=head2 name
fbcc39ad 255
18a9655c 256Returns the sub name of this action.
4ab87e27 257
0cff119a 258=head2 number_of_args
259
d4e8996f 260Returns the number of args this action expects. This is 0 if the action doesn't
261take any arguments and undef if it will take any number of arguments.
262
263=head2 normalized_arg_number
264
265For the purposes of comparison we normalize 'number_of_args' so that if it is
266undef we mean ~0 (as many args are we can think of).
0cff119a 267
268=head2 number_of_captures
269
270Returns the number of captures this action expects for L<Chained|Catalyst::DispatchType::Chained> actions.
271
3c0da3ec 272=head2 list_extra_info
273
ffca3e96 274A HashRef of key-values that an action can provide to a debugging screen
3c0da3ec 275
342d2169 276=head2 scheme
277
278Any defined scheme for the action
279
059c085b 280=head2 meta
281
18a9655c 282Provided by Moose.
059c085b 283
2f381252 284=head1 AUTHORS
fbcc39ad 285
2f381252 286Catalyst Contributors, see Catalyst.pm
fbcc39ad 287
288=head1 COPYRIGHT
289
536bee89 290This library is free software. You can redistribute it and/or modify it under
fbcc39ad 291the same terms as Perl itself.
292
85d9fce6 293=cut
81436df9 294
295