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