File Coverage

blib/lib/Catalyst/ActionRole/MethodSignatureDependencyInjection.pm
Criterion Covered Total %
statement 125 223 56.0
branch 56 112 50.0
condition 5 11 45.4
subroutine 24 44 54.5
pod 1 3 33.3
total 211 393 53.6


line stmt bran cond sub pod time code
1             package Catalyst::ActionRole::MethodSignatureDependencyInjection;
2              
3 3     3   1066036 use Moose::Role;
  3         8  
  3         34  
4 3     3   15373 use Carp;
  3         6  
  3         9830  
5              
6             our $VERSION = '0.020';
7              
8             has use_prototype => (
9             is=>'ro',
10             required=>1,
11             lazy=>1,
12             builder=>'_build_prototype');
13              
14             sub _build_at {
15 0     0   0 my ($self) = @_;
16 0 0       0 my ($attr) = @{$self->attributes->{UsePrototype}||[0]};
  0         0  
17 0         0 return $attr;
18             }
19              
20             has execute_args_template => (
21             is=>'ro',
22             required=>1,
23             lazy=>1,
24             builder=>'_build_execute_args_template');
25              
26             sub _build_execute_args_template {
27 1     1   3 my ($self) = @_;
28 1 50       2 my ($attr) = @{$self->attributes->{ExecuteArgsTemplate}||['']};
  1         29  
29 1         55 return $attr;
30             }
31              
32             has prototype => (
33             is=>'ro',
34             required=>1,
35             lazy=>1,
36             builder=>'_build_prototype');
37              
38             sub _build_prototype {
39 21     21   36 my ($self) = @_;
40 21 50       65 if($INC{'Function/Parameters.pm'}) {
41             return join ',',
42 0 0       0 map {$_->type? $_->type->class : $_->name}
  0         0  
43             Function::Parameters::info($self->code)->positional_required;
44             } else {
45 21         617 return prototype($self->code);
46             }
47             }
48              
49             has template => (
50             is=>'ro',
51             required=>1,
52             lazy=>1,
53             builder=>'_build_template');
54              
55             sub _build_template {
56 11     11   19 my ($self) = @_;
57 11 100       531 return $self->use_prototype ?
58             $self->prototype : $self->execute_args_template;
59             }
60              
61             sub parse_injection_spec_section {
62 41     41 0 54 my ($self) = @_;
63              
64             # These Regexps could be better to allow more whitespace.
65 41         120 my $p = qr/[^,]+/;
66 41         199 my $p2 = qr/$p<.+?>$p/x;
67              
68 41         316 $_[1]=~/\s*($p2|$p)\s*/gxc;
69              
70 41         192 return $1;
71             }
72              
73             has dependency_builder => (
74             is=>'ro',
75             required=>1,
76             isa=>'ArrayRef',
77             lazy=>1,
78             builder=>'_dependency_builder');
79              
80             sub _dependency_builder {
81 11     11   27 my $self = shift;
82 11         543 my $template = $self->template;
83 11         36 my @parsed = $self->_parse_dependencies($template);
84              
85 11         629 return \@parsed;
86             }
87              
88             sub _parse_dependencies {
89 11     11   20 my ($self, $template) = @_;
90 11         21 my @what = ();
91 11         22 for($template) {
92             PARSE: {
93 11 50       15 last PARSE unless length;
  11         31  
94 11         17 do {
95 41   50     92 push @what, $self->parse_injection_spec_section($_)
96             || die "trouble parsing action $self template '$template'";
97 41 100       161 last PARSE if (pos == length);
98             } until (pos == length);
99             }
100             }
101              
102             #cope with older Perls trimming whitspace from prototypes. I supposed
103             #this kills any models that end in 'required'...
104 11 100       569 if($self->use_prototype) {
105             @what = map {
106 10         20 $_=~m/\Srequired$/
107 33 50       115 ? do { $_=~s/required$//; "$_ required" }
  0         0  
  0         0  
108             : $_
109             } @what;
110             }
111              
112 11         59 return @what;
113             }
114              
115             has prepared_dependencies => (
116             is=>'ro',
117             required=>1,
118             isa=>'ArrayRef',
119             lazy=>1,
120             builder=>'_build_prepared_dependencies');
121              
122 36     36 0 191 sub not_required { return bless \(my $cb = shift), __PACKAGE__.'::not_required'; }
123 5     5 1 27 sub required { return bless \(my $cb = shift), __PACKAGE__.'required'; }
124              
125             sub _prepare_dependencies {
126 11     11   33 my ($self, @what) = @_;
127 11         19 my $arg_count = 0;
128 11         13 my $capture_count = 0;
129 11         20 my @dependencies = ();
130              
131 11         35 while(my $what = shift @what) {
132 41 100   13   181 my $method = $what =~m/required/ ? sub {required(shift) } : sub { not_required(shift) };
  5         14  
  36         84  
133 41 100   4   114 do { push @dependencies, $method->(sub { shift }); next } if lc($what) eq '$ctx';
  4         17  
  4         8  
  4         21  
134 37 100   2   78 do { push @dependencies, $method->(sub { shift }); next } if lc($what) eq '$c';
  4         16  
  2         5  
  4         17  
135 33 50   0   66 do { push @dependencies, $method->(sub { shift->state }); next } if lc($what) eq '$state';
  0         0  
  0         0  
  0         0  
136 33 100   2   69 do { push @dependencies, $method->(sub { shift->req }); next } if lc($what) eq '$req';
  2         9  
  2         15  
  2         8  
137 31 50   0   123 do { push @dependencies, $method->(sub { shift->req }); next } if lc($what) eq '$request';
  0         0  
  0         0  
  0         0  
138 31 50   0   68 do { push @dependencies, $method->(sub { shift->req->env }); next } if lc($what) eq '$env';
  0         0  
  0         0  
  0         0  
139              
140 31 100   6   66 do { push @dependencies, $method->(sub { shift->res }); next } if lc($what) eq '$res';
  6         30  
  6         43  
  6         26  
141 25 50   0   56 do { push @dependencies, $method->(sub { shift->res }); next } if lc($what) eq '$response';
  0         0  
  0         0  
  0         0  
142              
143 25 50   0   57 do { push @dependencies, $method->(sub { shift->req->args}); next } if lc($what) eq '$args';
  0         0  
  0         0  
  0         0  
144 25 50   2   52 do { push @dependencies, $method->(sub { shift->req->body_data||+{} }); next } if lc($what) eq '$bodydata';
  2 100       9  
  2         8  
  2         9  
145 23 100   2   50 do { push @dependencies, $method->(sub { shift->req->body_parameters}); next } if lc($what) eq '$bodyparams';
  2         13  
  2         8  
  2         9  
146 21 100   2   49 do { push @dependencies, $method->(sub { shift->req->query_parameters}); next } if lc($what) eq '$queryparams';
  2         8  
  2         7  
  2         7  
147              
148             #This will blow stuff up unless its the last...
149 19 50   0   46 do { push @dependencies, $method->(sub { @{shift->req->args}}) ; next } if lc($what) eq '@args';
  0         0  
  0         0  
  0         0  
  0         0  
150 19 50   0   43 do { push @dependencies, $method->(sub { %{shift->req->body_parameters}}); next } if lc($what) eq '%bodyparams';
  0         0  
  0         0  
  0         0  
  0         0  
151 19 0   0   41 do { push @dependencies, $method->(sub { %{shift->req->body_data||+{}}}); next } if lc($what) eq '%bodydata';
  0 50       0  
  0         0  
  0         0  
  0         0  
152              
153 19 50   0   57 do { push @dependencies, $method->(sub { %{shift->req->query_parameters}}); next } if lc($what) eq '%queryparams';
  0         0  
  0         0  
  0         0  
  0         0  
154 19 0   0   45 do { push @dependencies, $method->(sub { %{shift->req->body_data||+{}}}); next } if lc($what) eq '%body';
  0 50       0  
  0         0  
  0         0  
  0         0  
155 19 50   0   41 do { push @dependencies, $method->(sub { %{shift->req->query_parameters}}); next } if lc($what) eq '%query';
  0         0  
  0         0  
  0         0  
  0         0  
156              
157             # Default view and model
158             # For now default model / view can't be parameterized.
159 19 100 100 2   121 do { push @dependencies, $method->(sub { shift->model}) ; next } if($what =~/^Model/ && $what!~/^Model\:/);
  2         10  
  2         13  
  2         11  
160 17 50 33 0   52 do { push @dependencies, $method->(sub { shift->view}) ; next } if($what =~/^View/ && $what!~/^View\:/);
  0         0  
  0         0  
  0         0  
161            
162 17 100       57 if(defined(my $arg_index = ($what =~/^\$?Arg(\d+).*$/i)[0])) {
163 1     1   6 push @dependencies, $method->(sub { shift->req->args->[$arg_index] });
  1         5  
164 1         2 $arg_count = undef;
165 1         5 next;
166             }
167              
168 16 50       44 if($what=~/^\$?Args\s/) {
169 0     0   0 push @dependencies, $method->(sub { @{shift->req->args}}); # need to die if this is not the last..
  0         0  
  0         0  
170 0         0 next;
171             }
172              
173 16 100       48 if($what =~/^\$?Arg\s?.*/i) {
174             # count arg
175 2 50       66 confess "You can't mix numbered args and unnumbered args in the same signature" unless defined $arg_count;
176 2         4 my $local = $arg_count;
177 2     2   9 push @dependencies, $method->(sub { shift->req->args->[$local]}) ;
  2         8  
178 2         3 $arg_count++;
179 2         10 next;
180             }
181              
182 14 50       35 if($what =~/^\$?Capture\s?.*/i) {
183             # count arg
184 0         0 my $local = $capture_count;
185 0 0       0 confess "You can't mix numbered captures and unnumbered captures in the same signature" unless defined $arg_count;
186 0     0   0 push @dependencies, $method->(sub { my ($c, @args) = @_; return $args[$local] });
  0         0  
  0         0  
187 0         0 $capture_count++;
188             next
189 0         0 }
190              
191 14 50       38 if(defined(my $capture_index = ($what =~/^\$?Capture(\d+).*$/i)[0])) {
192             # If they are asking for captures, we look at @args.. sorry
193 0         0 my $local = $capture_index;
194 0     0   0 push @dependencies, $method->(sub { my ($c, @args) = @_; $args[$local] });
  0         0  
  0         0  
195 0         0 next;
196             }
197              
198 14 50       38 if($what=~/^Model\:\:/i) {
199             # Its a model. Could be:
200             # -- Model::Foo
201             # -- Model::Foo $foo
202             # -- Model::Foo $foo isa Int
203             # -- Model::Foo $foo isa Int required
204             # -- Model::Foo<$params>
205             # -- Model::Foo<$params> $foo
206             # -- Model::Foo<$params> $foo isa Int
207             # -- Model::Foo<$params> $foo isa Int required
208             # For where $params is any sort of parsable spec (Arg, Arg $id, Arg $id isa Int, ...)
209            
210             # first get the model name
211 14         21 my @inner_deps = ();
212 14         40 my ($model) = ($what=~m/^Model\:\:([\w\:]+)/i);
213              
214 14 50       28 die "Can't seem to extract a model name from '$what'!" unless $model;
215              
216 14         23 my ($rest) = ($what =~/<([^>]+)/);
217              
218             # Is the model parameterized??
219 14 50       25 if(defined($rest)) {
220 0         0 @inner_deps = $self->_prepare_dependencies($self->_parse_dependencies($rest));
221             }
222              
223 14 50       35 push @dependencies, @inner_deps if @inner_deps;
224             push @dependencies, $method->(sub {
225 12     12   19 my ($c, @args) = @_;
226              
227             # Make sure the $model is a component we already know about.
228             die "'$model' is not a defined component (parsed out of '$what'"
229 12 50       41 unless $c->components->{ ref($c).'::Model::'.$model };
230              
231 12         307 my ($ret, @rest) = $c->model($model, map { $$_->($c, @args) } @inner_deps);
  0         0  
232 12 50       557 warn "$model returns more than one arg" if @rest;
233 12         31 return $ret;
234 14         88 });
235 14         62 next;
236             }
237              
238 0 0       0 if($what=~/^View\:\:/i) {
239 0         0 my @inner_deps = ();
240 0         0 my ($view) = ($what=~m/^View\:\:([\w\:]+)/i);
241              
242 0 0       0 die "Can't seem to extract a view name from '$what'!" unless $view;
243              
244 0         0 my ($rest) = ($what =~/<([^>]+)/);
245              
246 0 0       0 if(defined($rest)) {
247 0         0 @inner_deps = $self->_prepare_dependencies($self->_parse_dependencies($rest));
248             }
249              
250 0 0       0 push @dependencies, @inner_deps if @inner_deps;
251             push @dependencies, $method->(sub {
252 0     0   0 my ($c, @args) = @_;
253              
254             die "'$view' is not a defined component (parsed out of '$what'"
255 0 0       0 unless $c->components->{ ref($c).'::View::'.$view };
256              
257 0         0 my ($ret, @rest) = $c->view($view, map { $$_->($c, @args) } @inner_deps);
  0         0  
258 0 0       0 warn "$view returns more than one arg" if @rest;
259 0         0 return $ret;
260 0         0 });
261 0         0 next;
262             }
263              
264 0 0 0     0 if(my $controller = ($what =~/^Controller\:\:(.+?)\s+.+$/)[0] || ($what =~/^Controller\:\:(.+)\s+.+$/)[0]) {
265             push @dependencies, $method->(sub {
266 0     0   0 my $c = shift;
267              
268             # Make sure the $controller is a component we already know about.
269             die "$controller is not a defined component"
270 0 0       0 unless $c->components->{ ref($c).'::Controller::'.$controller };
271              
272 0         0 my ($ret, @rest) = $c->controller($controller);
273 0 0       0 warn "$controller returns more than one arg" if @rest;
274 0         0 return $ret;
275 0         0 });
276 0         0 next;
277             }
278              
279 0         0 die "Found undefined Token in action $self signature '${\$self->template}' => '$what'";
  0         0  
280             }
281              
282 11 50       33 unless(scalar @dependencies) {
283             @dependencies = (
284 0     0   0 not_required(sub { return $_[0] }),
285 0     0   0 not_required(sub { return @{$_[0]->req->args} }),
  0         0  
  0         0  
286             );
287             }
288              
289 11         581 return @dependencies;
290             }
291              
292             sub _build_prepared_dependencies {
293 11     11   19 my ($self) = @_;
294 11         23 my @what = @{$self->dependency_builder};
  11         577  
295 11         43 return [ $self->_prepare_dependencies(@what) ];
296             }
297              
298             around ['match', 'match_captures'] => sub {
299             my ($orig, $self, $ctx, $args) = @_;
300             return 0 unless $self->$orig($ctx, $args);
301              
302             # For chain captures, we find @args, but not for args...
303             # So we have to normalize.
304            
305             my @args = scalar(@{$args||[]}) ? @{$args||[]} : @{$ctx->req->args||[]};
306              
307             my @resolved = ();
308             foreach my $dependency (@{ $self->prepared_dependencies }) {
309             my $required = $dependency=~m/not_required/ ? 0:1;
310             if($required) {
311             my $ret = $$dependency->($ctx, @args);
312             unless(defined $ret) {
313             return 0;
314             } else {
315             push @resolved, $ret;
316             }
317             } else {
318             push @resolved, $dependency;
319             }
320             }
321              
322             $ctx->stash->{__method_signature_dependencies_keys}->{"$self"} = \@resolved;
323             return 1;
324             };
325              
326             around 'execute', sub {
327             my ($orig, $self, $controller, $ctx, @args) = @_;
328             my $stash_key = $self .'__method_signature_dependencies';
329             my @dependencies = map { $_=~m/not_required/ ? $$_->($ctx, @args) : $_ }
330             @{$ctx->stash->{__method_signature_dependencies_keys}->{"$self"}};
331              
332             return $self->$orig($controller, @dependencies);
333             };
334              
335             1;
336              
337             =head1 NAME
338              
339             Catalyst::ActionRole::MethodSignatureDependencyInjection - Experimental Action Signature Dependency Injection
340              
341             =head1 SYNOPSIS
342              
343             Attribute syntax:
344              
345             package MyApp::Controller
346             use base 'Catalyst::Controller';
347              
348             sub test_model :Local :Does(MethodSignatureDependencyInjection)
349             ExecuteArgsTemplate($c, $Req, $Res, $BodyData, $BodyParams, $QueryParams, Model::A, Model::B)
350             {
351             my ($self, $c, $Req, $Res, $Data, $Params, $Query, $A, $B) = @_;
352             }
353              
354             Prototype syntax
355              
356             package MyApp::Controller
357             use base 'Catalyst::Controller';
358              
359             no warnings::illegalproto;
360              
361             sub test_model($c, $Req, $Res, $BodyData, $BodyParams, $QueryParams, Model::A required, Model::B)
362             :Local :Does(MethodSignatureDependencyInjection) UsePrototype(1)
363             {
364             my ($self, $c, $Req, $Res, $Data, $Params, $Query, $A, $B) = @_;
365             }
366              
367             With required model injection:
368              
369             package MyApp::Controller
370             use base 'Catalyst::Controller';
371              
372             no warnings::illegalproto;
373              
374             sub chainroot :Chained(/) PathPrefix CaptureArgs(0) { }
375              
376             sub no_null_chain_1( $c, Model::ReturnsNull, Model::ReturnsTrue)
377             :Chained(chainroot) PathPart('no_null_chain')
378             :Does(MethodSignatureDependencyInjection) UsePrototype(1)
379             {
380             my ($self, $c) = @_;
381             return $c->res->body('no_null_chain_1');
382             }
383              
384             sub no_null_chain_2( $c, Model::ReturnsNull required, Model::ReturnsTrue required)
385             :Chained(chainroot) PathPart('no_null_chain')
386             :Does(MethodSignatureDependencyInjection) UsePrototype(1)
387             {
388             my ($self, $c) = @_;
389             return $c->res->body('no_null_chain_2');
390             }
391              
392              
393             =head1 WARNING
394              
395             Lets you declare required action dependencies via the a subroutine attribute
396             and additionally via the prototype (if you dare)
397              
398             This is a weakly documented, early access prototype. The author reserves the
399             right to totally change everything and potentially disavow all knowledge of it.
400             Only report bugs if you are capable of offering a patch and discussion.
401              
402             B<UPDATE> This module is starting to stablize, and I'd be interested in seeing
403             people use it and getting back to me on it. But I do recommend using it only
404             if you feel like its code you understand.
405              
406             Please note if any of the declared dependencies return undef, that will cause
407             the action to not match. This could probably be better warning wise...
408              
409             =head1 DESCRIPTION
410              
411             L<Catalyst> when dispatching a request to an action calls the L<Action::Class>
412             execute method with the following arguments ($self, $c, @args). This you likely
413             already know (if you are a L<Catalyst> programmer).
414              
415             This action role lets you describe an alternative 'template' to be used for
416             what arguments go to the execute method. This way instead of @args you can
417             get a model, or a view, or something else. The goal of this action role is
418             to make your action bodies more concise and clear and to have your actions
419             declare what they want.
420              
421             Additionally, when we build these arguments, we also check their values and
422             require them to be true during the match/match_captures phase. This means
423             you can actually use this to control how an action is matched.
424              
425             There are two ways to use this action role. The default way is to describe
426             your execute template using the 'ExecuteArgsTemplate' attribute. The
427             second is to enable UsePrototype (via the UsePrototype(1) attribute) and
428             then you can declare your argument template via the method prototype. You
429             will of course need to use 'no warnings::illegalproto' for this to work.
430             The intention here is to work toward something that would play nice with
431             a system for method signatures like L<Kavorka>.
432              
433             If this sounds really verbose it is. This distribution is likely going to
434             be part of something larger that offers more sugar and less work, just it was
435             clearly also something that could be broken out and hacked pn separately.
436             If you use this you might for example set this action role in a base controller
437             such that all your controllers get it (one example usage).
438              
439             Please note that you must still access your arguments via C<@_>, this is not
440             a method signature framework. You can take a look at L<Catalyst::ActionSignatures>
441             for a system that bundles this all up more neatly.
442              
443             =head1 DEPENDENCY INJECTION
444              
445             You define your execute arguments as a positioned list (for now). The system
446             recognizes the following 'built ins' (you always get $self automatically).
447              
448             B<NOTE> These arguments are matched using a case insensitive regular expression
449             so generally whereever you see $arg you can also use $Arg or $ARG.
450              
451             =head2 $c
452              
453             =head2 $ctx
454              
455             The current context. You are encouraged to more clearly name your action
456             dependencies, but its here if you need it.
457              
458             =head2 $req
459              
460             =head2 $request
461              
462             The current L<Catalyst::Request>
463              
464             =head2 $res
465              
466             =head2 $request
467              
468             The current L<Catalyst::Response>
469              
470             =head2 $args
471              
472             An arrayref of the current args
473              
474             =head2 args
475             =head2 @args
476              
477             An array of the current args. Only makes sense if this is the last specified
478             argument.
479              
480             =head2 $arg0 .. $argN
481              
482             =head2 arg0 ... argN
483              
484             One of the indexed args, where $args0 => $args[0];
485              
486             =head2 arg
487              
488             =head2 $arg
489              
490             If you use 'arg' without a numbered index, we assume an index based on the number
491             of such 'un-numbered' args in your signature. For example:
492              
493             ExecuteArgsTemplate(Arg, Arg)
494              
495             Would match two arguments $arg->[0] and $args->[1]. You cannot use both numbered
496             and un-numbered args in the same signature.
497              
498             B<NOTE>This also works with the 'Args' special 'zero or more' match. So for
499             example:
500              
501             sub argsargs($res, Args @ids) :Local {
502             $res->body(join ',', @ids);
503             }
504              
505             Is the same as:
506              
507             sub argsargs($res, Args @ids) :Local Args {
508             $res->body(join ',', @ids);
509             }
510              
511             =head2 $captures
512              
513             An arrayref of the current CaptureArgs (used in Chained actions).
514              
515             =head2 @captures
516              
517             An array of the current CaptureArgs. Only makes sense if this is the last specified
518             argument.
519              
520             =head2 $capture0 .. $captureN
521              
522             =head2 capture0 ... captureN
523              
524             One of the indexed Capture Args, where $capture0 => $capture0[0];
525              
526             =head2 capture
527              
528             If you use 'capture' without a numbered index, we assume an index based on the number
529             of such 'un-numbered' args in your signature. For example:
530              
531             ExecuteArgsTemplate(Capture, Capture)
532              
533             Would match two arguments $capture->[0] and $capture->[1]. You cannot use both numbered
534             and un-numbered capture args in the same signature.
535              
536             =head2 $bodyData
537              
538             =head2 $bodydata
539              
540             $c->req->body_data
541              
542             =head2 $bodyParams
543              
544             =head2 $bodyparams
545              
546             $c->req->body_parameters
547              
548             =head2 $QueryParams
549              
550             =head2 $queryparams
551              
552             $c->req->query_parameters
553              
554             =head2 %query
555              
556             A hash of the information in $c->req->query_parameters. Must be the last argument in the
557             signature.
558              
559             =head2 %body
560              
561             A hash of the information in $c->req->body_data. Must be the last argument in the
562             signature.
563              
564             =head1 Accessing Components
565              
566             You can request a L<Catalyst> component (a model, view or controller). You
567             do this via [Model||View||Controller]::$component_name. For example if you
568             have a model that you'd normally access like this:
569              
570             $c->model("Schema::User");
571              
572             You would say "Model::Schema::User". For example:
573              
574             ExecuteArgsTemplate(Model::Schema::User)
575              
576             Or via the prototype
577              
578             sub myaction(Model::Schema::User) ...
579              
580             You can also pass arguments to your models. For example:
581              
582             ExecuteArgsTemplate(Model::UserForm<Model::User>)
583              
584             same as $c->model('UserForm', $c->model('User'));
585              
586             =head1 Default Components
587              
588             You may specify the current view or model by just using the declaration 'Model'
589             or 'View'. For example:
590              
591             package MyApp;
592             use Catalyst;
593            
594             # We assume MyApp::Model::Default
595             MyApp->config(default_model=>'Default');
596             MyApp->setup;
597              
598              
599             sub default_model($res,Model) :Local
600             :Does(MethodSignatureDependencyInjection) UsePrototype(1)
601             {
602             my ($self, $res, $model) = @_;
603             $res->body($model); # isa Model::Default
604             }
605              
606             sub chainroot :Chained(/) PathPrefix CaptureArgs(0) {
607             my ($self, $ctx) = @_;
608             $ctx->stash(current_model_instance => 100);
609             }
610              
611             sub default_again($res,Model required) :Chained(chainroot)
612             :Does(MethodSignatureDependencyInjection) UsePrototype(1)
613             {
614             my ($self, $res, $model) = @_;
615             return $res->body($model); # is 100
616             }
617              
618             Can be useful to help make controllers less tightly bound.
619              
620             =head1 Component Modifiers
621              
622             =head2 required
623              
624             Used to declare a component injection (Model, View or Controller) is 'required'. This means
625             it must return something that Perl thinks of as true (for now we exclude both undef and 0 since
626             I think that is less surprising and the use cases where a model validately return 0 seems
627             small). When a component is required, we resolve it during the match/match_captures phase of
628             dispatch and the action will fail to match should the component fail the required condition.
629             Useful if you use models as a we to determine if an action should run or no.
630              
631             B<NOTE> Since 'required' components get resolved during the match/match_captures phase, this
632             means that certain parts of the context have not been completed. For example $c->action will
633             be null (since we have not yet determined a matching action or not). If your model does
634             ACCEPT_CONTEXT and need $c->action, it cannot be required. I think this is the main thing
635             undefined with context at this phase but other bits may emerge so test carefully.
636              
637             =head1 Integration with Catalyst::ActionSignatures
638              
639             This action role will work with L<Catalyst::ActionSignatures> automatically and
640             all features are present.
641              
642             =head1 Integration with Function::Parameters
643              
644             For those of you that would like to push the limits even harder, we have
645             experimental support for L<Function::Parameters>. You may use like in the
646             following example.
647              
648             package MyApp::Controller::Root;
649              
650             use base 'Catalyst::Controller';
651              
652             use Function::Parameters({
653             method => {defaults => 'method'},
654             action => {
655             attributes => ':method :Does(MethodSignatureDependencyInjection) UsePrototype(1)',
656             shift => '$self',
657             check_argument_types => 0,
658             strict => 0,
659             default_arguments => 1,
660             }});
661              
662             action test_model($c, $res, Model::A $A, Model::Z $Z)
663             :Local
664             {
665             # ...
666             $res->body(...);
667             }
668              
669             method test($a) {
670             return $a;
671             }
672              
673             Please note that currently you cannot use the 'parameterized' syntax for component
674             injection (no Model::A<Model::Z> support).
675              
676             =head1 SEE ALSO
677              
678             L<Catalyst::Action>, L<Catalyst>, L<warnings::illegalproto>,
679             L<Catalyst::ActionSignatures>
680              
681             =head1 AUTHOR
682            
683             John Napiorkowski L<email:jjnapiork@cpan.org>
684            
685             =head1 COPYRIGHT & LICENSE
686            
687             Copyright 2015, John Napiorkowski L<email:jjnapiork@cpan.org>
688            
689             This library is free software; you can redistribute it and/or modify it under
690             the same terms as Perl itself.
691              
692             =cut