File Coverage

blib/lib/exact/class.pm
Criterion Covered Total %
statement 143 147 97.2
branch 54 60 90.0
condition 9 18 50.0
subroutine 24 25 96.0
pod 7 7 100.0
total 237 257 92.2


line stmt bran cond sub pod time code
1             package exact::class;
2             # ABSTRACT: Simple class interface extension for exact
3              
4 9     9   2175722 use 5.014;
  9         39  
5 9     9   601 use exact;
  9         43121  
  9         98  
6 9     9   10262 use Import::Into;
  9         23  
  9         305  
7 9     9   46 use feature ();
  9         12  
  9         240  
8 9     9   5058 use Class::Method::Modifiers ();
  9         23852  
  9         510  
9 9     9   4245 use Role::Tiny ();
  9         54381  
  9         1023  
10 9     9   80 use Scalar::Util ();
  9         21  
  9         4809  
11              
12             our $VERSION = '1.21'; # VERSION
13              
14             my $store;
15             my ($perl_version) = $^V =~ /^v5\.(\d+)/;
16              
17             sub import {
18 29     29   29224 my ( $self, $params, $caller ) = @_;
19              
20 29 100       94 if ($caller) {
21 26         91 exact->late_parent;
22             }
23             else {
24 3   33     31 $caller //= caller();
25 3 100       24 exact->add_isa( $self, $caller ) if ( $self eq 'exact::class' );
26             }
27              
28 29         219 $store->{struc}{$caller} = {};
29              
30 29         171 Class::Method::Modifiers->import::into($caller);
31 29 50       11459 feature->unimport('class') if ( $perl_version > 36 );
32              
33 29         210 exact->monkey_patch( $caller, $_, \&$_ ) for ( qw( has class_has with ) );
34             }
35              
36       0     sub DESTROY {}
37              
38             sub ____parents {
39 81     81   137 my ($namespace) = @_;
40 9     9   88 no strict 'refs';
  9         19  
  9         28449  
41 81         105 my @parents = @{ $namespace . '::ISA' };
  81         311  
42 81         226 return @parents, map { ____parents($_) } @parents;
  43         137  
43             }
44              
45             sub ____install {
46 107     107   208 my ( $self, $namespace, $input ) = @_;
47              
48 107 100       332 if ( ref $store->{struc}{$namespace} eq 'HASH' ) {
49 68         133 my @has_names = keys %{ $store->{struc}{$namespace}->{has} };
  68         278  
50              
51 68         103 for my $class_has_name (
52             grep {
53 239         1282 my $name = $_;
54 239         370 not grep { $_ eq $name } @has_names;
  1517         2007  
55 68         199 } keys %{ $store->{struc}{$namespace}->{name} }
56             ) {
57 68 100       173 $self->$class_has_name( $input->{$class_has_name} ) if ( exists $input->{$class_has_name} );
58             }
59              
60 68         127 for my $has_name (@has_names) {
61 171 100       455 if ( exists $input->{$has_name} ) {
    100          
62 10         72 $self->attr( $has_name, $input->{$has_name} );
63             }
64             elsif ( exists $store->{struc}{$namespace}->{value}{$has_name} ) {
65 111         254 $self->attr( $has_name, $store->{struc}{$namespace}->{value}{$has_name} );
66             }
67             else {
68 50         119 $self->attr($has_name);
69             }
70             }
71             }
72             }
73              
74             sub new {
75 38     38 1 4811 my $class = shift;
76 38 100       143 my $input = @_ ? @_ > 1 ? {@_} : { %{ $_[0] } } : {};
  2 100       8  
77 38   33     263 my $self = bless( { %$input }, ref $class || $class );
78              
79 38         141 for my $namespace ( reverse ( ref $self, ____parents( ref $self ) ) ) {
80 81 100       267 if ( ref $store->{roles}{$namespace} eq 'ARRAY' ) {
81 26         34 for my $role ( @{ $store->{roles}->{$namespace} } ) {
  26         71  
82 26         53 ____install( $self, $role, $input );
83             }
84             }
85              
86 81         181 ____install( $self, $namespace, $input );
87             }
88              
89 38         198 return $self;
90             }
91              
92             sub tap {
93 4     4 1 13 my ( $self, $cb ) = ( shift, shift );
94 4         20 $_->$cb(@_) for $self;
95 4         13 return $self;
96             }
97              
98             sub attr {
99 179     179 1 10772 my ( $self, $attrs, $value ) = @_;
100              
101 179   66     724 my $set = {
102             attrs => $attrs,
103             caller => ref($self) || $self,
104             set_has => 1,
105             self => $self,
106             obj_accessor => 1,
107             };
108              
109 179 100       431 $set->{value} = $value if ( @_ > 2 );
110 179         287 return ____attrs($set);
111             }
112              
113             sub class_has {
114 12     12 1 255289 my ( $attrs, $value ) = @_;
115              
116 12         48 my $set = {
117             attrs => $attrs,
118             caller => scalar( caller() ),
119             };
120              
121 12 50       41 $set->{value} = $value if ( @_ > 1 );
122 12         33 ____attrs($set);
123 12         48 return;
124             }
125              
126             sub has {
127 45     45 1 1270917 my ( $attrs, $value ) = @_;
128              
129 45         174 my $set = {
130             attrs => $attrs,
131             caller => scalar( caller() ),
132             set_has => 1,
133             };
134              
135 45 100       158 $set->{value} = $value if ( @_ > 1 );
136 45         109 ____attrs($set);
137 45         187 return;
138             }
139              
140             sub ____attrs {
141 265     265   414 for my $set (@_) {
142 265 100       583 for my $name ( ( ref $set->{attrs} ) ? @{ $set->{attrs} } : $set->{attrs} ) {
  6         8  
143             my $accessor = ( $set->{obj_accessor} )
144             ? sub {
145 264     264   33649 my ( $self, $value ) = @_;
146              
147 264 100       453 if ( @_ > 1 ) {
148 151         565 $self->{$name} = $value;
149 151         332 return $self;
150             }
151             else {
152 4         19 return ${ $self->{$name} } if (
153             ref $self->{$name} eq 'REF' and
154 113 100 66     364 ref ${ $self->{$name} } eq 'CODE'
  4         22  
155             );
156              
157 109 100       380 $self->{$name} = $self->{$name}->($self) if ( ref $self->{$name} eq 'CODE' );
158 109         490 return $self->{$name};
159             }
160             }
161             : sub {
162 85     85   588 my ( $self, $value ) = @_;
163              
164 85 100       176 if ( @_ > 1 ) {
165 16         58 $store->{struc}{ $set->{caller} }->{value}{$name} = $value;
166 16         47 return $self;
167             }
168             else {
169 0         0 return ${ $store->{struc}{ $set->{caller} }->{value}{$name} } if (
170             ref $store->{struc}{ $set->{caller} }->{value}{$name} eq 'REF' and
171 69 50 33     1784 ref ${ $store->{struc}{ $set->{caller} }->{value}{$name} } eq 'CODE'
  0         0  
172             );
173              
174             $store->{struc}{ $set->{caller} }->{value}{$name} =
175             $store->{struc}{ $set->{caller} }->{value}{$name}->($self)
176 69 50       162 if ( ref $store->{struc}{ $set->{caller} }->{value}{$name} eq 'CODE' );
177 69         269 return $store->{struc}{ $set->{caller} }->{value}{$name};
178             }
179 277 100       1359 };
180              
181             {
182 9     9   110 no strict 'refs';
  9         22  
  9         505  
  277         354  
183 9     9   47 no warnings 'redefine';
  9         18  
  9         16947  
184 277         314 *{ $set->{caller} . '::' . $name } = $accessor;
  277         1855  
185             }
186              
187 277 100       558 if ( ref $set->{self} ) {
188 179 100       512 $set->{self}->$name( $set->{value} ) if ( exists $set->{value} );
189             }
190             else {
191 98 100       358 $store->{struc}{ $set->{caller} }->{has}{$name} = 1 if ( $set->{set_has} );
192 98         210 $store->{struc}{ $set->{caller} }->{name}{$name} = 1;
193 98 100       342 $store->{struc}{ $set->{caller} }->{value}{$name} = $set->{value} if ( exists $set->{value} );
194             }
195             }
196             }
197              
198 265         700 return;
199             }
200              
201             sub ____role_attrs {
202 16     16   828 my ( $caller, $roles, $object ) = @_;
203              
204 16         43 for my $role (@$roles) {
205 21         31 for my $name (
206 21         118 keys %{ $store->{struc}{$role}{name} }
207             ) {
208 29         91 my $set = {
209             attrs => $name,
210             caller => $caller,
211             };
212              
213 29 100       112 if ( $store->{struc}{$role}{has}{$name} ) {
214 18         39 $set->{self} = $object;
215 18         37 $set->{obj_accessor} = 1;
216 18         38 $set->{set_has} = 1;
217             }
218              
219             $set->{value} = $store->{struc}{$role}{value}{$name}
220 29 100       115 if ( exists $store->{struc}{$role}{value}{$name} );
221              
222 29         59 ____attrs($set);
223             }
224             }
225              
226 16         36 return;
227             }
228              
229             sub with {
230 11     11 1 345497 my $caller = scalar(caller);
231 11         43 push( @{ $store->{roles}->{$caller} }, @_ );
  11         57  
232              
233 11         31 try {
234 11         95 Role::Tiny->apply_roles_to_package( $caller, $_ ) for @_;
235             }
236             catch ($e) {
237 0         0 $e =~ s/\s+at\s.+\sline\s\d+\.\s*$//g;
238 0         0 croak $e;
239             }
240              
241 11         5331 ____role_attrs( $caller, [@_] );
242 11         51 return;
243             }
244              
245             sub with_roles {
246 5     5 1 605 my ( $self, @roles ) = @_;
247 5         8 my $object;
248              
249 5 100       14 unless ( my $class = Scalar::Util::blessed($self) ) {
250             $object = Role::Tiny->create_class_with_roles(
251             $self,
252 1 50       4 map { /^\+(.+)$/ ? "${self}::Role::$1" : $_ } @roles
  1         18  
253             );
254             }
255             else {
256             $object = Role::Tiny->apply_roles_to_object(
257             $self,
258 4 50       7 map { /^\+(.+)$/ ? "${class}::Role::$1" : $_ } @roles
  4         25  
259             );
260             }
261              
262 5   66     1579 ____role_attrs( Scalar::Util::blessed($object) || $object, [@_], $object );
263 5         24 return $object;
264             }
265              
266             1;
267              
268             __END__