1 package Catalyst::Controller::ActionRole;
3 $Catalyst::Controller::ActionRole::VERSION = '0.14';
5 # ABSTRACT: Apply roles to action instances
10 use Moose::Meta::Class;
11 use String::RewritePrefix;
12 use MooseX::Types::Moose qw/ArrayRef Str RoleName/;
13 use List::Util qw(first);
15 use namespace::clean -except => 'meta';
17 extends 'Catalyst::Controller';
20 __PACKAGE__->mk_classdata(qw/_action_role_prefix/);
21 __PACKAGE__->_action_role_prefix([ 'Catalyst::ActionRole::' ]);
24 has _action_role_args => (
25 traits => [qw(Array)],
27 init_arg => 'action_roles',
28 default => sub { [] },
30 _action_role_args => 'elements',
34 has _action_roles => (
35 traits => [qw(Array)],
36 isa => ArrayRef[RoleName],
40 _action_roles => 'elements',
44 sub _build__action_roles {
46 my @roles = $self->_expand_role_shortname($self->_action_role_args);
47 Class::MOP::load_class($_) for @roles;
53 # force this to run at object creation time
58 my ($self, %args) = @_;
60 my $class = exists $args{attributes}->{ActionClass}
61 ? $args{attributes}->{ActionClass}->[0]
62 : $self->_action_class;
64 Class::MOP::load_class($class);
66 my @roles = ($self->_action_roles, @{ $args{attributes}->{Does} || [] });
68 Class::MOP::load_class($_) for @roles;
69 my $meta = Moose::Meta::Class->initialize($class)->create_anon_class(
70 superclasses => [$class],
74 $meta->add_method(meta => sub { $meta });
78 return $class->new(\%args);
81 sub _expand_role_shortname {
82 my ($self, @shortnames) = @_;
83 my $app = $self->_application;
85 my $prefix = $self->can('_action_role_prefix') ? $self->_action_role_prefix : ['Catalyst::ActionRole::'];
86 my @prefixes = (qq{${app}::ActionRole::}, @$prefix);
88 return String::RewritePrefix->rewrite(
90 my $loaded = Class::MOP::load_first_existing_class(
91 map { "$_$_[0]" } @prefixes
93 return first { $loaded =~ /^$_/ }
94 sort { length $b <=> length $a } @prefixes;
102 sub _parse_Does_attr {
103 my ($self, $app, $name, $value) = @_;
104 return Does => $self->_expand_role_shortname($value);
115 Catalyst::Controller::ActionRole - Apply roles to action instances
123 package MyApp::Controller::Foo;
125 use parent qw/Catalyst::Controller::ActionRole/;
127 sub bar : Local Does('Moo') { ... }
131 This module allows to apply roles to the C<Catalyst::Action>s for different
134 For that a C<Does> attribute is provided. That attribute takes an argument,
135 that determines the role, which is going to be applied. If that argument is
136 prefixed with C<+>, it is assumed to be the full name of the role. If it's
137 prefixed with C<~>, the name of your application followed by
138 C<::ActionRole::> is prepended. If it isn't prefixed with C<+> or C<~>,
139 the role name will be searched for in C<@INC> according to the rules for
140 L<role prefix searching|/ROLE PREFIX SEARCHING>.
142 Additionally it's possible to to apply roles to B<all> actions of a controller
143 without specifying the C<Does> keyword in every action definition:
145 package MyApp::Controller::Bar
147 use parent qw/Catalyst::Controller::ActionRole/;
150 action_roles => ['Foo', '~Bar'],
153 # has Catalyst::ActionRole::Foo and MyApp::ActionRole::Bar applied
154 # if MyApp::ActionRole::Foo exists and is loadable, it will take
155 # precedence over Catalyst::ActionRole::Foo
156 sub moo : Local { ... }
160 =head2 _action_role_prefix
162 This class attribute stores an array reference of role prefixes to search for
163 role names in if they aren't prefixed with C<+> or C<~>. It defaults to
164 C<[ 'Catalyst::ActionRole::' ]>. See L</role prefix searching>.
168 This attribute stores an array reference of role names that will be applied to
169 every action of this controller. It can be set by passing a C<action_roles>
170 argument to the constructor. The same expansions as for C<Does> will be
173 =head1 ROLE PREFIX SEARCHING
175 Roles specified with no prefix are looked up under a set of role prefixes. The
176 first prefix is always C<MyApp::ActionRole::> (with C<MyApp> replaced as
177 appropriate for your application); the following prefixes are taken from the
178 C<_action_role_prefix> attribute.
180 =for Pod::Coverage BUILD
184 Florian Ragwitz <rafl@debian.org>
185 Hans Dieter Pearcey <hdp@weftsoar.net>
187 =head1 COPYRIGHT AND LICENSE
189 This software is copyright (c) 2010 by Florian Ragwitz.
191 This is free software; you can redistribute it and/or modify it under
192 the same terms as the Perl 5 programming language system itself.