new match and match captutres for http methods, plus tests, docs
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Action.pm
1 package Catalyst::Action;
2
3 =head1 NAME
4
5 Catalyst::Action - Catalyst Action
6
7 =head1 SYNOPSIS
8
9     <form action="[%c.uri_for(c.action)%]">
10
11     $c->forward( $action->private_path );
12
13 =head1 DESCRIPTION
14
15 This class represents a Catalyst Action. You can access the object for the
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
22 use Moose;
23 use Scalar::Util 'looks_like_number';
24 with 'MooseX::Emulate::Class::Accessor::Fast';
25 use namespace::clean -except => 'meta';
26
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');
33 has private_path => (
34   reader => 'private_path',
35   isa => 'Str',
36   lazy => 1,
37   required => 1,
38   default => sub { '/'.shift->reverse },
39 );
40
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
56 no warnings 'recursion';
57
58 sub dispatch {    # Execute ourselves against a context
59     my ( $self, $c ) = @_;
60     return $c->execute( $self->class, $self );
61 }
62
63 sub execute {
64   my $self = shift;
65   $self->code->(@_);
66 }
67
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
77 sub match {
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   }
109 }
110
111 sub compare {
112     my ($a1, $a2) = @_;
113
114     my ($a1_args) = @{ $a1->attributes->{Args} || [] };
115     my ($a2_args) = @{ $a2->attributes->{Args} || [] };
116
117     $_ = looks_like_number($_) ? $_ : ~0
118         for $a1_args, $a2_args;
119
120     return $a1_args <=> $a2_args;
121 }
122
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
136 __PACKAGE__->meta->make_immutable;
137
138 1;
139
140 __END__
141
142 =head1 METHODS
143
144 =head2 attributes
145
146 The sub attributes that are set for this action, like Local, Path, Private
147 and so on. This determines how the action is dispatched to.
148
149 =head2 class
150
151 Returns the name of the component where this action is defined.
152 Derived by calling the L<Catalyst::Component/catalyst_component_name|catalyst_component_name>
153 method on each component.
154
155 =head2 code
156
157 Returns a code reference to this action.
158
159 =head2 dispatch( $c )
160
161 Dispatch this action against a context.
162
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
168 =head2 match( $c )
169
170 Check Args attribute, and makes sure number of args matches the setting.
171 Always returns true if Args is omitted.
172
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
183 =head2 compare
184
185 Compares 2 actions based on the value of the C<Args> attribute, with no C<Args>
186 having the highest precedence.
187
188 =head2 namespace
189
190 Returns the private namespace this action lives in.
191
192 =head2 reverse
193
194 Returns the private path for this action.
195
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
201 =head2 name
202
203 Returns the sub name of this action.
204
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
213 =head2 meta
214
215 Provided by Moose.
216
217 =head1 AUTHORS
218
219 Catalyst Contributors, see Catalyst.pm
220
221 =head1 COPYRIGHT
222
223 This library is free software. You can redistribute it and/or modify it under
224 the same terms as Perl itself.
225
226 =cut