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