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'; |
241edc9b |
24 | with 'MooseX::Emulate::Class::Accessor::Fast'; |
05b47f2e |
25 | use namespace::clean -except => 'meta'; |
241edc9b |
26 | |
5fb12dbb |
27 | has class => (is => 'rw'); |
28 | has namespace => (is => 'rw'); |
29 | has 'reverse' => (is => 'rw'); |
30 | has attributes => (is => 'rw'); |
31 | has name => (is => 'rw'); |
32 | has code => (is => 'rw'); |
009b5b23 |
33 | has private_path => ( |
34 | reader => 'private_path', |
35 | isa => 'Str', |
36 | lazy => 1, |
37 | required => 1, |
38 | default => sub { '/'.shift->reverse }, |
39 | ); |
059c085b |
40 | |
2055d9ad |
41 | use overload ( |
42 | |
43 | # Stringify to reverse for debug output etc. |
44 | q{""} => sub { shift->{reverse} }, |
45 | |
46 | # Codulate to execute to invoke the encapsulated action coderef |
47 | '&{}' => sub { my $self = shift; sub { $self->execute(@_); }; }, |
48 | |
49 | # Make general $stuff still work |
50 | fallback => 1, |
51 | |
52 | ); |
53 | |
54 | |
55 | |
059c085b |
56 | no warnings 'recursion'; |
57 | |
b2ddf6d7 |
58 | sub dispatch { # Execute ourselves against a context |
59 | my ( $self, $c ) = @_; |
049f82e2 |
60 | return $c->execute( $self->class, $self ); |
b2ddf6d7 |
61 | } |
fbcc39ad |
62 | |
b2ddf6d7 |
63 | sub execute { |
64 | my $self = shift; |
059c085b |
65 | $self->code->(@_); |
b2ddf6d7 |
66 | } |
fbcc39ad |
67 | |
760d121e |
68 | sub match_captures { |
69 | my ( $self, $c, $captures ) = @_; |
70 | ## It would seem that now that we can match captures, we could remove a lot |
71 | ## of the capture_args to args mapping all around. I gave it a go, but was |
72 | ## not trival, contact jnap on irc for what I tried if you want to try. |
73 | ## return $self->_match_has_expected_capture_args($captures) && |
74 | return $self->_match_has_expected_http_method($c->req->method); |
75 | } |
76 | |
b2ddf6d7 |
77 | sub match { |
760d121e |
78 | my ( $self, $c ) = @_; |
79 | return $self->_match_has_expected_args($c->req->args) && |
80 | $self->_match_has_expected_http_method($c->req->method); |
81 | } |
82 | |
83 | sub _match_has_expected_args { |
84 | my ($self, $req_args) = @_; |
85 | return 1 unless exists $self->attributes->{Args}; |
86 | my $args = $self->attributes->{Args}[0]; |
87 | return 1 unless defined($args) && length($args); |
88 | return scalar( @{$req_args} ) == $args; |
89 | } |
90 | |
91 | sub _match_has_expected_capture_args { |
92 | my ($self, $req_args) = @_; |
93 | return 1 unless exists $self->attributes->{CaptureArgs}; |
94 | my $args = $self->attributes->{CaptureArgs}[0]; |
95 | return 1 unless defined($args) && length($args); |
96 | return scalar( @{$req_args} ) == $args; |
97 | } |
98 | |
99 | sub _match_has_expected_http_method { |
100 | my ($self, $method) = @_; |
101 | my @methods = @{ $self->attributes->{Method} || [] }; |
102 | if(scalar @methods) { |
103 | my $result = scalar(grep { lc($_) eq lc($method) } @methods) ? 1:0; |
104 | return $result; |
105 | } else { |
106 | ## No HTTP Methods to check |
107 | return 1; |
108 | } |
b2ddf6d7 |
109 | } |
fbcc39ad |
110 | |
05b47f2e |
111 | sub compare { |
112 | my ($a1, $a2) = @_; |
113 | |
cbe555e8 |
114 | my ($a1_args) = @{ $a1->attributes->{Args} || [] }; |
115 | my ($a2_args) = @{ $a2->attributes->{Args} || [] }; |
116 | |
18a9655c |
117 | $_ = looks_like_number($_) ? $_ : ~0 |
cbe555e8 |
118 | for $a1_args, $a2_args; |
119 | |
120 | return $a1_args <=> $a2_args; |
05b47f2e |
121 | } |
122 | |
0cff119a |
123 | sub number_of_args { |
124 | my ( $self ) = @_; |
125 | return 0 unless exists $self->attributes->{Args}; |
126 | return $self->attributes->{Args}[0]; |
127 | } |
128 | |
129 | sub number_of_captures { |
130 | my ( $self ) = @_; |
131 | |
132 | return 0 unless exists $self->attributes->{CaptureArgs}; |
133 | return $self->attributes->{CaptureArgs}[0] || 0; |
134 | } |
135 | |
e5ecd5bc |
136 | __PACKAGE__->meta->make_immutable; |
137 | |
b2ddf6d7 |
138 | 1; |
fbcc39ad |
139 | |
b2ddf6d7 |
140 | __END__ |
4ab87e27 |
141 | |
fbcc39ad |
142 | =head1 METHODS |
143 | |
b5ecfcf0 |
144 | =head2 attributes |
fbcc39ad |
145 | |
4ab87e27 |
146 | The sub attributes that are set for this action, like Local, Path, Private |
b2ddf6d7 |
147 | and so on. This determines how the action is dispatched to. |
4ab87e27 |
148 | |
b5ecfcf0 |
149 | =head2 class |
b96f127f |
150 | |
4d38cb07 |
151 | Returns the name of the component where this action is defined. |
8f6cebb2 |
152 | Derived by calling the L<Catalyst::Component/catalyst_component_name|catalyst_component_name> |
fb0c5b21 |
153 | method on each component. |
4ab87e27 |
154 | |
b5ecfcf0 |
155 | =head2 code |
11bd4e3e |
156 | |
b2ddf6d7 |
157 | Returns a code reference to this action. |
4ab87e27 |
158 | |
b8f669f3 |
159 | =head2 dispatch( $c ) |
4ab87e27 |
160 | |
18a9655c |
161 | Dispatch this action against a context. |
fbcc39ad |
162 | |
b8f669f3 |
163 | =head2 execute( $controller, $c, @args ) |
164 | |
165 | Execute this action's coderef against a given controller with a given |
166 | context and arguments |
167 | |
649fd1fa |
168 | =head2 match( $c ) |
4ab87e27 |
169 | |
649fd1fa |
170 | Check Args attribute, and makes sure number of args matches the setting. |
b2ddf6d7 |
171 | Always returns true if Args is omitted. |
4082e678 |
172 | |
760d121e |
173 | =head2 match_captures ($c, $captures) |
174 | |
175 | Can be implemented by action class and action role authors. If the method |
176 | exists, then it will be called with the request context and an array reference |
177 | of the captures for this action. |
178 | |
179 | Returning true from this method causes the chain match to continue, returning |
180 | makes the chain not match (and alternate, less preferred chains will be attempted). |
181 | |
182 | |
91955398 |
183 | =head2 compare |
184 | |
cbe555e8 |
185 | Compares 2 actions based on the value of the C<Args> attribute, with no C<Args> |
186 | having the highest precedence. |
91955398 |
187 | |
b5ecfcf0 |
188 | =head2 namespace |
fbcc39ad |
189 | |
4ab87e27 |
190 | Returns the private namespace this action lives in. |
191 | |
b5ecfcf0 |
192 | =head2 reverse |
6b239949 |
193 | |
4ab87e27 |
194 | Returns the private path for this action. |
195 | |
009b5b23 |
196 | =head2 private_path |
197 | |
198 | Returns absolute private path for this action. Unlike C<reverse>, the |
199 | C<private_path> of an action is always suitable for passing to C<forward>. |
200 | |
b5ecfcf0 |
201 | =head2 name |
fbcc39ad |
202 | |
18a9655c |
203 | Returns the sub name of this action. |
4ab87e27 |
204 | |
0cff119a |
205 | =head2 number_of_args |
206 | |
207 | 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. |
208 | |
209 | =head2 number_of_captures |
210 | |
211 | Returns the number of captures this action expects for L<Chained|Catalyst::DispatchType::Chained> actions. |
212 | |
059c085b |
213 | =head2 meta |
214 | |
18a9655c |
215 | Provided by Moose. |
059c085b |
216 | |
2f381252 |
217 | =head1 AUTHORS |
fbcc39ad |
218 | |
2f381252 |
219 | Catalyst Contributors, see Catalyst.pm |
fbcc39ad |
220 | |
221 | =head1 COPYRIGHT |
222 | |
536bee89 |
223 | This library is free software. You can redistribute it and/or modify it under |
fbcc39ad |
224 | the same terms as Perl itself. |
225 | |
85d9fce6 |
226 | =cut |