File Coverage

blib/lib/mop/attribute.pm
Criterion Covered Total %
statement 118 118 100.0
branch 15 18 83.3
condition 12 15 80.0
subroutine 28 28 100.0
pod 17 18 94.4
total 190 197 96.4


line stmt bran cond sub pod time code
1             package mop::attribute;
2              
3 143     143   1610 use v5.16;
  143         487  
  143         5461  
4 143     143   734 use warnings;
  143         261  
  143         6960  
5              
6 143     143   744 use Scalar::Util qw[ weaken isweak ];
  143         443  
  143         8837  
7 143     143   957 use mop::internals::util;
  143         276  
  143         10522  
8              
9             our $VERSION = '0.03';
10             our $AUTHORITY = 'cpan:STEVAN';
11              
12 143     143   985 use parent 'mop::object', 'mop::internals::observable';
  143         347  
  143         879  
13              
14             mop::internals::util::init_attribute_storage(my %name);
15             mop::internals::util::init_attribute_storage(my %default);
16             mop::internals::util::init_attribute_storage(my %associated_meta);
17             mop::internals::util::init_attribute_storage(my %original_id);
18             mop::internals::util::init_attribute_storage(my %storage);
19              
20 36476   50 36476 1 40914 sub name { ${ $name{ $_[0] } // \undef } }
  36476         245360  
21 93   100 93 1 185 sub associated_meta { ${ $associated_meta{ $_[0] } // \undef } }
  93         742  
22              
23             sub set_associated_meta {
24 4656     4656 1 8925 my ($self, $meta) = @_;
25 4656         24807 $associated_meta{ $self } = \$meta;
26 4656         6669 weaken(${ $associated_meta{ $self } });
  4656         34804  
27             }
28              
29             # temporary, for bootstrapping
30             sub new {
31 31     31   36 my $class = shift;
32 31         77 my %args = @_;
33 31         83 my $self = $class->SUPER::new;
34 31         119 $name{ $self } = \($args{'name'});
35 31 100       102 $default{ $self } = \($args{'default'}) if exists $args{'default'};
36 31 50       110 $storage{ $self } = \($args{'storage'}) if exists $args{'storage'};
37             # NOTE:
38             # keep track of the original ID here
39             # so that we can still detect attribute
40             # conflicts in roles even after something
41             # has been cloned
42             # - SL
43 31         79 $original_id{ $self } = \(mop::id($self));
44              
45 31         124 $self
46             }
47              
48             sub BUILD {
49 224     224 1 402 my $self = shift;
50 224 100       851 return unless $default{ $self };
51 219         347 my $value = ${ $default{ $self } };
  219         527  
52 219 100 100     1385 if ( ref $value && ref $value ne 'CODE' ) {
53 1 50       6 die "References of type (" . ref($value) . ") are not supported as attribute defaults (in attribute " . $self->name . ($self->associated_meta ? " in class " . $self->associated_meta->name : "") . ")";
54             }
55             }
56              
57             # temporary, for bootstrapping
58             sub clone {
59 11     11   13 my $self = shift;
60 11         30 return ref($self)->new(
61             name => $self->name,
62 11         33 default => ${ $default{ $self } },
63 11         21 storage => ${ $storage{ $self } },
64             );
65             }
66              
67             sub key_name {
68 14305     14305 1 19296 my $self = shift;
69 14305         24492 substr( $self->name, 2, length $self->name )
70             }
71              
72 5973     5973 1 7639 sub has_default { defined( ${ $default{ $_[0] } } ) }
  5973         32676  
73              
74             sub set_default {
75 6     6 1 17 my $self = shift;
76 6         10 my ($value) = @_;
77 6 100 66     46 if ( ref $value && ref $value ne 'CODE' ) {
78 1 50       6 die "References of type (" . ref($value) . ") are not supported as attribute defaults (in attribute " . $self->name . ($self->associated_meta ? " in class " . $self->associated_meta->name : "") . ")";
79             }
80 5         22 $default{ $self } = \$value
81             }
82              
83 9     9 1 14 sub clear_default { ${ delete $default{ $_[0] } } }
  9         38  
84              
85             sub get_default {
86 3254     3254 1 4142 my $self = shift;
87 3254         3210 my $value = ${ $default{ $self } };
  3254         7868  
88 3254 100 66     15202 if ( ref $value && ref $value eq 'CODE' ) {
89 2800         7295 $value = $value->();
90             }
91             $value
92 3251         12159 }
93              
94 6     6 1 10 sub conflicts_with { ${ $original_id{ $_[0] } } ne ${ $original_id{ $_[1] } } }
  6         16  
  6         52  
95              
96 434     434 1 578 sub locally_defined { ${ $original_id{ $_[0] } } eq mop::id( $_[0] ) }
  434         1728  
97              
98             sub has_data_in_slot_for {
99 735     735 1 1982 my ($self, $instance) = @_;
100 735         694 defined ${ $self->get_slot_for($instance) };
  735         1257  
101             }
102              
103             sub fetch_data_in_slot_for {
104 724     724 1 3407 my ($self, $instance) = @_;
105 724         1986 $self->fire('before:FETCH_DATA', $instance);
106 724         778 my $val = ${ $self->get_slot_for($instance) };
  724         1400  
107 724         2162 $self->fire('after:FETCH_DATA', $instance, \$val);
108 724         3768 return $val;
109             }
110              
111             sub store_data_in_slot_for {
112 7176     7176 1 12228 my ($self, $instance, $data) = @_;
113 7176         22642 $self->fire('before:STORE_DATA', $instance, \$data);
114 7176         8603 ${ $self->get_slot_for($instance) } = $data;
  7176         42286  
115 7176         33567 $self->fire('after:STORE_DATA', $instance, \$data);
116 7176         32984 return;
117             }
118              
119             sub store_default_in_slot_for {
120 5967     5967 1 7649 my ($self, $instance) = @_;
121 5967 100       10064 $self->store_data_in_slot_for($instance, do {
122 3253         4132 local $_ = $instance;
123 3253         14900 $self->get_default;
124             }) if $self->has_default;
125             }
126              
127             sub weaken_data_in_slot_for {
128 5     5 1 9 my ($self, $instance) = @_;
129 5         9 weaken(${ $self->get_slot_for($instance) });
  5         11  
130             }
131              
132             sub is_data_in_slot_weak_for {
133 3     3 1 10 my ($self, $instance) = @_;
134 3         7 isweak(${ $self->get_slot_for($instance) });
  3         9  
135             }
136              
137             sub get_slot_for {
138 8868     8868 0 13012 my ($self, $instance) = @_;
139 8868   100     8576 ${ $storage{ $self } }->{ $instance } //= \(my $slot);
  8868         81817  
140             }
141              
142             sub __INIT_METACLASS__ {
143 143     143   957 my $METACLASS = mop::class->new(
144             name => 'mop::attribute',
145             version => $VERSION,
146             authority => $AUTHORITY,
147             superclass => 'mop::object',
148             );
149              
150 143         682 $METACLASS->add_attribute(mop::attribute->new(
151             name => '$!name',
152             storage => \%name,
153             ));
154 143         632 $METACLASS->add_attribute(mop::attribute->new(
155             name => '$!default',
156             storage => \%default,
157             ));
158 143         870 $METACLASS->add_attribute(mop::attribute->new(
159             name => '$!associated_meta',
160             storage => \%associated_meta,
161             ));
162             $METACLASS->add_attribute(mop::attribute->new(
163             name => '$!original_id',
164             storage => \%original_id,
165 198     198   694 default => sub { mop::id($_) },
166 143         1466 ));
167             $METACLASS->add_attribute(mop::attribute->new(
168             name => '$!storage',
169             storage => \%storage,
170 198     198   796 default => sub { mop::internals::util::init_attribute_storage(my %x) },
171 143         1197 ));
172              
173 143         840 $METACLASS->add_method( mop::method->new( name => 'BUILD', body => \&BUILD ) );
174              
175 143         816 $METACLASS->add_method( mop::method->new( name => 'name', body => \&name ) );
176 143         847 $METACLASS->add_method( mop::method->new( name => 'key_name', body => \&key_name ) );
177              
178 143         752 $METACLASS->add_method( mop::method->new( name => 'has_default', body => \&has_default ) );
179 143         805 $METACLASS->add_method( mop::method->new( name => 'get_default', body => \&get_default ) );
180 143         770 $METACLASS->add_method( mop::method->new( name => 'set_default', body => \&set_default ) );
181 143         882 $METACLASS->add_method( mop::method->new( name => 'clear_default', body => \&clear_default ) );
182              
183 143         872 $METACLASS->add_method( mop::method->new( name => 'associated_meta', body => \&associated_meta ) );
184 143         809 $METACLASS->add_method( mop::method->new( name => 'set_associated_meta', body => \&set_associated_meta ) );
185              
186 143         788 $METACLASS->add_method( mop::method->new( name => 'conflicts_with', body => \&conflicts_with ) );
187 143         791 $METACLASS->add_method( mop::method->new( name => 'locally_defined', body => \&locally_defined ) );
188              
189 143         865 $METACLASS->add_method( mop::method->new( name => 'has_data_in_slot_for', body => \&has_data_in_slot_for ) );
190 143         937 $METACLASS->add_method( mop::method->new( name => 'fetch_data_in_slot_for', body => \&fetch_data_in_slot_for ) );
191 143         778 $METACLASS->add_method( mop::method->new( name => 'store_data_in_slot_for', body => \&store_data_in_slot_for ) );
192 143         964 $METACLASS->add_method( mop::method->new( name => 'store_default_in_slot_for', body => \&store_default_in_slot_for ) );
193 143         4590 $METACLASS->add_method( mop::method->new( name => 'weaken_data_in_slot_for', body => \&weaken_data_in_slot_for ) );
194 143         849 $METACLASS->add_method( mop::method->new( name => 'is_data_in_slot_weak_for', body => \&is_data_in_slot_weak_for ) );
195 143         758 $METACLASS->add_method( mop::method->new( name => 'get_slot_for', body => \&get_slot_for ) );
196              
197 143         1364 $METACLASS;
198             }
199              
200             1;
201              
202             __END__