File Coverage

blib/lib/mop/role.pm
Criterion Covered Total %
statement 181 182 99.4
branch 21 22 95.4
condition 22 32 68.7
subroutine 46 47 97.8
pod 28 28 100.0
total 298 311 95.8


line stmt bran cond sub pod time code
1             package mop::role;
2              
3 143     154   110710 use v5.16;
  143         504  
  143         5127  
4 143     147   3096 use warnings;
  143         301  
  143         3913  
5              
6 143     147   729 use mop::internals::util;
  143         257  
  143         11155  
7              
8             our $VERSION = '0.03';
9             our $AUTHORITY = 'cpan:STEVAN';
10              
11 143     147   780 use parent 'mop::object', 'mop::internals::observable';
  143         278  
  143         1357  
12              
13             mop::internals::util::init_attribute_storage(my %name);
14             mop::internals::util::init_attribute_storage(my %version);
15             mop::internals::util::init_attribute_storage(my %authority);
16             mop::internals::util::init_attribute_storage(my %roles);
17             mop::internals::util::init_attribute_storage(my %attributes);
18             mop::internals::util::init_attribute_storage(my %methods);
19             mop::internals::util::init_attribute_storage(my %required_methods);
20              
21 7192   50 7192 1 7687 sub name { ${ $name{ $_[0] } // \undef } }
  7192         58649  
22 443   100 443 1 747 sub version { ${ $version{ $_[0] } // \undef } }
  443         2223  
23 1   50 1 1 3 sub authority { ${ $authority{ $_[0] } // \undef } }
  1         11  
24 1385   50 1385 1 1732 sub roles { ${ $roles{ $_[0] } // \undef } }
  1385         8564  
25 11194   50 11194 1 11568 sub attribute_map { ${ $attributes{ $_[0] } // \undef } }
  11194         80774  
26 29393   50 29393 1 30056 sub method_map { ${ $methods{ $_[0] } // \undef } }
  29393         222609  
27 18209   50 18209 1 18957 sub required_method_map { ${ $required_methods{ $_[0] } // \undef } }
  18209         125520  
28              
29             # temporary, for bootstrapping
30             sub new {
31 6     6   10 my $class = shift;
32 6         17 my %args = @_;
33              
34 6         24 my $self = $class->SUPER::new( @_ );
35              
36 6         20 $name{ $self } = \($args{'name'});
37 6         18 $version{ $self } = \($args{'version'});
38 6         17 $authority{ $self } = \($args{'authority'});
39              
40 6   50     45 $roles{ $self } = \($args{'roles'} || []);
41 6         18 $attributes{ $self } = \({});
42 6         17 $methods{ $self } = \({});
43 6         17 $required_methods{ $self } = \({});
44              
45 6         22 $self;
46             }
47              
48             sub BUILD {
49 108     108 1 203 my $self = shift;
50              
51 108         345 mop::internals::util::install_meta($self);
52              
53 108 100       179 if (my @nometa = grep { !mop::meta($_) } @{ $self->roles }) {
  76         198  
  108         700  
54 1         16 die "No metaclass found for these roles: @nometa";
55             }
56             }
57              
58             sub clone {
59 12     12 1 29 my $self = shift;
60 12         43 my (%args) = @_;
61              
62 12 50       56 die "You must specify a name when cloning a metaclass"
63             unless $args{name};
64              
65 12         36 my $methods = $self->method_map;
66 12         101 $args{methods} //= {
67 12   50     75 map { $_ => $methods->{$_}->clone } keys %$methods
68             };
69              
70 12         37 my $attributes = $self->attribute_map;
71 2         16 $args{attributes} //= {
72 12   50     96 map { $_ => $attributes->{$_}->clone } keys %$attributes
73             };
74              
75 12         135 my $clone = $self->SUPER::clone(%args);
76              
77 12         33 for my $method (keys %{ $args{methods} }) {
  12         46  
78 12         37 $clone->get_method($method)->set_associated_meta($clone);
79             }
80              
81 12         27 for my $attribute (keys %{ $args{attributes} }) {
  12         42  
82 2         11 $clone->get_attribute($attribute)->set_associated_meta($clone);
83             }
84              
85 12         50 return $clone;
86             }
87              
88             sub add_role {
89 151     151 1 1464 my ($self, $role) = @_;
90 151         265 push @{ $self->roles } => $role;
  151         920  
91             }
92              
93             sub does_role {
94 211     211 1 1429 my ($self, $name) = @_;
95 211         228 foreach my $role ( @{ $self->roles } ) {
  211         411  
96 124 100 100     300 return 1 if $role->name eq $name
97             || $role->does_role( $name );
98             }
99 157         968 return 0;
100             }
101              
102 195     195 1 1197 sub attribute_class { 'mop::attribute' }
103              
104 1419     1419 1 1984 sub attributes { values %{ $_[0]->attribute_map } }
  1419         3362  
105              
106             sub add_attribute {
107 4654     4654 1 10851 my ($self, $attr) = @_;
108 4654         11725 $self->attribute_map->{ $attr->name } = $attr;
109 4654         16497 $attr->set_associated_meta($self);
110             }
111              
112             sub get_attribute {
113 369     369 1 33570 my ($self, $name) = @_;
114 369         946 $self->attribute_map->{ $name }
115             }
116              
117             sub has_attribute {
118 1163     1163 1 1686 my ($self, $name) = @_;
119 1163         2104 exists $self->attribute_map->{ $name };
120             }
121              
122             sub remove_attribute {
123 1     1 1 3 my ($self, $name) = @_;
124 1         4 delete $self->attribute_map->{ $name };
125             }
126              
127 506     506 1 1865 sub method_class { 'mop::method' }
128              
129 1668     1668 1 2475 sub methods { values %{ $_[0]->method_map } }
  1668         3911  
130              
131             sub add_method {
132 16899     16899 1 26739 my ($self, $method) = @_;
133 16899         33483 $self->method_map->{ $method->name } = $method;
134 16899         56757 $method->set_associated_meta($self);
135 16899         43351 $self->remove_required_method($method->name);
136             }
137              
138             sub get_method {
139 6808     6808 1 9369 my ($self, $name) = @_;
140 6808         12951 $self->method_map->{ $name }
141             }
142              
143             sub has_method {
144 3983     3983 1 29716 my ($self, $name) = @_;
145 3983         7743 exists $self->method_map->{ $name };
146             }
147              
148             sub remove_method {
149 2     2 1 6 my ($self, $name) = @_;
150 2         9 delete $self->method_map->{ $name };
151             }
152              
153 1231     1231 1 2742 sub required_methods { keys %{ $_[0]->required_method_map } }
  1231         3266  
154              
155             sub add_required_method {
156 70     70 1 99 my ($self, $name) = @_;
157 70         136 $self->required_method_map->{ $name } = 1;
158             }
159              
160             sub remove_required_method {
161 16901     16901 1 25184 my ($self, $name) = @_;
162 16901         43229 delete $self->required_method_map->{ $name };
163             }
164              
165             sub requires_method {
166 7     7 1 16 my ($self, $name) = @_;
167 7         25 defined $self->required_method_map->{ $name };
168             }
169              
170             sub FINALIZE {
171 449     449 1 919 my $self = shift;
172              
173 74         279 mop::internals::util::apply_all_roles($self, @{ $self->roles })
  449         1137  
174 449 100       605 if @{ $self->roles };
175              
176             # XXX gross
177 445 100       5381 if ($self->isa('mop::class')) {
178 363 100 100     1162 die 'Required method(s) [' . (join ', ' => $self->required_methods)
179             . '] are not allowed in ' . $self->name
180             . ' unless class is declared abstract'
181             if $self->required_methods && not $self->is_abstract;
182             }
183              
184 437         1667 $self->fire('before:FINALIZE');
185              
186             {
187 143     143   252823 no strict 'refs';
  143         407  
  143         54149  
  437         591  
188 437         2611 *{ $self->name . '::VERSION' } = \$self->version;
  437         1561  
189 437 100 66     3427 @{ $self->name . '::ISA' } = ($self->superclass)
  355         1065  
190             if $self->isa('mop::class') && defined $self->superclass;
191             }
192              
193 437         1777 for my $method ($self->methods) {
194             # XXX
195 576 100       3825 if ($self->isa('mop::class')) {
196 1210 100       4221 my @super_methods = (
197 1210         4092 map { $_ ? $_->get_method($method->name) : undef }
198 515         1139 map { mop::meta($_) }
199 515         755 @{ mro::get_linear_isa($self->name) }
200             );
201 515         1013 shift @super_methods;
202 515         873 @super_methods = grep { defined } @super_methods;
  695         1653  
203              
204 515 100       1749 if (my $super = $super_methods[0]) {
205 47         218 mop::apply_metaclass($method, $super);
206             }
207             }
208              
209 575         1301 my $name = $self->name . '::' . $method->name;
210             my $body = ref($method) eq 'mop::method' && !$method->has_events
211             ? $method->body
212 575 100 100 20   3412 : sub { $method->execute(shift, \@_) };
  20     31   3409  
        19      
        6      
        6      
213 143     143   831 no strict 'refs';
  143         249  
  143         4240  
214 143     143   835 no warnings 'redefine';
  143         286  
  143         3527028  
215 575         7972 *$name = mop::internals::util::subname($name, $body);
216             }
217              
218 436         1512 mop::internals::util::set_meta_magic($self, $self->name);
219 436         1320 mop::internals::util::incr_attr_generation($self);
220              
221 436         1448 $self->fire('after:FINALIZE');
222             }
223              
224             sub __INIT_METACLASS__ {
225 143     143   2657 my $METACLASS = mop::class->new(
226             name => 'mop::role',
227             version => $VERSION,
228             authority => $AUTHORITY,
229             superclass => 'mop::object',
230             );
231              
232             $METACLASS->add_attribute(mop::attribute->new(
233             name => '$!name',
234             storage => \%name,
235 0     0   0 default => sub { die "name is required when creating a role or class" },
236 143         3501 ));
237 143         1872 $METACLASS->add_attribute(mop::attribute->new(
238             name => '$!version',
239             storage => \%version,
240             ));
241 143         2265 $METACLASS->add_attribute(mop::attribute->new(
242             name => '$!authority',
243             storage => \%authority,
244             ));
245             $METACLASS->add_attribute(mop::attribute->new(
246             name => '$!roles',
247             storage => \%roles,
248 12     12   43 default => sub { [] },
249 143         1204 ));
250             $METACLASS->add_attribute(mop::attribute->new(
251             name => '$!attributes',
252             storage => \%attributes,
253 483     483   1291 default => sub { {} },
254 143         4502 ));
255             $METACLASS->add_attribute(mop::attribute->new(
256             name => '$!methods',
257             storage => \%methods,
258 482     482   1489 default => sub { {} },
259 143         3657 ));
260             $METACLASS->add_attribute(mop::attribute->new(
261             name => '$!required_methods',
262             storage => \%required_methods,
263 483     483   1332 default => sub { {} },
264 143         952 ));
265              
266 143         828 $METACLASS->add_method( mop::method->new( name => 'BUILD', body => \&BUILD ) );
267 143         1010 $METACLASS->add_method( mop::method->new( name => 'clone', body => \&clone ) );
268              
269 143         776 $METACLASS->add_method( mop::method->new( name => 'name', body => \&name ) );
270              
271 143         753 $METACLASS->add_method( mop::method->new( name => 'version', body => \&version ) );
272              
273 143         733 $METACLASS->add_method( mop::method->new( name => 'authority', body => \&authority ) );
274              
275 143         732 $METACLASS->add_method( mop::method->new( name => 'roles', body => \&roles ) );
276 143         776 $METACLASS->add_method( mop::method->new( name => 'add_role', body => \&add_role ) );
277 143         756 $METACLASS->add_method( mop::method->new( name => 'does_role', body => \&does_role ) );
278              
279 143         769 $METACLASS->add_method( mop::method->new( name => 'attribute_class', body => \&attribute_class ) );
280 143         7823 $METACLASS->add_method( mop::method->new( name => 'attribute_map', body => \&attribute_map ) );
281 143         757 $METACLASS->add_method( mop::method->new( name => 'attributes', body => \&attributes ) );
282 143         825 $METACLASS->add_method( mop::method->new( name => 'get_attribute', body => \&get_attribute ) );
283 143         1048 $METACLASS->add_method( mop::method->new( name => 'add_attribute', body => \&add_attribute ) );
284 143         759 $METACLASS->add_method( mop::method->new( name => 'has_attribute', body => \&has_attribute ) );
285 143         852 $METACLASS->add_method( mop::method->new( name => 'remove_attribute', body => \&remove_attribute ) );
286              
287 143         977 $METACLASS->add_method( mop::method->new( name => 'method_class', body => \&method_class ) );
288 143         727 $METACLASS->add_method( mop::method->new( name => 'method_map', body => \&method_map ) );
289 143         17845 $METACLASS->add_method( mop::method->new( name => 'methods', body => \&methods ) );
290 143         801 $METACLASS->add_method( mop::method->new( name => 'get_method', body => \&get_method ) );
291 143         924 $METACLASS->add_method( mop::method->new( name => 'add_method', body => \&add_method ) );
292 143         794 $METACLASS->add_method( mop::method->new( name => 'has_method', body => \&has_method ) );
293 143         753 $METACLASS->add_method( mop::method->new( name => 'remove_method', body => \&remove_method ) );
294              
295 143         799 $METACLASS->add_method( mop::method->new( name => 'required_methods', body => \&required_methods ) );
296 143         774 $METACLASS->add_method( mop::method->new( name => 'required_method_map', body => \&required_method_map ) );
297 143         747 $METACLASS->add_method( mop::method->new( name => 'add_required_method', body => \&add_required_method ) );
298 143         757 $METACLASS->add_method( mop::method->new( name => 'requires_method', body => \&requires_method ) );
299 143         5178 $METACLASS->add_method( mop::method->new( name => 'remove_required_method', body => \&remove_required_method ) );
300              
301 143         962 $METACLASS->add_method( mop::method->new( name => 'FINALIZE', body => \&FINALIZE ) );
302              
303 143         1485 $METACLASS;
304             }
305              
306             1;
307              
308             __END__