File Coverage

/tmp/u09XM_8B49/lib/My/Prefix/HashBase.pm
Criterion Covered Total %
statement 117 249 46.9
branch 33 116 28.4
condition 12 40 30.0
subroutine 16 30 53.3
pod 1 4 25.0
total 179 439 40.7


line stmt bran cond sub pod time code
1             package My::Prefix::HashBase;
2 1     1   1852 use strict;
  1         2  
  1         48  
3 1     1   6 use warnings;
  1         2  
  1         104  
4              
5             our $VERSION = '123';
6              
7             #################################################################
8             # #
9             # This is a generated file! Do not modify this file directly! #
10             # Use hashbase_inc.pl script to regenerate this file. #
11             # The script is part of the Object::HashBase distribution. #
12             # Note: You can modify the version number above this comment #
13             # if needed, that is fine. #
14             # #
15             #################################################################
16              
17             {
18 1     1   5 no warnings 'once';
  1         2  
  1         142  
19             $My::Prefix::HashBase::HB_VERSION = '0.016';
20             *My::Prefix::HashBase::ATTR_SUBS = \%Object::HashBase::ATTR_SUBS;
21             *My::Prefix::HashBase::ATTR_LIST = \%Object::HashBase::ATTR_LIST;
22             *My::Prefix::HashBase::VERSION = \%Object::HashBase::VERSION;
23             *My::Prefix::HashBase::CAN_CACHE = \%Object::HashBase::CAN_CACHE;
24             }
25              
26              
27             require Carp;
28             {
29 1     1   6 no warnings 'once';
  1         2  
  1         256  
30             $Carp::Internal{+__PACKAGE__} = 1;
31             }
32              
33             BEGIN {
34             {
35             # Make sure none of these get messed up.
36 1     1   4 local ($SIG{__DIE__}, $@, $?, $!, $^E);
  1         10  
37 1 50       2 if (eval { require Class::XSAccessor; Class::XSAccessor->VERSION(1.19); 1 }) {
  1         7  
  1         22  
  1         7  
38             *CLASS_XS_ACCESSOR = sub() { 1 }
39 1         8 }
40             else {
41             *CLASS_XS_ACCESSOR = sub() { 0 }
42 0         0 }
43             }
44              
45             # these are not strictly equivalent, but for out use we don't care
46             # about order
47             *_isa = ($] >= 5.010 && require mro) ? \&mro::get_linear_isa : sub {
48 1     1   7 no strict 'refs';
  1         2  
  1         163  
49 0         0 my @packages = ($_[0]);
50 0         0 my %seen;
51 0         0 for my $package (@packages) {
52 0         0 push @packages, grep !$seen{$_}++, @{"$package\::ISA"};
  0         0  
53             }
54 0         0 return \@packages;
55             }
56 1 50 33     670 }
57              
58             sub _is_role {
59 2     2   4 my $pkg = shift;
60 2 50       9 return 0 unless $INC{'Role/Tiny.pm'};
61 0 0       0 return Role::Tiny->is_role($pkg) ? 1 : 0;
62             }
63              
64             my %SPEC = (
65             '^' => {reader => 1, writer => 0, dep_writer => 1, read_only => 0, strip => 1},
66             '-' => {reader => 1, writer => 0, dep_writer => 0, read_only => 1, strip => 1},
67             '>' => {reader => 0, writer => 1, dep_writer => 0, read_only => 0, strip => 1},
68             '<' => {reader => 1, writer => 0, dep_writer => 0, read_only => 0, strip => 1},
69             '+' => {reader => 0, writer => 0, dep_writer => 0, read_only => 0, strip => 1},
70             '~' => {reader => 1, writer => 1, dep_writer => 0, read_only => 0, strip => 1, no_xs => 1},
71             );
72              
73 2     2 0 14 sub spec { \%SPEC }
74              
75             sub import {
76 2     2   3983 my $class = shift;
77 2         6 my $into = caller;
78 2         7 $class->do_import($into, @_);
79             }
80              
81             sub do_import {
82 2     2 0 4 my $class = shift;
83 2         5 my $into = shift;
84              
85             # Make sure we list the OLDEST version used to create this class.
86 2   33     8 my $ver = $My::Prefix::HashBase::HB_VERSION || $My::Prefix::HashBase::VERSION;
87 2 50 33     69 $My::Prefix::HashBase::VERSION{$into} = $ver if !$My::Prefix::HashBase::VERSION{$into} || $My::Prefix::HashBase::VERSION{$into} > $ver;
88              
89 2         4 my (@parents, @roles, @attrs);
90 2         5 for my $arg (@_) {
91 2 50 33     12 if (defined($arg) && length($arg)) {
92 2         6 my $p = substr($arg, 0, 1);
93 2 50       5 if ($p eq '@') {
94 0         0 push @parents, substr($arg, 1);
95 0         0 next;
96             }
97 2 50       5 if ($p eq '&') {
98 0         0 push @roles, substr($arg, 1);
99 0         0 next;
100             }
101             }
102 2         6 push @attrs, $arg;
103             }
104              
105 2         4 for my $parent (@parents) {
106 0         0 my $pm = $parent;
107 0         0 $pm =~ s{::}{/}g;
108 0         0 $pm .= '.pm';
109 0 0       0 unless ($INC{$pm}) {
110 0         0 local ($@);
111 0 0       0 unless (eval { require $pm; 1 }) {
  0         0  
  0         0  
112 0         0 Carp::croak("Could not load parent class '$parent': $@");
113             }
114             }
115 1     1   7 no strict 'refs';
  1         2  
  1         358  
116 0 0       0 push @{"$into\::ISA"}, $parent unless grep { $_ eq $parent } @{"$into\::ISA"};
  0         0  
  0         0  
  0         0  
117             }
118              
119 2         13 my $isa = _isa($into);
120 2   50     33 my $attr_list = $My::Prefix::HashBase::ATTR_LIST{$into} ||= [];
121 2   50     14 my $attr_subs = $My::Prefix::HashBase::ATTR_SUBS{$into} ||= {};
122              
123 2         3 my @pre_init;
124             my @post_init;
125              
126 2 50       5 my $add_new = _is_role($into) ? 0 : 1;
127              
128 2 100 66     28 if ($add_new && (my $have_new = $into->can('new'))) {
129 1   50     5 my $new_lookup = $My::Prefix::HashBase::NEW_LOOKUP //= {};
130 1 50       5 $add_new = 0 unless $new_lookup->{$have_new};
131             }
132              
133             my %subs = (
134             ($add_new ? ($class->_build_new($into, \@pre_init, \@post_init)) : ()),
135 2 50       20 (map %{$My::Prefix::HashBase::ATTR_SUBS{$_} || {}}, @{$isa}[1 .. $#$isa]),
  1 50       7  
  2         7  
136             ($class->args_to_subs($attr_list, $attr_subs, \@attrs, $into)),
137             );
138              
139 1     1   7 no strict 'refs';
  1         3  
  1         498  
140 2         11 while (my ($k, $v) = each %subs) {
141 17 100       49 if (ref($v) eq 'CODE') {
142 13         25 *{"$into\::$k"} = $v;
  13         64  
143             }
144             else {
145 4         12 my ($sub, @args) = @$v;
146 4         88 $sub->(@args);
147             }
148             }
149              
150 2 50       15 if (@roles) {
151 0 0       0 Carp::croak("My::Prefix::HashBase '&' role prefix requires Perl 5.010 or newer (this is $])")
152             if $] < 5.010;
153              
154 0 0       0 unless ($INC{'Role/Tiny.pm'}) {
155 0         0 local ($@);
156 0 0       0 unless (eval { require Role::Tiny; 1 }) {
  0         0  
  0         0  
157 0         0 Carp::croak("My::Prefix::HashBase '&' role prefix requires Role::Tiny but it could not be loaded: $@");
158             }
159             }
160              
161 0 0       0 unless (Role::Tiny->can('is_role')) {
162 0         0 Carp::croak("My::Prefix::HashBase '&' role prefix requires Role::Tiny 1.003000 or newer (is_role missing)");
163             }
164              
165 0         0 for my $role (@roles) {
166 0         0 my $pm = $role;
167 0         0 $pm =~ s{::}{/}g;
168 0         0 $pm .= '.pm';
169              
170 0 0       0 unless ($INC{$pm}) {
171 0         0 local ($@);
172 0 0       0 unless (eval { require $pm; 1 }) {
  0         0  
  0         0  
173 0         0 Carp::croak("Could not load role '$role': $@");
174             }
175             }
176              
177 0 0       0 Carp::croak("'$role' is not a Role::Tiny role")
178             unless Role::Tiny->is_role($role);
179              
180             Carp::croak("'$role' does not use My::Prefix::HashBase")
181 0 0       0 unless exists $My::Prefix::HashBase::VERSION{$role};
182              
183 0   0     0 my $role_subs = $My::Prefix::HashBase::ATTR_SUBS{$role} || {};
184              
185 1     1   7 no strict 'refs';
  1         4  
  1         2166  
186 0         0 for my $const (keys %$role_subs) {
187 0 0       0 next if defined &{"$into\::$const"}; # keep existing sub, no override, no warn
  0         0  
188 0         0 *{"$into\::$const"} = $role_subs->{$const};
  0         0  
189             }
190              
191 0   0     0 my $role_attr_list = $My::Prefix::HashBase::ATTR_LIST{$role} || [];
192 0   0     0 push @{$My::Prefix::HashBase::ROLE_ATTRS{$into} ||= []}, @$role_attr_list;
  0         0  
193             }
194              
195 0         0 my $key = "My::Prefix::HashBase::role_applier::$into";
196 0   0     0 my $applier = $^H{$key} ||= My::Prefix::HashBase::_RoleApplier->new($into);
197 0         0 $applier->add($_) for @roles;
198             }
199             }
200              
201             sub args_to_subs {
202 2     2 0 5 my $class = shift;
203 2         5 my ($attr_list, $attr_subs, $args, $into) = @_;
204              
205 2         14 my $use_gen = $class->can('gen_accessor') ;
206              
207 2         5 my %out;
208              
209 2         6 while (@$args) {
210 2         4 my $x = shift @$args;
211 2         5 my $p = substr($x, 0, 1);
212              
213 2   50     5 my $spec = $class->spec->{$p} || {reader => 1, writer => 1};
214 2 50       16 substr($x, 0, 1) = '' if $spec->{strip};
215              
216 2         5 push @$attr_list => $x;
217 2         6 my ($sub, $attr) = (uc $x, $x);
218              
219 2     0   20 $attr_subs->{$sub} = sub() { $attr };
  0         0  
220 2         5 $out{$sub} = $attr_subs->{$sub};
221              
222 2         4 my $copy = "$attr";
223 2 50       5 if ($spec->{reader}) {
224 2 50       8 if ($use_gen) {
    50          
225 0         0 $out{$attr} = $class->gen_accessor(reader => $copy, $spec, $args);
226             }
227             elsif (CLASS_XS_ACCESSOR && !$spec->{no_xs}) {
228 2         9 $out{$attr} = [\&Class::XSAccessor::newxs_getter, "$into\::$attr", $copy];
229             }
230             else {
231 0     0   0 $out{$attr} = sub { $_[0]->{$attr} };
  0         0  
232             }
233             }
234              
235 2 50       5 if ($spec->{writer}) {
    0          
    0          
236 2 50       5 if ($use_gen) {
    50          
237 0         0 $out{"set_$attr"} = $class->gen_accessor(writer => $copy, $spec, $args);
238             }
239             elsif(CLASS_XS_ACCESSOR && !$spec->{no_xs}) {
240 2         8 $out{"set_$attr"} = [\&Class::XSAccessor::newxs_setter, "$into\::set_$attr", $copy, 0];
241             }
242             else {
243 0     0   0 $out{"set_$attr"} = sub { $_[0]->{$attr} = $_[1] };
  0         0  
244             }
245             }
246             elsif($spec->{read_only}) {
247 0 0   0   0 $out{"set_$attr"} = $use_gen ? $class->gen_accessor(read_only => $copy, $spec, $args) : sub { Carp::croak("'$attr' is read-only") };
  0         0  
248             }
249             elsif($spec->{dep_writer}) {
250 0 0   0   0 $out{"set_$attr"} = $use_gen ? $class->gen_accessor(dep_writer => $copy, $spec, $args) : sub { Carp::carp("set_$attr() is deprecated"); $_[0]->{$attr} = $_[1] };
  0         0  
  0         0  
251             }
252              
253 2 50       10 if ($spec->{custom}) {
254 0         0 my %add = $class->gen_accessor(custom => $copy, $spec, $args);
255 0         0 $out{$_} = $add{$_} for keys %add;
256             }
257             }
258              
259 2         14 return %out;
260             }
261              
262             sub attr_list {
263 0     0 1 0 my $class = shift;
264              
265 0         0 my $isa = _isa($class);
266              
267 0         0 my %seen;
268             my @list;
269 0         0 for my $pkg (reverse @$isa) {
270 0 0 0     0 if (0.004 > ($My::Prefix::HashBase::VERSION{$pkg} || 0)) {
271 0         0 Carp::carp("$pkg uses an inlined version of My::Prefix::HashBase too old to support attr_list()");
272 0         0 next;
273             }
274 0         0 my $own = $My::Prefix::HashBase::ATTR_LIST{$pkg};
275 0   0     0 my $role_attrs = $My::Prefix::HashBase::ROLE_ATTRS{$pkg} || [];
276 0 0       0 for my $a (@$role_attrs, ($own ? @$own : ())) {
277 0 0       0 push @list, $a unless $seen{$a}++;
278             }
279             }
280              
281 0         0 return @list;
282             }
283              
284             sub _build_new {
285 2     2   4 my $class = shift;
286 2         5 my ($into, $pre_init, $post_init) = @_;
287              
288 2     0   9 my $add_pre_init = sub(&) { push @$pre_init => $_[-1] };
  0         0  
289 2     0   16 my $add_post_init = sub(&) { push @$post_init => $_[-1] };
  0         0  
290              
291 2         14 my $__pre_init = $into->can('_pre_init');
292 2 100   0   8 my $_pre_init = $__pre_init ? sub { ($__pre_init->(), @$pre_init) } : sub { @$pre_init };
  0         0  
  0         0  
293              
294 2         11 my $__post_init = $into->can('_post_init');
295 2 100   0   11 my $_post_init = $__post_init ? sub { ($__post_init->(), @$post_init) } : sub { @$post_init };
  0         0  
  0         0  
296              
297             my $new = sub {
298 0     0   0 my $class = shift;
299              
300 0         0 my $self;
301              
302 0 0       0 if (@_ == 1) {
303 0         0 my $arg = shift;
304 0         0 my $type = ref($arg);
305              
306 0 0       0 if ($type eq 'HASH') {
307 0         0 $self = bless({%$arg}, $class);
308             }
309             else {
310 0 0       0 Carp::croak("Not sure what to do with '$type' in $class constructor")
311             unless $type eq 'ARRAY';
312              
313 0         0 my %proto;
314 0         0 my @attributes = attr_list($class);
315 0         0 while (@$arg) {
316 0         0 my $val = shift @$arg;
317 0 0       0 my $key = shift @attributes or Carp::croak("Too many arguments for $class constructor");
318 0         0 $proto{$key} = $val;
319             }
320              
321 0         0 $self = bless(\%proto, $class);
322             }
323             }
324             else {
325 0         0 $self = bless({@_}, $class);
326             }
327              
328             $My::Prefix::HashBase::CAN_CACHE{$class} = $self->can('init')
329 0 0       0 unless exists $My::Prefix::HashBase::CAN_CACHE{$class};
330              
331 0         0 $self->$_() for $_pre_init->();
332 0 0       0 $self->init() if $My::Prefix::HashBase::CAN_CACHE{$class};
333 0         0 $self->$_() for reverse $_post_init->();
334              
335 0         0 $self;
336 2         41 };
337              
338 2   100     12 my $new_lookup = $My::Prefix::HashBase::NEW_LOOKUP //= {};
339 2         7 $new_lookup->{$new} = 1;
340              
341 2         3 my %out;
342              
343             {
344 1     1   9 no strict 'refs';
  1         2  
  1         647  
  2         4  
345 2 50       2 $out{new} = $new unless defined(&{"${into}\::new"});
  2         11  
346 2 50       4 $out{add_pre_init} = $add_pre_init unless defined(&{"${into}\::add_pre_init"});
  2         12  
347 2 50       4 $out{add_post_init} = $add_post_init unless defined(&{"${into}\::add_post_init"});
  2         10  
348 2 50       3 $out{_pre_init} = $_pre_init unless defined(&{"${into}\::_pre_init"});
  2         8  
349 2 50       3 $out{_post_init} = $_post_init unless defined(&{"${into}\::_post_init"});
  2         7  
350             }
351              
352 2         26 return %out;
353             }
354              
355             # _RoleApplier — deferred Role::Tiny composition.
356             #
357             # My::Prefix::HashBase's '&' import prefix copies role constants into the consumer
358             # eagerly (so `$self->{+FOO}` resolves at compile time), then defers actual
359             # Role::Tiny->apply_roles_to_package to end of consumer's compile scope by
360             # storing a blessed object in %^H. Perl destroys %^H entries at end of compile
361             # scope, triggering DESTROY here, which finally composes the role(s).
362              
363             package # hide from PAUSE indexer
364             My::Prefix::HashBase::_RoleApplier;
365              
366             sub new {
367 0     0     my ($class, $into) = @_;
368 0           return bless { into => $into, roles => [] }, $class;
369             }
370              
371             sub add {
372 0     0     my ($self, $role) = @_;
373 0           push @{$self->{roles}}, $role
374 0 0         unless grep { $_ eq $role } @{$self->{roles}};
  0            
  0            
375             }
376              
377             sub DESTROY {
378 0     0     my $self = shift;
379 0 0         return unless @{$self->{roles}};
  0            
380 0           local $@;
381 0           my $ok = eval { Role::Tiny->apply_roles_to_package($self->{into}, @{$self->{roles}}); 1 };
  0            
  0            
  0            
382 0 0         unless ($ok) {
383 0   0       my $err = $@ || 'unknown error';
384 0           my $into = $self->{into};
385 0           my $roles = join(', ', @{$self->{roles}});
  0            
386 0           warn "My::Prefix::HashBase: failed to compose role(s) [$roles] into $into: $err";
387             }
388             }
389              
390             1;
391              
392             __END__