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