File Coverage

blib/lib/Moose/Util/MetaRole.pm
Criterion Covered Total %
statement 60 60 100.0
branch 14 18 77.7
condition 13 17 76.4
subroutine 13 13 100.0
pod 2 2 100.0
total 102 110 92.7


line stmt bran cond sub pod time code
1             package Moose::Util::MetaRole;
2             our $VERSION = '2.2203';
3              
4 402     402   194353 use strict;
  402         779  
  402         11572  
5 402     402   1879 use warnings;
  402         733  
  402         10230  
6 402     402   1920 use Scalar::Util 'blessed';
  402         741  
  402         19163  
7              
8 402     402   2105 use List::Util 1.33 qw( first all );
  402         5571  
  402         21519  
9 402     402   21453 use Moose::Deprecated;
  402         797  
  402         2746  
10 402     402   162540 use Moose::Util 'throw_exception';
  402         981  
  402         2101  
11              
12             sub apply_metaroles {
13 121     121 1 11944 my %args = @_;
14              
15 121         421 my $for = _metathing_for( $args{for} );
16              
17 116 100       534 if ( $for->isa('Moose::Meta::Role') ) {
18 20         72 return _make_new_metaclass( $for, $args{role_metaroles}, 'role' );
19             }
20             else {
21 96         357 return _make_new_metaclass( $for, $args{class_metaroles}, 'class' );
22             }
23             }
24              
25             sub _metathing_for {
26 128     128   249 my $passed = shift;
27              
28 128 100       637 my $found
29             = blessed $passed
30             ? $passed
31             : Class::MOP::class_of($passed);
32              
33 128 100 66     1954 return $found
      100        
      100        
34             if defined $found
35             && blessed $found
36             && ( $found->isa('Moose::Meta::Role')
37             || $found->isa('Moose::Meta::Class') );
38              
39 6         15 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
40              
41 6         23 throw_exception( InvalidArgPassedToMooseUtilMetaRole => argument => $passed );
42             }
43              
44             sub _make_new_metaclass {
45 116     116   212 my $for = shift;
46 116         178 my $roles = shift;
47 116         196 my $primary = shift;
48              
49 116 50       181 return $for unless keys %{$roles};
  116         426  
50              
51             my $new_metaclass
52             = exists $roles->{$primary}
53 116 100       599 ? _make_new_class( ref $for, $roles->{$primary} )
54             : blessed $for;
55              
56 116         587 my %classes;
57              
58 116         223 for my $key ( grep { $_ ne $primary } keys %{$roles} ) {
  141         441  
  116         331  
59 67     67   194 my $attr = first {$_}
60 67         383 map { $for->meta->find_attribute_by_name($_) } (
  134         412  
61             $key . '_metaclass',
62             $key . '_class'
63             );
64              
65 67         327 my $reader = $attr->get_read_method;
66              
67             $classes{ $attr->init_arg }
68 67         1257 = _make_new_class( $for->$reader(), $roles->{$key} );
69             }
70              
71 116         742 my $new_meta = $new_metaclass->reinitialize( $for, %classes );
72              
73 115         774 return $new_meta;
74             }
75              
76             sub apply_base_class_roles {
77 7     7 1 952 my %args = @_;
78              
79 7   33     40 my $meta = _metathing_for( $args{for} || $args{for_class} );
80 6 100       49 throw_exception( CannotApplyBaseClassRolesToRole => params => \%args,
81             role_name => $meta->name,
82             )
83             if $meta->isa('Moose::Meta::Role');
84              
85             my $new_base = _make_new_class(
86             $meta->name,
87             $args{roles},
88 4         28 [ $meta->superclasses() ],
89             );
90              
91 4 50       30 $meta->superclasses($new_base)
92             if $new_base ne $meta->name();
93             }
94              
95             sub _make_new_class {
96 145     145   272 my $existing_class = shift;
97 145         275 my $roles = shift;
98 145   100     632 my $superclasses = shift || [$existing_class];
99              
100 145 50       407 return $existing_class unless $roles;
101              
102 145         526 my $meta = Class::MOP::Class->initialize($existing_class);
103              
104             return $existing_class
105 19     19   68 if $meta->can('does_role') && all { $meta->does_role($_) }
106 145 50 66     1000 grep { !ref $_ } @{$roles};
  19         93  
  19         49  
107              
108 145         752 return Moose::Meta::Class->create_anon_class(
109             superclasses => $superclasses,
110             roles => $roles,
111             cache => 1,
112             )->name();
113             }
114              
115             1;
116              
117             # ABSTRACT: Apply roles to any metaclass, as well as the object base class
118              
119             __END__
120              
121             =pod
122              
123             =encoding UTF-8
124              
125             =head1 NAME
126              
127             Moose::Util::MetaRole - Apply roles to any metaclass, as well as the object base class
128              
129             =head1 VERSION
130              
131             version 2.2203
132              
133             =head1 SYNOPSIS
134              
135             package MyApp::Moose;
136              
137             use Moose ();
138             use Moose::Exporter;
139             use Moose::Util::MetaRole;
140              
141             use MyApp::Role::Meta::Class;
142             use MyApp::Role::Meta::Method::Constructor;
143             use MyApp::Role::Object;
144              
145             Moose::Exporter->setup_import_methods( also => 'Moose' );
146              
147             sub init_meta {
148             shift;
149             my %args = @_;
150              
151             Moose->init_meta(%args);
152              
153             Moose::Util::MetaRole::apply_metaroles(
154             for => $args{for_class},
155             class_metaroles => {
156             class => ['MyApp::Role::Meta::Class'],
157             constructor => ['MyApp::Role::Meta::Method::Constructor'],
158             },
159             );
160              
161             Moose::Util::MetaRole::apply_base_class_roles(
162             for => $args{for_class},
163             roles => ['MyApp::Role::Object'],
164             );
165              
166             return $args{for_class}->meta();
167             }
168              
169             =head1 DESCRIPTION
170              
171             This utility module is designed to help authors of Moose extensions
172             write extensions that are able to cooperate with other Moose
173             extensions. To do this, you must write your extensions as roles, which
174             can then be dynamically applied to the caller's metaclasses.
175              
176             This module makes sure to preserve any existing superclasses and roles
177             already set for the meta objects, which means that any number of
178             extensions can apply roles in any order.
179              
180             =head1 USAGE
181              
182             The easiest way to use this module is through L<Moose::Exporter>, which can
183             generate the appropriate C<init_meta> method for you, and make sure it is
184             called when imported.
185              
186             =head1 FUNCTIONS
187              
188             This module provides two functions.
189              
190             =head2 apply_metaroles( ... )
191              
192             This function will apply roles to one or more metaclasses for the specified
193             class. It will return a new metaclass object for the class or role passed in
194             the "for" parameter.
195              
196             It accepts the following parameters:
197              
198             =over 4
199              
200             =item * for => $name
201              
202             This specifies the class for which to alter the meta classes. This can be a
203             package name, or an appropriate meta-object (a L<Moose::Meta::Class> or
204             L<Moose::Meta::Role>).
205              
206             =item * class_metaroles => \%roles
207              
208             This is a hash reference specifying which metaroles will be applied to the
209             class metaclass and its contained metaclasses and helper classes.
210              
211             Each key should in turn point to an array reference of role names.
212              
213             It accepts the following keys:
214              
215             =over 8
216              
217             =item class
218              
219             =item attribute
220              
221             =item method
222              
223             =item wrapped_method
224              
225             =item instance
226              
227             =item constructor
228              
229             =item destructor
230              
231             =item error
232              
233             =back
234              
235             =item * role_metaroles => \%roles
236              
237             This is a hash reference specifying which metaroles will be applied to the
238             role metaclass and its contained metaclasses and helper classes.
239              
240             It accepts the following keys:
241              
242             =over 8
243              
244             =item role
245              
246             =item attribute
247              
248             =item method
249              
250             =item required_method
251              
252             =item conflicting_method
253              
254             =item application_to_class
255              
256             =item application_to_role
257              
258             =item application_to_instance
259              
260             =item application_role_summation
261              
262             =item applied_attribute
263              
264             =back
265              
266             =back
267              
268             =head2 apply_base_class_roles( for => $class, roles => \@roles )
269              
270             This function will apply the specified roles to the object's base class.
271              
272             =head1 BUGS
273              
274             See L<Moose/BUGS> for details on reporting bugs.
275              
276             =head1 AUTHORS
277              
278             =over 4
279              
280             =item *
281              
282             Stevan Little <stevan@cpan.org>
283              
284             =item *
285              
286             Dave Rolsky <autarch@urth.org>
287              
288             =item *
289              
290             Jesse Luehrs <doy@cpan.org>
291              
292             =item *
293              
294             Shawn M Moore <sartak@cpan.org>
295              
296             =item *
297              
298             יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
299              
300             =item *
301              
302             Karen Etheridge <ether@cpan.org>
303              
304             =item *
305              
306             Florian Ragwitz <rafl@debian.org>
307              
308             =item *
309              
310             Hans Dieter Pearcey <hdp@cpan.org>
311              
312             =item *
313              
314             Chris Prather <chris@prather.org>
315              
316             =item *
317              
318             Matt S Trout <mstrout@cpan.org>
319              
320             =back
321              
322             =head1 COPYRIGHT AND LICENSE
323              
324             This software is copyright (c) 2006 by Infinity Interactive, Inc.
325              
326             This is free software; you can redistribute it and/or modify it under
327             the same terms as the Perl 5 programming language system itself.
328              
329             =cut