File Coverage

blib/lib/exact/class.pm
Criterion Covered Total %
statement 142 144 98.6
branch 54 60 90.0
condition 9 18 50.0
subroutine 24 25 96.0
pod 4 4 100.0
total 233 251 92.8


line stmt bran cond sub pod time code
1             package exact::class;
2             # ABSTRACT: Simple class interface extension for exact
3              
4 8     8   1442161 use 5.014;
  8         71  
5 8     8   43 use exact;
  8         16  
  8         108  
6 8     8   5805 use Import::Into;
  8         17  
  8         164  
7 8     8   45 use feature ();
  8         15  
  8         120  
8 8     8   4955 use Class::Method::Modifiers ();
  8         13288  
  8         172  
9 8     8   3453 use Role::Tiny ();
  8         26605  
  8         186  
10 8     8   57 use Scalar::Util ();
  8         18  
  8         2535  
11              
12             our $VERSION = '1.19'; # VERSION
13              
14             my $store;
15             my ($perl_version) = $^V =~ /^v5\.(\d+)/;
16              
17             sub import {
18 29     29   22426 my ( $self, $params, $caller ) = @_;
19              
20 29 100       82 if ($caller) {
21 26         87 exact->late_parent;
22             }
23             else {
24 3   33     21 $caller //= caller();
25 3 100       21 exact->add_isa( $self, $caller ) if ( $self eq 'exact::class' );
26             }
27              
28 29         183 $store->{struc}{$caller} = {};
29              
30 29         137 Class::Method::Modifiers->import::into($caller);
31 29 50       7023 feature->unimport('class') if ( $perl_version > 36 );
32              
33 29         169 exact->monkey_patch( $caller, $_, \&$_ ) for ( qw( has class_has with ) );
34             }
35              
36       0     sub DESTROY {}
37              
38             sub ____parents {
39 85     85   141 my ($namespace) = @_;
40 8     8   66 no strict 'refs';
  8         27  
  8         13998  
41 85         110 my @parents = @{ $namespace . '::ISA' };
  85         293  
42 85         230 return @parents, map { ____parents($_) } @parents;
  47         122  
43             }
44              
45             sub ____install {
46 111     111   193 my ( $self, $namespace, $input ) = @_;
47              
48 111 100       319 if ( ref $store->{struc}{$namespace} eq 'HASH' ) {
49 68         95 my @has_names = keys %{ $store->{struc}{$namespace}->{has} };
  68         237  
50              
51 68         111 for my $class_has_name (
52             grep {
53 239         306 my $name = $_;
54 239         310 not grep { $_ eq $name } @has_names;
  1517         2285  
55 68         170 } keys %{ $store->{struc}{$namespace}->{name} }
56             ) {
57 68 100       146 $self->$class_has_name( $input->{$class_has_name} ) if ( exists $input->{$class_has_name} );
58             }
59              
60 68         147 for my $has_name (@has_names) {
61 171 100       413 if ( exists $input->{$has_name} ) {
    100          
62 10         28 $self->attr( $has_name, $input->{$has_name} );
63             }
64             elsif ( exists $store->{struc}{$namespace}->{value}{$has_name} ) {
65 111         268 $self->attr( $has_name, $store->{struc}{$namespace}->{value}{$has_name} );
66             }
67             else {
68 50         90 $self->attr($has_name);
69             }
70             }
71             }
72             }
73              
74             sub new {
75 38     38 1 3693 my $class = shift;
76 38 100       117 my $input = @_ ? @_ > 1 ? {@_} : { %{ $_[0] } } : {};
  2 100       7  
77 38   33     206 my $self = bless( { %$input }, ref $class || $class );
78              
79 38         116 for my $namespace ( reverse ( ref $self, ____parents( ref $self ) ) ) {
80 85 100       242 if ( ref $store->{roles}{$namespace} eq 'ARRAY' ) {
81 26         43 for my $role ( @{ $store->{roles}->{$namespace} } ) {
  26         63  
82 26         63 ____install( $self, $role, $input );
83             }
84             }
85              
86 85         151 ____install( $self, $namespace, $input );
87             }
88              
89 38         166 return $self;
90             }
91              
92             sub tap {
93 4     4 1 14 my ( $self, $cb ) = ( shift, shift );
94 4         18 $_->$cb(@_) for $self;
95 4         15 return $self;
96             }
97              
98             sub attr {
99 179     179 1 8105 my ( $self, $attrs, $value ) = @_;
100              
101 179   66     646 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       411 $set->{value} = $value if ( @_ > 2 );
110 179         293 return ____attrs($set);
111             }
112              
113             sub class_has {
114 12     12   152 my ( $attrs, $value ) = @_;
115              
116 12         40 my $set = {
117             attrs => $attrs,
118             caller => scalar( caller() ),
119             };
120              
121 12 50       40 $set->{value} = $value if ( @_ > 1 );
122 12         30 ____attrs($set);
123 12         49 return;
124             }
125              
126             sub has {
127 45     45   1764 my ( $attrs, $value ) = @_;
128              
129 45         160 my $set = {
130             attrs => $attrs,
131             caller => scalar( caller() ),
132             set_has => 1,
133             };
134              
135 45 100       132 $set->{value} = $value if ( @_ > 1 );
136 45         110 ____attrs($set);
137 45         265 return;
138             }
139              
140             sub ____attrs {
141 265     265   409 for my $set (@_) {
142 265 100       522 for my $name ( ( ref $set->{attrs} ) ? @{ $set->{attrs} } : $set->{attrs} ) {
  6         46  
143             my $accessor = ( $set->{obj_accessor} )
144             ? sub {
145 264     264   23335 my ( $self, $value ) = @_;
146              
147 264 100       526 if ( @_ > 1 ) {
148 151         561 $self->{$name} = $value;
149 151         398 return $self;
150             }
151             else {
152 4         16 return ${ $self->{$name} } if (
153             ref $self->{$name} eq 'REF' and
154 113 100 66     300 ref ${ $self->{$name} } eq 'CODE'
  4         17  
155             );
156              
157 109 100       287 $self->{$name} = $self->{$name}->($self) if ( ref $self->{$name} eq 'CODE' );
158 109         470 return $self->{$name};
159             }
160             }
161             : sub {
162 85     85   583 my ( $self, $value ) = @_;
163              
164 85 100       157 if ( @_ > 1 ) {
165 16         35 $store->{struc}{ $set->{caller} }->{value}{$name} = $value;
166 16         38 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     209 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       142 if ( ref $store->{struc}{ $set->{caller} }->{value}{$name} eq 'CODE' );
177 69         233 return $store->{struc}{ $set->{caller} }->{value}{$name};
178             }
179 277 100       1048 };
180              
181             {
182 8     8   71 no strict 'refs';
  8         19  
  8         304  
  277         411  
183 8     8   50 no warnings 'redefine';
  8         16  
  8         8354  
184 277         335 *{ $set->{caller} . '::' . $name } = $accessor;
  277         1476  
185             }
186              
187 277 100       563 if ( ref $set->{self} ) {
188 179 100       533 $set->{self}->$name( $set->{value} ) if ( exists $set->{value} );
189             }
190             else {
191 98 100       271 $store->{struc}{ $set->{caller} }->{has}{$name} = 1 if ( $set->{set_has} );
192 98         206 $store->{struc}{ $set->{caller} }->{name}{$name} = 1;
193 98 100       310 $store->{struc}{ $set->{caller} }->{value}{$name} = $set->{value} if ( exists $set->{value} );
194             }
195             }
196             }
197              
198 265         615 return;
199             }
200              
201             sub ____role_attrs {
202 16     16   42 my ( $caller, $roles, $object ) = @_;
203              
204 16         35 for my $role (@$roles) {
205 21         32 for my $name (
206 21         93 keys %{ $store->{struc}{$role}{name} }
207             ) {
208 29         84 my $set = {
209             attrs => $name,
210             caller => $caller,
211             };
212              
213 29 100       84 if ( $store->{struc}{$role}{has}{$name} ) {
214 18         31 $set->{self} = $object;
215 18         31 $set->{obj_accessor} = 1;
216 18         32 $set->{set_has} = 1;
217             }
218              
219             $set->{value} = $store->{struc}{$role}{value}{$name}
220 29 100       83 if ( exists $store->{struc}{$role}{value}{$name} );
221              
222 29         81 ____attrs($set);
223             }
224             }
225              
226 16         30 return;
227             }
228              
229             sub with {
230 11     11   144 my $caller = scalar(caller);
231 11         64 push( @{ $store->{roles}->{$caller} }, @_ );
  11         52  
232              
233             try {
234             Role::Tiny->apply_roles_to_package( $caller, $_ ) for @_;
235             }
236 11         66 catch ($e) {
237             $e =~ s/\s+at\s.+\sline\s\d+\.\s*$//g;
238             croak $e;
239             }
240              
241 11         4054 ____role_attrs( $caller, [@_] );
242 11         33 return;
243             }
244              
245             sub with_roles {
246 5     5 1 571 my ( $self, @roles ) = @_;
247 5         9 my $object;
248              
249 5 100       21 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         14  
253             );
254             }
255             else {
256             $object = Role::Tiny->apply_roles_to_object(
257             $self,
258 4 50       7 map { /^\+(.+)$/ ? "${class}::Role::$1" : $_ } @roles
  4         39  
259             );
260             }
261              
262 5   66     1561 ____role_attrs( Scalar::Util::blessed($object) || $object, [@_], $object );
263 5         24 return $object;
264             }
265              
266             1;
267              
268             __END__