File Coverage

blib/lib/Catalyst/Controller/ActionRole.pm
Criterion Covered Total %
statement 49 50 98.0
branch 3 4 75.0
condition n/a
subroutine 15 24 62.5
pod 1 2 50.0
total 68 80 85.0


line stmt bran cond sub pod time code
1             package Catalyst::Controller::ActionRole; # git description: v0.16-10-ge946d48
2             # ABSTRACT: (DEPRECATED) Apply roles to action instances
3             our $VERSION = '0.17';
4 5     5   7878345 use Moose;
  5         9  
  5         37  
5 5     5   29945 use Class::Load qw(load_class load_first_existing_class);
  5         10  
  5         381  
6 5     5   26 use Catalyst::Utils;
  5         7  
  5         145  
7 5     5   23 use Moose::Meta::Class;
  5         7  
  5         169  
8 5     5   22 use String::RewritePrefix 0.004;
  5         167  
  5         33  
9 5     5   770 use MooseX::Types::Moose qw/ArrayRef Str RoleName/;
  5         9  
  5         77  
10 5     5   24174 use List::Util qw(first);
  5         8  
  5         441  
11 5     5   27 use namespace::autoclean;
  5         6  
  5         49  
12              
13             extends 'Catalyst::Controller';
14              
15             #pod =head1 DEPRECATION NOTICE
16             #pod
17             #pod As of version C<5.90013>, L<Catalyst> has merged this functionality into the
18             #pod core L<Catalyst::Controller>. You should no longer use it for new development
19             #pod and we recommend switching to the core controller as soon as practical.
20             #pod
21             #pod =head1 SYNOPSIS
22             #pod
23             #pod package MyApp::Controller::Foo;
24             #pod
25             #pod use Moose;
26             #pod use namespace::autoclean;
27             #pod
28             #pod BEGIN { extends 'Catalyst::Controller::ActionRole' }
29             #pod
30             #pod sub bar : Local Does('Moo') { ... }
31             #pod
32             #pod =head1 DESCRIPTION
33             #pod
34             #pod This module allows one to apply L<Moose::Role>s to the C<Catalyst::Action>s for
35             #pod different controller methods.
36             #pod
37             #pod For that a C<Does> attribute is provided. That attribute takes an argument,
38             #pod that determines the role, which is going to be applied. If that argument is
39             #pod prefixed with C<+>, it is assumed to be the full name of the role. If it's
40             #pod prefixed with C<~>, the name of your application followed by
41             #pod C<::ActionRole::> is prepended. If it isn't prefixed with C<+> or C<~>,
42             #pod the role name will be searched for in C<@INC> according to the rules for
43             #pod L<role prefix searching|/ROLE PREFIX SEARCHING>.
44             #pod
45             #pod It's possible to apply roles to B<all> actions of a controller without
46             #pod specifying the C<Does> keyword in every action definition:
47             #pod
48             #pod package MyApp::Controller::Bar
49             #pod
50             #pod use Moose;
51             #pod use namespace::autoclean;
52             #pod
53             #pod BEGIN { extends 'Catalyst::Controller::ActionRole' }
54             #pod
55             #pod __PACKAGE__->config(
56             #pod action_roles => ['Foo', '~Bar'],
57             #pod );
58             #pod
59             #pod # Has Catalyst::ActionRole::Foo and MyApp::ActionRole::Bar applied.
60             #pod #
61             #pod # If MyApp::ActionRole::Foo exists and is loadable, it will take
62             #pod # precedence over Catalyst::ActionRole::Foo.
63             #pod #
64             #pod # If MyApp::ActionRole::Bar exists and is loadable, it will be loaded,
65             #pod # but even if it doesn't exist Catalyst::ActionRole::Bar will not be loaded.
66             #pod sub moo : Local { ... }
67             #pod
68             #pod Additionally, roles can be applied to selected actions without specifying
69             #pod C<Does> using L<Catalyst::Controller/action> and configured with
70             #pod L<Catalyst::Controller/action_args>:
71             #pod
72             #pod package MyApp::Controller::Baz;
73             #pod
74             #pod use Moose;
75             #pod use namespace::autoclean;
76             #pod
77             #pod BEGIN { extends 'Catalyst::Controller::ActionRole' }
78             #pod
79             #pod __PACKAGE__->config(
80             #pod action_roles => [qw( Foo )],
81             #pod action => {
82             #pod some_action => { Does => [qw( ~Bar )] },
83             #pod another_action => { Does => [qw( +MyActionRole::Baz )] },
84             #pod },
85             #pod action_args => {
86             #pod another_action => { customarg => 'arg1' },
87             #pod }
88             #pod );
89             #pod
90             #pod # has Catalyst::ActionRole::Foo and MyApp::ActionRole::Bar applied
91             #pod sub some_action : Local { ... }
92             #pod
93             #pod # has Catalyst::ActionRole::Foo and MyActionRole::Baz applied
94             #pod # and associated action class would get additional arguments passed
95             #pod sub another_action : Local { ... }
96             #pod
97             #pod =head1 ROLE PREFIX SEARCHING
98             #pod
99             #pod Roles specified with no prefix are looked up under a set of role prefixes. The
100             #pod first prefix is always C<MyApp::ActionRole::> (with C<MyApp> replaced as
101             #pod appropriate for your application); the following prefixes are taken from the
102             #pod C<_action_role_prefix> attribute.
103             #pod
104             #pod =attr _action_role_prefix
105             #pod
106             #pod This class attribute stores an array reference of role prefixes to search for
107             #pod role names in if they aren't prefixed with C<+> or C<~>. It defaults to
108             #pod C<[ 'Catalyst::ActionRole::' ]>. See L</role prefix searching>.
109             #pod
110             #pod =cut
111              
112             __PACKAGE__->mk_classdata(qw/_action_role_prefix/);
113             __PACKAGE__->_action_role_prefix([ 'Catalyst::ActionRole::' ]);
114              
115             #pod =attr _action_roles
116             #pod
117             #pod This attribute stores an array reference of role names that will be applied to
118             #pod every action of this controller. It can be set by passing a C<action_roles>
119             #pod argument to the constructor. The same expansions as for C<Does> will be
120             #pod performed.
121             #pod
122             #pod =cut
123              
124             has _action_role_args => (
125             traits => [qw(Array)],
126             isa => ArrayRef[Str],
127             init_arg => 'action_roles',
128             default => sub { [] },
129             handles => {
130             _action_role_args => 'elements',
131             },
132             );
133              
134             has _action_roles => (
135             traits => [qw(Array)],
136             isa => ArrayRef[RoleName],
137             init_arg => undef,
138             lazy_build => 1,
139             handles => {
140             _action_roles => 'elements',
141             },
142             );
143              
144             sub _build__action_roles {
145 25     25   31 my $self = shift;
146 25         1097 my @roles = $self->_expand_role_shortname($self->_action_role_args);
147 25         1067 load_class($_) for @roles;
148 25         23785 return \@roles;
149             }
150              
151             #pod =for Pod::Coverage BUILD
152             #pod
153             #pod =cut
154              
155             sub BUILD {
156 25     25 0 200 my $self = shift;
157             # force this to run at object creation time
158 25         1052 $self->_action_roles;
159             }
160              
161             around create_action => sub {
162             my ($orig, $self, %args) = @_;
163              
164             return $self->$orig(%args)
165             if $args{name} =~ /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/;
166              
167             my @roles = $self->gather_action_roles(%args);
168             return $self->$orig(%args) unless @roles;
169              
170             load_class($_) for @roles;
171              
172             my $action_class = $self->_build_action_subclass(
173             $self->action_class(%args), @roles,
174             );
175              
176             my $action_args = $self->config->{action_args};
177             my %extra_args = (
178             %{ $action_args->{'*'} || {} },
179             %{ $action_args->{ $args{name} } || {} },
180             );
181              
182             return $action_class->new({ %extra_args, %args });
183             };
184              
185             #pod =method gather_action_roles(\%action_args)
186             #pod
187             #pod Gathers the list of roles to apply to an action with the given C<%action_args>.
188             #pod
189             #pod =cut
190              
191             sub gather_action_roles {
192 61     61 1 53906 my ($self, %args) = @_;
193              
194             return (
195 61 100       347 $self->_action_roles,
196 61         2806 @{ $args{attributes}->{Does} || [] },
197             );
198             }
199             sub _build_action_subclass {
200 53     53   3805 my ($self, $action_class, @roles) = @_;
201              
202 53         272 my $meta = Moose::Meta::Class->initialize($action_class)->create_anon_class(
203             superclasses => [$action_class],
204             roles => \@roles,
205             cache => 1,
206             );
207 53     0   184630 $meta->add_method(meta => sub { $meta });
  0     0   0  
        0      
        0      
        0      
        0      
        0      
        0      
        0      
208              
209 53         2564 return $meta->name;
210             }
211              
212             sub _expand_role_shortname {
213 86     86   154 my ($self, @shortnames) = @_;
214 86         2630 my $app = $self->_application;
215              
216 86 50       835 my $prefix = $self->can('_action_role_prefix')
217             ? $self->_action_role_prefix
218             : ['Catalyst::ActionRole::'];
219              
220 86         3431 my @prefixes = (qq{${app}::ActionRole::}, @$prefix);
221              
222             return String::RewritePrefix->rewrite(
223             {
224             '' => sub {
225 98         303 my $loaded = load_first_existing_class(
226 49     49   2019 map { "$_$_[0]" } @prefixes
227             );
228 89         1278 return first { $loaded =~ /^$_/ }
229 49         132455 sort { length $b <=> length $a } @prefixes;
  49         242  
230             },
231 86         957 '~' => $prefixes[0],
232             '+' => '',
233             },
234             @shortnames,
235             );
236             }
237              
238             sub _parse_Does_attr {
239 61     61   85278 my ($self, $app, $name, $value) = @_;
240 61         176 return Does => $self->_expand_role_shortname($value);
241             }
242              
243             1;
244              
245             __END__
246              
247             =pod
248              
249             =encoding UTF-8
250              
251             =head1 NAME
252              
253             Catalyst::Controller::ActionRole - (DEPRECATED) Apply roles to action instances
254              
255             =head1 VERSION
256              
257             version 0.17
258              
259             =head1 SYNOPSIS
260              
261             package MyApp::Controller::Foo;
262              
263             use Moose;
264             use namespace::autoclean;
265              
266             BEGIN { extends 'Catalyst::Controller::ActionRole' }
267              
268             sub bar : Local Does('Moo') { ... }
269              
270             =head1 DESCRIPTION
271              
272             This module allows one to apply L<Moose::Role>s to the C<Catalyst::Action>s for
273             different controller methods.
274              
275             For that a C<Does> attribute is provided. That attribute takes an argument,
276             that determines the role, which is going to be applied. If that argument is
277             prefixed with C<+>, it is assumed to be the full name of the role. If it's
278             prefixed with C<~>, the name of your application followed by
279             C<::ActionRole::> is prepended. If it isn't prefixed with C<+> or C<~>,
280             the role name will be searched for in C<@INC> according to the rules for
281             L<role prefix searching|/ROLE PREFIX SEARCHING>.
282              
283             It's possible to apply roles to B<all> actions of a controller without
284             specifying the C<Does> keyword in every action definition:
285              
286             package MyApp::Controller::Bar
287              
288             use Moose;
289             use namespace::autoclean;
290              
291             BEGIN { extends 'Catalyst::Controller::ActionRole' }
292              
293             __PACKAGE__->config(
294             action_roles => ['Foo', '~Bar'],
295             );
296              
297             # Has Catalyst::ActionRole::Foo and MyApp::ActionRole::Bar applied.
298             #
299             # If MyApp::ActionRole::Foo exists and is loadable, it will take
300             # precedence over Catalyst::ActionRole::Foo.
301             #
302             # If MyApp::ActionRole::Bar exists and is loadable, it will be loaded,
303             # but even if it doesn't exist Catalyst::ActionRole::Bar will not be loaded.
304             sub moo : Local { ... }
305              
306             Additionally, roles can be applied to selected actions without specifying
307             C<Does> using L<Catalyst::Controller/action> and configured with
308             L<Catalyst::Controller/action_args>:
309              
310             package MyApp::Controller::Baz;
311              
312             use Moose;
313             use namespace::autoclean;
314              
315             BEGIN { extends 'Catalyst::Controller::ActionRole' }
316              
317             __PACKAGE__->config(
318             action_roles => [qw( Foo )],
319             action => {
320             some_action => { Does => [qw( ~Bar )] },
321             another_action => { Does => [qw( +MyActionRole::Baz )] },
322             },
323             action_args => {
324             another_action => { customarg => 'arg1' },
325             }
326             );
327              
328             # has Catalyst::ActionRole::Foo and MyApp::ActionRole::Bar applied
329             sub some_action : Local { ... }
330              
331             # has Catalyst::ActionRole::Foo and MyActionRole::Baz applied
332             # and associated action class would get additional arguments passed
333             sub another_action : Local { ... }
334              
335             =head1 ATTRIBUTES
336              
337             =head2 _action_role_prefix
338              
339             This class attribute stores an array reference of role prefixes to search for
340             role names in if they aren't prefixed with C<+> or C<~>. It defaults to
341             C<[ 'Catalyst::ActionRole::' ]>. See L</role prefix searching>.
342              
343             =head2 _action_roles
344              
345             This attribute stores an array reference of role names that will be applied to
346             every action of this controller. It can be set by passing a C<action_roles>
347             argument to the constructor. The same expansions as for C<Does> will be
348             performed.
349              
350             =head1 METHODS
351              
352             =head2 gather_action_roles(\%action_args)
353              
354             Gathers the list of roles to apply to an action with the given C<%action_args>.
355              
356             =head1 DEPRECATION NOTICE
357              
358             As of version C<5.90013>, L<Catalyst> has merged this functionality into the
359             core L<Catalyst::Controller>. You should no longer use it for new development
360             and we recommend switching to the core controller as soon as practical.
361              
362             =head1 ROLE PREFIX SEARCHING
363              
364             Roles specified with no prefix are looked up under a set of role prefixes. The
365             first prefix is always C<MyApp::ActionRole::> (with C<MyApp> replaced as
366             appropriate for your application); the following prefixes are taken from the
367             C<_action_role_prefix> attribute.
368              
369             =for Pod::Coverage BUILD
370              
371             =head1 AUTHOR
372              
373             Florian Ragwitz <rafl@debian.org>
374              
375             =head1 COPYRIGHT AND LICENSE
376              
377             This software is copyright (c) 2009 by Florian Ragwitz.
378              
379             This is free software; you can redistribute it and/or modify it under
380             the same terms as the Perl 5 programming language system itself.
381              
382             =head1 CONTRIBUTORS
383              
384             =for stopwords Karen Etheridge Tomas Doran Hans Dieter Pearcey Alex J. G. BurzyÅ„ski Jason Kohles William King NAKAGAWA Masaki Joenio Costa John Napiorkowski
385              
386             =over 4
387              
388             =item *
389              
390             Karen Etheridge <ether@cpan.org>
391              
392             =item *
393              
394             Tomas Doran <bobtfish@bobtfish.net>
395              
396             =item *
397              
398             Hans Dieter Pearcey <hdp@weftsoar.net>
399              
400             =item *
401              
402             Alex J. G. BurzyÅ„ski <ajgb@ajgb.net>
403              
404             =item *
405              
406             Jason Kohles <email@jasonkohles.com>
407              
408             =item *
409              
410             William King <william.king@quentustech.com>
411              
412             =item *
413              
414             NAKAGAWA Masaki <masaki.nakagawa@gmail.com>
415              
416             =item *
417              
418             Joenio Costa <joenio@cpan.org>
419              
420             =item *
421              
422             John Napiorkowski <jjnapiork@cpan.org>
423              
424             =back
425              
426             =cut