File Coverage

blib/lib/exact/class.pm
Criterion Covered Total %
statement 145 147 98.6
branch 53 58 91.3
condition 9 18 50.0
subroutine 26 27 96.3
pod 4 4 100.0
total 237 254 93.3


line stmt bran cond sub pod time code
1             package exact::class;
2             # ABSTRACT: Simple class interface extension for exact
3              
4 16     16   1446579 use 5.014;
  16         592  
5 16     16   104 use exact;
  16         35  
  16         1322  
6 15     15   9452 use Role::Tiny ();
  15         27728  
  15         257  
7 15     15   73 use Scalar::Util ();
  15         1123  
  8         123  
8 8     8   4506 use Class::Method::Modifiers ();
  8         13401  
  8         2218  
9              
10             our $VERSION = '1.18'; # VERSION
11              
12             my $store;
13              
14             sub import {
15 29     29   21612 my ( $self, $caller ) = @_;
16              
17 29 100       85 if ($caller) {
18 26         85 exact->late_parent;
19             }
20             else {
21 3   33     24 $caller //= caller();
22 3 100       18 exact->add_isa( $self, $caller ) if ( $self eq 'exact::class');
23             }
24              
25 29         171 $store->{struc}{$caller} = {};
26              
27 8     8   44 eval qq{
  8     8   497  
  8     6   53  
  8     6   17  
  8         2263  
  6         25  
  6         433  
  6         45  
  6         23  
  6         1131  
  29         4937  
28             package $caller {
29             use Class::Method::Modifiers;
30             no feature 'class';
31             };
32             };
33              
34 29         358 exact->monkey_patch( $caller, $_, \&$_ ) for ( qw( has class_has with ) );
35             }
36              
37       0     sub DESTROY {}
38              
39             sub ____parents {
40 89     89   149 my ($namespace) = @_;
41 8     8   66 no strict 'refs';
  8         19  
  8         14610  
42 89         116 my @parents = @{ $namespace . '::ISA' };
  89         297  
43 89         262 return @parents, map { ____parents($_) } @parents;
  51         121  
44             }
45              
46             sub ____install {
47 115     115   245 my ( $self, $namespace, $input ) = @_;
48              
49 115 100       333 if ( ref $store->{struc}{$namespace} eq 'HASH' ) {
50 68         92 my @has_names = keys %{ $store->{struc}{$namespace}->{has} };
  68         226  
51              
52 68         111 for my $class_has_name (
53             grep {
54 239         315 my $name = $_;
55 239         296 not grep { $_ eq $name } @has_names;
  1517         2319  
56 68         170 } keys %{ $store->{struc}{$namespace}->{name} }
57             ) {
58 68 100       185 $self->$class_has_name( $input->{$class_has_name} ) if ( exists $input->{$class_has_name} );
59             }
60              
61 68         146 for my $has_name (@has_names) {
62 171 100       393 if ( exists $input->{$has_name} ) {
    100          
63 10         26 $self->attr( $has_name, $input->{$has_name} );
64             }
65             elsif ( exists $store->{struc}{$namespace}->{value}{$has_name} ) {
66 111         283 $self->attr( $has_name, $store->{struc}{$namespace}->{value}{$has_name} );
67             }
68             else {
69 50         106 $self->attr($has_name);
70             }
71             }
72             }
73             }
74              
75             sub new {
76 38     38 1 3445 my $class = shift;
77 38 100       120 my $input = @_ ? @_ > 1 ? {@_} : { %{ $_[0] } } : {};
  2 100       7  
78 38   33     253 my $self = bless( { %$input }, ref $class || $class );
79              
80 38         122 for my $namespace ( reverse ( ref $self, ____parents( ref $self ) ) ) {
81 89 100       255 if ( ref $store->{roles}{$namespace} eq 'ARRAY' ) {
82 26         34 for my $role ( @{ $store->{roles}->{$namespace} } ) {
  26         57  
83 26         54 ____install( $self, $role, $input );
84             }
85             }
86              
87 89         159 ____install( $self, $namespace, $input );
88             }
89              
90 38         198 return $self;
91             }
92              
93             sub tap {
94 4     4 1 10 my ( $self, $cb ) = ( shift, shift );
95 4         15 $_->$cb(@_) for $self;
96 4         10 return $self;
97             }
98              
99             sub attr {
100 179     179 1 8203 my ( $self, $attrs, $value ) = @_;
101              
102 179   66     647 my $set = {
103             attrs => $attrs,
104             caller => ref($self) || $self,
105             set_has => 1,
106             self => $self,
107             obj_accessor => 1,
108             };
109              
110 179 100       443 $set->{value} = $value if ( @_ > 2 );
111 179         278 return ____attrs($set);
112             }
113              
114             sub class_has {
115 12     12   172 my ( $attrs, $value ) = @_;
116              
117 12         54 my $set = {
118             attrs => $attrs,
119             caller => scalar( caller() ),
120             };
121              
122 12 50       42 $set->{value} = $value if ( @_ > 1 );
123 12         28 ____attrs($set);
124 12         53 return;
125             }
126              
127             sub has {
128 45     45   1877 my ( $attrs, $value ) = @_;
129              
130 45         152 my $set = {
131             attrs => $attrs,
132             caller => scalar( caller() ),
133             set_has => 1,
134             };
135              
136 45 100       135 $set->{value} = $value if ( @_ > 1 );
137 45         103 ____attrs($set);
138 45         246 return;
139             }
140              
141             sub ____attrs {
142 265     265   415 for my $set (@_) {
143 265 100       544 for my $name ( ( ref $set->{attrs} ) ? @{ $set->{attrs} } : $set->{attrs} ) {
  6         14  
144             my $accessor = ( $set->{obj_accessor} )
145             ? sub {
146 264     264   24313 my ( $self, $value ) = @_;
147              
148 264 100       490 if ( @_ > 1 ) {
149 151         554 $self->{$name} = $value;
150 151         361 return $self;
151             }
152             else {
153 4         18 return ${ $self->{$name} } if (
154             ref $self->{$name} eq 'REF' and
155 113 100 66     349 ref ${ $self->{$name} } eq 'CODE'
  4         18  
156             );
157              
158 109 100       296 $self->{$name} = $self->{$name}->($self) if ( ref $self->{$name} eq 'CODE' );
159 109         444 return $self->{$name};
160             }
161             }
162             : sub {
163 85     85   582 my ( $self, $value ) = @_;
164              
165 85 100       171 if ( @_ > 1 ) {
166 16         36 $store->{struc}{ $set->{caller} }->{value}{$name} = $value;
167 16         32 return $self;
168             }
169             else {
170 0         0 return ${ $store->{struc}{ $set->{caller} }->{value}{$name} } if (
171             ref $store->{struc}{ $set->{caller} }->{value}{$name} eq 'REF' and
172 69 50 33     239 ref ${ $store->{struc}{ $set->{caller} }->{value}{$name} } eq 'CODE'
  0         0  
173             );
174              
175             $store->{struc}{ $set->{caller} }->{value}{$name} =
176             $store->{struc}{ $set->{caller} }->{value}{$name}->($self)
177 69 50       158 if ( ref $store->{struc}{ $set->{caller} }->{value}{$name} eq 'CODE' );
178 69         240 return $store->{struc}{ $set->{caller} }->{value}{$name};
179             }
180 277 100       1055 };
181              
182             {
183 8     8   71 no strict 'refs';
  8         18  
  8         325  
  277         424  
184 8     8   54 no warnings 'redefine';
  8         16  
  8         8324  
185 277         334 *{ $set->{caller} . '::' . $name } = $accessor;
  277         1505  
186             }
187              
188 277 100       570 if ( ref $set->{self} ) {
189 179 100       495 $set->{self}->$name( $set->{value} ) if ( exists $set->{value} );
190             }
191             else {
192 98 100       303 $store->{struc}{ $set->{caller} }->{has}{$name} = 1 if ( $set->{set_has} );
193 98         209 $store->{struc}{ $set->{caller} }->{name}{$name} = 1;
194 98 100       307 $store->{struc}{ $set->{caller} }->{value}{$name} = $set->{value} if ( exists $set->{value} );
195             }
196             }
197             }
198              
199 265         610 return;
200             }
201              
202             sub ____role_attrs {
203 16     16   39 my ( $caller, $roles, $object ) = @_;
204              
205 16         38 for my $role (@$roles) {
206 21         32 for my $name (
207 21         88 keys %{ $store->{struc}{$role}{name} }
208             ) {
209 29         72 my $set = {
210             attrs => $name,
211             caller => $caller,
212             };
213              
214 29 100       84 if ( $store->{struc}{$role}{has}{$name} ) {
215 18         35 $set->{self} = $object;
216 18         28 $set->{obj_accessor} = 1;
217 18         62 $set->{set_has} = 1;
218             }
219              
220             $set->{value} = $store->{struc}{$role}{value}{$name}
221 29 100       128 if ( exists $store->{struc}{$role}{value}{$name} );
222              
223 29         59 ____attrs($set);
224             }
225             }
226              
227 16         29 return;
228             }
229              
230             sub with {
231 11     11   154 my $caller = scalar(caller);
232 11         20 push( @{ $store->{roles}->{$caller} }, @_ );
  11         48  
233              
234             try {
235             Role::Tiny->apply_roles_to_package( $caller, $_ ) for @_;
236             }
237 11         37 catch ($e) {
238             $e =~ s/\s+at\s.+\sline\s\d+\.\s*$//g;
239             croak $e;
240             }
241              
242 11         4017 ____role_attrs( $caller, [@_] );
243 11         31 return;
244             }
245              
246             sub with_roles {
247 5     5 1 550 my ( $self, @roles ) = @_;
248 5         8 my $object;
249              
250 5 100       22 unless ( my $class = Scalar::Util::blessed($self) ) {
251             $object = Role::Tiny->create_class_with_roles(
252             $self,
253 1 50       4 map { /^\+(.+)$/ ? "${self}::Role::$1" : $_ } @roles
  1         12  
254             );
255             }
256             else {
257             $object = Role::Tiny->apply_roles_to_object(
258             $self,
259 4 50       9 map { /^\+(.+)$/ ? "${class}::Role::$1" : $_ } @roles
  4         33  
260             );
261             }
262              
263 5   66     1549 ____role_attrs( Scalar::Util::blessed($object) || $object, [@_], $object );
264 5         24 return $object;
265             }
266              
267             1;
268              
269             __END__