File Coverage

blib/lib/MOP/Role.pm
Criterion Covered Total %
statement 312 312 100.0
branch 166 200 83.0
condition 21 42 50.0
subroutine 46 46 100.0
pod 37 37 100.0
total 582 637 91.3


line stmt bran cond sub pod time code
1             package MOP::Role;
2             # ABSTRACT: A representation of a role
3              
4 28     28   1254540 use strict;
  28         231  
  28         823  
5 28     28   159 use warnings;
  28         58  
  28         704  
6              
7 28     28   156 use Carp ();
  28         66  
  28         546  
8              
9 28     28   7525 use UNIVERSAL::Object::Immutable;
  28         42922  
  28         989  
10              
11 28     28   8473 use MOP::Method;
  28         107  
  28         1123  
12 28     28   10237 use MOP::Slot;
  28         79  
  28         911  
13              
14 28     28   193 use MOP::Internal::Util;
  28         61  
  28         1604  
15              
16             our $VERSION = '0.12';
17             our $AUTHORITY = 'cpan:STEVAN';
18              
19 28     28   6550 our @ISA; BEGIN { @ISA = 'UNIVERSAL::Object::Immutable' };
20              
21             sub BUILDARGS {
22 110     110 1 141316 my $class = shift;
23 110         196 my %args;
24              
25 110 100       334 if ( scalar( @_ ) == 1 ) {
26 16 100       43 if ( ref $_[0] ) {
27 4 50       15 if ( ref $_[0] eq 'HASH' ) {
28 4 100       18 if ( MOP::Internal::Util::IS_STASH_REF( $_[0] ) ) {
29             # if it is a stash, grab the name
30 2         9 %args = (
31             name => MOP::Internal::Util::GET_NAME( $_[0] ),
32             stash => $_[0]
33             );
34             }
35             else {
36             # just plain old HASH ref ...
37 2         5 %args = %{ $_[0] };
  2         8  
38             }
39             }
40             }
41             else {
42             # assume it is a single package name ...
43 12         34 %args = ( name => $_[0] );
44             }
45             }
46             else {
47             # assume we got key/value pairs ...
48 94         311 %args = @_;
49             }
50              
51             Carp::confess('[ARGS] You must specify a package name')
52 110 50       338 unless $args{name};
53              
54             Carp::confess('[ARGS] You must specify a valid package name, not `'.$_[0].'`')
55 110 50       417 unless MOP::Internal::Util::IS_VALID_MODULE_NAME( $args{name} );
56              
57 110         560 return \%args;
58             }
59              
60             sub CREATE {
61 110     110 1 1510 my ($class, $args) = @_;
62              
63             # intiialize the stash ...
64 110         203 my $stash = $args->{stash};
65              
66             # if we have it, otherwise get it ...
67 110 100       260 unless ( $stash ) {
68             # get a ref to to the stash itself ...
69 28     28   207 no strict 'refs';
  28         70  
  28         97366  
70 108         161 $stash = \%{ $args->{name} . '::' };
  108         379  
71             }
72             # and then a ref to that, because we
73             # eventually will need to bless it and
74             # we do not want to bless the actual
75             # stash because that persists beyond
76             # the lifetime of this object, so we
77             # bless a ref of a ref then ...
78 110         362 return \$stash;
79             }
80              
81             # stash
82              
83             sub stash {
84 2058     2058 1 3222 my ($self) = @_;
85 2058         5410 return $$self; # returns the direct HASH ref of the stash
86             }
87              
88             # identity
89              
90             sub name {
91 1115     1115 1 41483 my ($self) = @_;
92 1115         2005 return MOP::Internal::Util::GET_NAME( $self->stash );
93             }
94              
95             sub version {
96 8     8 1 16 my ($self) = @_;
97 8         20 my $version = MOP::Internal::Util::GET_GLOB_SLOT( $self->stash, 'VERSION', 'SCALAR' );
98 8 50       24 return unless $version;
99 8         34 return $$version;
100             }
101              
102             sub authority {
103 8     8 1 17 my ($self) = @_;
104 8         22 my $authority = MOP::Internal::Util::GET_GLOB_SLOT( $self->stash, 'AUTHORITY', 'SCALAR' );
105 8 50       22 return unless $authority;
106 8         28 return $$authority;
107             }
108              
109             # other roles
110              
111             sub roles {
112 155     155 1 720 my ($self) = @_;
113 155         331 my $does = MOP::Internal::Util::GET_GLOB_SLOT( $self->stash, 'DOES', 'ARRAY' );
114 155 100       517 return unless $does;
115 52         175 return @$does;
116             }
117              
118             sub set_roles {
119 1     1 1 83 my ($self, @roles) = @_;
120 1 50       3 Carp::confess('[ARGS] You must specify at least one role')
121             if scalar( @roles ) == 0;
122 1         2 MOP::Internal::Util::SET_GLOB_SLOT( $self->stash, 'DOES', \@roles );
123 1         2 return;
124             }
125              
126             sub does_role {
127 23     23 1 2704 my ($self, $to_test) = @_;
128              
129 23 50       58 Carp::confess('[ARGS] You must specify a role')
130             unless $to_test;
131              
132 23         51 my @roles = $self->roles;
133              
134             # no roles, will never match ...
135 23 100       62 return 0 unless @roles;
136              
137             # try the simple way first ...
138 17         32 foreach my $role ( @roles ) {
139 20 100       91 return 1 if $role eq $to_test;
140             }
141              
142             # then try the harder way next ...
143 4         9 foreach my $role ( @roles ) {
144 4 100       19 return 1
145             if MOP::Role->new( name => $role )
146             ->does_role( $to_test );
147             }
148              
149             # oh well ...
150 1         9 return 0;
151             }
152              
153             ## Methods
154              
155             # get them all; regular, aliased & required
156             sub all_methods {
157 46     46 1 113 my $stash = $_[0]->stash;
158 46         78 my @methods;
159 46         305 foreach my $candidate ( keys %$stash ) {
160 1215 100       16657 if ( my $code = MOP::Internal::Util::GET_GLOB_SLOT( $stash, $candidate, 'CODE' ) ) {
161 941         2582 push @methods => MOP::Method->new( body => $code );
162             }
163             }
164 46         660 return @methods;
165             }
166              
167             # just the local non-required methods
168             sub methods {
169 21     21 1 1288 my $self = shift;
170 21         60 my $class = $self->name;
171 21         79 my @roles = $self->roles;
172              
173 21         39 my @methods;
174 21         59 foreach my $method ( $self->all_methods ) {
175             # if the method is required, we don't want it
176 464 100       1006 next if $method->is_required;
177              
178             # if the method is not originally from the
179             # class, then we probably don't want it ...
180 459 100       1074 if ( $method->origin_stash ne $class ) {
181             # if our class has roles, then non-local
182             # methods *might* be valid, so ...
183              
184             # if we don't have roles, then
185             # it can't be valid, so we don't
186             # want it
187 3 50       16 next unless @roles;
188              
189             # if we do have roles, but our
190             # method was not aliased from one
191             # of them, then we don't want it.
192 3 50       16 next unless $method->was_aliased_from( @roles );
193              
194             # if we are here then we have
195             # a non-required method that is
196             # not from the local class, it
197             # has roles and was aliased from
198             # one of them, which means we want
199             # to keep it, so we let it fall through
200             }
201              
202             # if we are here then we have
203             # a non-required method that is
204             # either from the local class,
205             # or is not from a local class,
206             # but has fallen through our
207             # conditional above.
208              
209 459         1079 push @methods => $method;
210             }
211              
212 21         122 return @methods;
213             }
214              
215             # just the non-local non-required methods
216             sub aliased_methods {
217 2     2 1 20 my $self = shift;
218 2         4 my $class = $self->name;
219 2 100       4 return grep { (!$_->is_required) && $_->origin_stash ne $class } $self->all_methods
  5         9  
220             }
221              
222             # just the required methods (locality be damned)
223             # NOTE:
224             # We don't care where are required method comes from
225             # just that one exists, so aliasing is not part of the
226             # criteria here.
227             # - SL
228             sub required_methods {
229 20     20 1 63 my $self = shift;
230 20         61 return grep { $_->is_required } $self->all_methods
  427         1009  
231             }
232              
233             # required methods
234              
235             # NOTE:
236             # there is no real heavy need to use the MOP::Method API
237             # below because 1) it is not needed, and 2) the MOP::Method
238             # API is really just an information shim, it does not perform
239             # much in the way of actions. From my point of view, the below
240             # operations are mostly stash manipulation functions and so
241             # therefore belong here in the continuim of responsibility/
242             # ownership.
243             #
244             ## The only argument that could likely be made is for the
245             ## MOP::Method API to handle creating the NULL CV for the
246             ## add_required_method, but that would require us to pass in
247             ## a MOP::Method instance, which would be silly since we never
248             ## need it anyway.
249             #
250             # - SL
251              
252             sub has_required_method {
253 37     37 1 123 my $stash = $_[0]->stash;
254 37         69 my $name = $_[1];
255              
256 37 50       133 Carp::confess('[ARGS] You must specify the name of the required method to look for')
257             unless $name;
258              
259 37 100       112 return 0 unless exists $stash->{ $name };
260 34         118 return MOP::Internal::Util::DOES_GLOB_HAVE_NULL_CV( $stash->{ $name } );
261             }
262              
263             # consistency is a good thing ...
264 36     36 1 15223 sub requires_method { goto &has_required_method }
265              
266             sub get_required_method {
267 20     20 1 10243 my $class = $_[0]->name;
268 20         63 my $stash = $_[0]->stash;
269 20         40 my $name = $_[1];
270              
271 20 50       65 Carp::confess('[ARGS] You must specify the name of the required method to get')
272             unless $name;
273              
274             # check these two easy cases first ...
275 20 100       75 return unless exists $stash->{ $name };
276 17 100       57 return unless MOP::Internal::Util::DOES_GLOB_HAVE_NULL_CV( $stash->{ $name } );
277              
278             # now we grab the CV ...
279 6         25 my $method = MOP::Method->new(
280             body => MOP::Internal::Util::GET_GLOB_SLOT( $stash, $name, 'CODE' )
281             );
282             # and make sure it is local, and
283             # then return the method ...
284 6 100       136 return $method if $method->origin_stash eq $class;
285             # or return nothing ...
286 1         8 return;
287             }
288              
289             sub add_required_method {
290 5     5 1 5017 my ($self, $name) = @_;
291              
292 5 50       15 Carp::confess('[ARGS] You must specify the name of the required method to add')
293             unless $name;
294              
295             # if we already have a glob there ...
296 5 100       16 if ( my $glob = $self->stash->{ $name } ) {
297             # and if we have a NULL CV in it, just return
298 3 100       12 return if MOP::Internal::Util::DOES_GLOB_HAVE_NULL_CV( $glob );
299             # and if we don't and we have a CODE slot, we
300             # need to die because this doesn't make sense
301             Carp::confess("[CONFLICT] Cannot add a required method ($name) when there is a regular method already there")
302 2 100       5 if defined *{ $glob }{CODE};
  2         178  
303             }
304              
305             # if we get here, then we
306             # just create a null CV
307 3         18 MOP::Internal::Util::CREATE_NULL_CV( $self->name, $name );
308              
309 2         45 return;
310             }
311              
312             sub delete_required_method {
313 4     4 1 1578 my ($self, $name) = @_;
314              
315 4 50       10 Carp::confess('[ARGS] You must specify the name of the required method to delete')
316             unless $name;
317              
318             # check if we have a stash entry for $name ...
319 4 100       9 if ( my $glob = $self->stash->{ $name } ) {
320             # and if we have a NULL CV in it, ...
321 3 100       8 if ( MOP::Internal::Util::DOES_GLOB_HAVE_NULL_CV( $glob ) ) {
322             # then we must delete it
323 1         2 MOP::Internal::Util::REMOVE_CV_FROM_GLOB( $self->stash, $name );
324 1         3 return;
325             }
326             else {
327             # and if we have a CV slot, but it doesn't have
328             # a NULL CV in it, then we need to die because
329             # this doesn't make sense
330             Carp::confess("[CONFLICT] Cannot delete a required method ($name) when there is a regular method already there")
331 2 100       3 if defined *{ $glob }{CODE};
  2         167  
332              
333             # if we have the glob, but no CV slot (NULL or otherwise)
334             # we do nothing ...
335 1         3 return;
336             }
337             }
338             # if there is no stash entry for $name, we do nothing
339 1         4 return;
340             }
341              
342             # methods
343              
344             sub has_method {
345 460     460 1 18698 my $self = $_[0];
346 460         1024 my $class = $self->name;
347 460         1026 my $stash = $self->stash;
348 460         765 my $name = $_[1];
349              
350 460 50       1066 Carp::confess('[ARGS] You must specify the name of the method to look for')
351             unless $name;
352              
353             # check these two easy cases first ...
354 460 100       1542 return 0 unless exists $stash->{ $name };
355 35 100       135 return 0 if MOP::Internal::Util::DOES_GLOB_HAVE_NULL_CV( $stash->{ $name } );
356              
357             # now we grab the CV and make sure it is
358             # local, and return accordingly
359 30 100       110 if ( my $code = MOP::Internal::Util::GET_GLOB_SLOT( $stash, $name, 'CODE' ) ) {
360 27         146 my $method = MOP::Method->new( body => $code );
361 27         584 my @roles = $self->roles;
362             # and make sure it is local, and
363             # then return accordingly
364 27   100     92 return $method->origin_stash eq $class
365             || (@roles && $method->was_aliased_from( @roles ));
366             }
367              
368             # if there was no CV, return false.
369 3         13 return 0;
370             }
371              
372             sub get_method {
373 22     22 1 9205 my $self = $_[0];
374 22         69 my $class = $self->name;
375 22         76 my $stash = $self->stash;
376 22         48 my $name = $_[1];
377              
378 22 50       74 Carp::confess('[ARGS] You must specify the name of the method to get')
379             unless $name;
380              
381             # check the easy cases first ...
382 22 100       84 return unless exists $stash->{ $name };
383 20 100       81 return if MOP::Internal::Util::DOES_GLOB_HAVE_NULL_CV( $stash->{ $name } );
384              
385             # now we grab the CV ...
386 17 100       68 if ( my $code = MOP::Internal::Util::GET_GLOB_SLOT( $stash, $name, 'CODE' ) ) {
387 14         65 my $method = MOP::Method->new( body => $code );
388 14         275 my @roles = $self->roles;
389             # and make sure it is local, and
390             # then return accordingly
391 14 100 66     48 return $method
      100        
392             if $method->origin_stash eq $class
393             || (@roles && $method->was_aliased_from( @roles ));
394             }
395              
396             # if there was no CV, return false.
397 4         26 return;
398             }
399              
400             sub add_method {
401 1     1 1 4 my ($self, $name, $code) = @_;
402              
403 1 50       5 Carp::confess('[ARGS] You must specify the name of the method to add')
404             unless $name;
405              
406 1 50 33     10 Carp::confess('[ARGS] You must specify a CODE reference to add as a method')
407             unless $code && ref $code eq 'CODE';
408              
409 1         6 MOP::Internal::Util::INSTALL_CV( $self->name, $name, $code, set_subname => 1 );
410 1         4 return;
411             }
412              
413             sub delete_method {
414 5     5 1 3148 my ($self, $name) = @_;
415              
416 5 50       12 Carp::confess('[ARGS] You must specify the name of the method to delete')
417             unless $name;
418              
419             # check if we have a stash entry for $name ...
420 5 100       12 if ( my $glob = $self->stash->{ $name } ) {
421             # and if we have a NULL CV in it, ...
422 4 100       12 if ( MOP::Internal::Util::DOES_GLOB_HAVE_NULL_CV( $glob ) ) {
423             # then we need to die because this
424             # shouldn't happen, we should only
425             # delete regular methods.
426 1         160 Carp::confess("[CONFLICT] Cannot delete a regular method ($name) when there is a required method already there");
427             }
428             else {
429             # if we don't have a code slot ...
430 3 100       7 return unless defined *{ $glob }{CODE};
  3         14  
431              
432             # we need to make sure it is local, and
433             # otherwise, error accordingly
434 2         11 my $method = MOP::Method->new( body => *{ $glob }{CODE} );
  2         10  
435 2         32 my @roles = $self->roles;
436              
437             # if the method has not come from
438             # the local class, we need to see
439             # if it was added from a role
440 2 100       5 if ($method->origin_stash ne $self->name) {
441              
442             # if it came from a role, then it is
443             # okay to be deleted, but if it didn't
444             # then we have an error cause they are
445             # trying to delete an alias using the
446             # regular method method
447 1 50 33     9 unless ( @roles && $method->was_aliased_from( @roles ) ) {
448 1         104 Carp::confess("[CONFLICT] Cannot delete a regular method ($name) when there is an aliased method already there")
449             }
450             }
451              
452             # but if we have a regular method, then we
453             # can just delete the CV from the glob
454 1         3 MOP::Internal::Util::REMOVE_CV_FROM_GLOB( $self->stash, $name );
455             }
456             }
457             # if there is no stash entry for $name, we do nothing
458 2         24 return;
459             }
460              
461             # aliased methods
462              
463             sub get_method_alias {
464 14     14 1 1757 my $class = $_[0]->name;
465 14         41 my $stash = $_[0]->stash;
466 14         26 my $name = $_[1];
467              
468 14 50       43 Carp::confess('[ARGS] You must specify the name of the method alias to look for')
469             unless $name;
470              
471             # check the easy cases first ...
472 14 100       56 return unless exists $stash->{ $name };
473 12 100       47 return if MOP::Internal::Util::DOES_GLOB_HAVE_NULL_CV( $stash->{ $name } );
474              
475             # now we grab the CV ...
476 11 100       44 if ( my $code = MOP::Internal::Util::GET_GLOB_SLOT( $stash, $name, 'CODE' ) ) {
477 8         58 my $method = MOP::Method->new( body => $code );
478             # and make sure it is not local, and
479             # then return accordingly
480 8 100       165 return $method
481             if $method->origin_stash ne $class;
482             }
483              
484             # if there was no CV, return false.
485 5         40 return;
486             }
487              
488             # NOTE:
489             # Should aliasing be aloud even after a class is closed?
490             # Probably not, but it might not be a bad idea to at
491             # least discuss in more detail what happens when a class
492             # is actually closed.
493             # - SL
494              
495             sub alias_method {
496 420     420 1 841 my ($self, $name, $code) = @_;
497              
498 420 50       798 Carp::confess('[ARGS] You must specify the name of the method alias to add')
499             unless $name;
500              
501 420 50 33     1584 Carp::confess('[ARGS] You must specify a CODE reference to add as a method alias')
502             unless $code && ref $code eq 'CODE';
503              
504 420         836 MOP::Internal::Util::INSTALL_CV( $self->name, $name, $code, set_subname => 0 );
505 420         1041 return;
506             }
507              
508             sub delete_method_alias {
509 5     5 1 3545 my ($self, $name) = @_;
510              
511 5 50       22 Carp::confess('[ARGS] You must specify the name of the method alias to remove')
512             unless $name;
513              
514             # check if we have a stash entry for $name ...
515 5 100       15 if ( my $glob = $self->stash->{ $name } ) {
516             # and if we have a NULL CV in it, ...
517 4 100       14 if ( MOP::Internal::Util::DOES_GLOB_HAVE_NULL_CV( $glob ) ) {
518             # then we need to die because this
519             # shouldn't happen, we should only
520             # delete regular methods.
521 1         183 Carp::confess("[CONFLICT] Cannot delete an aliased method ($name) when there is a required method already there");
522             }
523             else {
524             # if we don't have a code slot ...
525 3 100       3 return unless defined *{ $glob }{CODE};
  3         12  
526              
527             # we need to make sure it is local, and
528             # otherwise, error accordingly
529 2         4 my $method = MOP::Method->new( body => *{ $glob }{CODE} );
  2         7  
530              
531 2 100       31 Carp::confess("[CONFLICT] Cannot delete an aliased method ($name) when there is a regular method already there")
532             if $method->origin_stash eq $self->name;
533              
534             # but if we have a regular method, then we
535             # can just delete the CV from the glob
536 1         6 MOP::Internal::Util::REMOVE_CV_FROM_GLOB( $self->stash, $name );
537             }
538             }
539             # if there is no stash entry for $name, we do nothing
540 2         14 return;
541             }
542              
543             sub has_method_alias {
544 30     30 1 7074 my $class = $_[0]->name;
545 30         92 my $stash = $_[0]->stash;
546 30         62 my $name = $_[1];
547              
548 30 50       85 Carp::confess('[ARGS] You must specify the name of the method alias to look for')
549             unless $name;
550              
551             # check these two easy cases first ...
552 30 100       100 return 0 unless exists $stash->{ $name };
553 28 100       94 return 0 if MOP::Internal::Util::DOES_GLOB_HAVE_NULL_CV( $stash->{ $name } );
554              
555             # now we grab the CV and make sure it is
556             # local, and return accordingly
557 23 100       79 if ( my $code = MOP::Internal::Util::GET_GLOB_SLOT( $stash, $name, 'CODE' ) ) {
558 20         93 return MOP::Method->new( body => $code )->origin_stash ne $class;
559             }
560              
561             # if there was no CV, return false.
562 3         15 return 0;
563             }
564              
565             ## Slots
566              
567             ## FIXME:
568             ## The same problem we had methods needs to be fixed with
569             ## slots, just checking the origin_stash v. class is
570             ## not enough, we need to check aliasing as well.
571             ## - SL
572              
573             # get them all; regular & aliased
574             sub all_slots {
575 43     43 1 4331 my $self = shift;
576 43         110 my $has = MOP::Internal::Util::GET_GLOB_SLOT( $self->stash, 'HAS', 'HASH' );
577 43 100       165 return unless $has;
578             return map {
579 18         61 MOP::Slot->new(
580             name => $_,
581 18         130 initializer => $has->{ $_ }
582             )
583             } keys %$has;
584             }
585              
586             # just the local slots
587             sub slots {
588 26     26 1 182 my $self = shift;
589 26         100 my $class = $self->name;
590 26         92 my @roles = $self->roles;
591             return grep {
592 26 100 33     82 $_->origin_stash eq $class
  6         73  
593             ||
594             (@roles && $_->was_aliased_from( @roles ))
595             } $self->all_slots
596             }
597              
598             # just the non-local slots
599             sub aliased_slots {
600 8     8 1 56 my $self = shift;
601 8         31 my $class = $self->name;
602 8         25 return grep { $_->origin_stash ne $class } $self->all_slots
  6         77  
603             }
604              
605             ## regular ...
606              
607             sub has_slot {
608 11     11 1 14206 my $self = $_[0];
609 11         25 my $name = $_[1];
610 11         28 my $class = $self->name;
611 11         35 my $has = MOP::Internal::Util::GET_GLOB_SLOT( $self->stash, 'HAS', 'HASH' );
612              
613 11 50       37 Carp::confess('[ARGS] You must specify the name of the slot to look for')
614             unless $name;
615              
616 11 100       42 return unless $has;
617 8 100       40 return unless exists $has->{ $name };
618              
619 4         14 my @roles = $self->roles;
620             my $slot = MOP::Slot->new(
621             name => $name,
622 4         21 initializer => $has->{ $name }
623             );
624              
625 4   66     74 return $slot->origin_stash eq $class
626             || (@roles && $slot->was_aliased_from( @roles ));
627             }
628              
629             sub get_slot {
630 16     16 1 2565 my $self = $_[0];
631 16         33 my $name = $_[1];
632 16         43 my $class = $self->name;
633 16         49 my $has = MOP::Internal::Util::GET_GLOB_SLOT( $self->stash, 'HAS', 'HASH' );
634              
635 16 50       47 Carp::confess('[ARGS] You must specify the name of the slot to get')
636             unless $name;
637              
638 16 100       48 return unless $has;
639 13 100       50 return unless exists $has->{ $name };
640              
641 9         30 my @roles = $self->roles;
642             my $slot = MOP::Slot->new(
643             name => $name,
644 9         43 initializer => $has->{ $name }
645             );
646              
647 9 100 33     151 return $slot
      66        
648             if $slot->origin_stash eq $class
649             || (@roles && $slot->was_aliased_from( @roles ));
650              
651 1         3 return;
652             }
653              
654             sub add_slot {
655 3     3 1 1639 my $self = $_[0];
656 3         8 my $name = $_[1];
657 3         6 my $initializer = $_[2];
658              
659 3 50       11 Carp::confess('[ARGS] You must specify the name of the slot to add')
660             unless $name;
661              
662 3 50 33     20 Carp::confess('[ARGS] You must specify an initializer CODE reference to associate with the slot')
      33        
663             unless $initializer && (ref $initializer eq 'CODE' || MOP::Internal::Util::CAN_COERCE_TO_CODE_REF( $initializer ));
664              
665 3         9 my $stash = $self->stash;
666 3         11 my $class = $self->name;
667 3         23 my $slot = MOP::Slot->new( name => $name, initializer => $initializer );
668              
669             # just as with add_method, we take ownership
670             # of the initializer and set the COMP STASH
671             # field so that we know it is ours.
672 3 100       71 MOP::Internal::Util::SET_COMP_STASH_FOR_CV( $slot->initializer, $class )
673             if $slot->origin_stash ne $class;
674              
675 3         11 my $has = MOP::Internal::Util::GET_GLOB_SLOT( $stash, 'HAS', 'HASH' );
676 3 100       14 MOP::Internal::Util::SET_GLOB_SLOT( $stash, 'HAS', $has = {} )
677             unless $has;
678              
679 3         10 $has->{ $name } = $initializer;
680 3         15 return;
681             }
682              
683             sub delete_slot {
684 4     4 1 2495 my $self = $_[0];
685 4         7 my $name = $_[1];
686 4         9 my $stash = $self->stash;
687 4         10 my $class = $self->name;
688              
689 4 50       10 Carp::confess('[ARGS] You must specify the name of the slot to delete')
690             unless $name;
691              
692 4         12 my $has = MOP::Internal::Util::GET_GLOB_SLOT( $stash, 'HAS', 'HASH' );
693              
694 4 100       11 return unless $has;
695 3 100       9 return unless exists $has->{ $name };
696              
697             Carp::confess("[CONFLICT] Cannot delete a regular slot ($name) when there is an aliased slot already there")
698             if MOP::Slot->new(
699             name => $name,
700 2 100       9 initializer => $has->{ $name }
701             )->origin_stash ne $class;
702              
703 1         3 delete $has->{ $name };
704              
705 1         7 return;
706             }
707              
708             sub has_slot_alias {
709 8     8 1 4837 my $self = $_[0];
710 8         15 my $name = $_[1];
711 8         17 my $class = $self->name;
712 8         24 my $has = MOP::Internal::Util::GET_GLOB_SLOT( $self->stash, 'HAS', 'HASH' );
713              
714 8 50       24 Carp::confess('[ARGS] You must specify the name of the slot alias to look for')
715             unless $name;
716              
717 8 100       25 return unless $has;
718 7 100       26 return unless exists $has->{ $name };
719              
720             return MOP::Slot->new(
721             name => $name,
722 5         21 initializer => $has->{ $name }
723             )->origin_stash ne $class;
724             }
725              
726             sub get_slot_alias {
727 8     8 1 1539 my $self = $_[0];
728 8         14 my $name = $_[1];
729 8         19 my $class = $self->name;
730 8         24 my $has = MOP::Internal::Util::GET_GLOB_SLOT( $self->stash, 'HAS', 'HASH' );
731              
732 8 50       32 Carp::confess('[ARGS] You must specify the name of the slot alias to get')
733             unless $name;
734              
735 8 100       25 return unless $has;
736 7 100       23 return unless exists $has->{ $name };
737              
738             my $slot = MOP::Slot->new(
739             name => $name,
740 6         26 initializer => $has->{ $name }
741             );
742              
743 6 100       106 return $slot
744             if $slot->origin_stash ne $class;
745              
746 2         8 return;
747             }
748              
749             sub alias_slot {
750 3     3 1 1416 my $self = $_[0];
751 3         7 my $name = $_[1];
752 3         6 my $initializer = $_[2];
753              
754 3 50       9 Carp::confess('[ARGS] You must specify the name of the slot alias to add')
755             unless $name;
756              
757 3 50 33     21 Carp::confess('[ARGS] You must specify an initializer CODE reference to associate with the slot alias')
      33        
758             unless $initializer && (ref $initializer eq 'CODE' || MOP::Internal::Util::CAN_COERCE_TO_CODE_REF( $initializer ));
759              
760 3         10 my $stash = $self->stash;
761 3         10 my $class = $self->name;
762 3         19 my $slot = MOP::Slot->new( name => $name, initializer => $initializer );
763              
764 3 100       67 Carp::confess('[CONFLICT] Slot is from the local class (' . $class . '), it should be from a different class')
765             if $slot->origin_stash eq $class;
766              
767 2         7 my $has = MOP::Internal::Util::GET_GLOB_SLOT( $stash, 'HAS', 'HASH' );
768 2 100       9 MOP::Internal::Util::SET_GLOB_SLOT( $stash, 'HAS', $has = {} )
769             unless $has;
770              
771 2         4 $has->{ $name } = $initializer;
772 2         13 return;
773             }
774              
775             sub delete_slot_alias {
776 4     4 1 1529 my $self = $_[0];
777 4         9 my $name = $_[1];
778 4         10 my $stash = $self->stash;
779 4         9 my $class = $self->name;
780              
781 4 50       9 Carp::confess('[ARGS] You must specify the name of the slot alias to delete')
782             unless $name;
783              
784 4         13 my $has = MOP::Internal::Util::GET_GLOB_SLOT( $stash, 'HAS', 'HASH' );
785              
786 4 100       10 return unless $has;
787 3 100       11 return unless exists $has->{ $name };
788              
789             Carp::confess("[CONFLICT] Cannot delete an slot alias ($name) when there is an regular slot already there")
790             if MOP::Slot->new(
791             name => $name,
792 2 100       8 initializer => $has->{ $name }
793             )->origin_stash eq $class;
794              
795 1         3 delete $has->{ $name };
796              
797 1         8 return;
798             }
799              
800             1;
801              
802             __END__