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