File Coverage

blib/lib/Object/HashBase.pm
Criterion Covered Total %
statement 76 84 90.4
branch 28 34 82.3
condition 8 17 47.0
subroutine 13 14 92.8
pod 1 1 100.0
total 126 150 84.0


line stmt bran cond sub pod time code
1             package Object::HashBase;
2 2     2   66264 use strict;
  2         13  
  2         66  
3 2     2   9 use warnings;
  2         4  
  2         108  
4              
5             our $VERSION = '0.009';
6             our $HB_VERSION = $VERSION;
7             # The next line is for inlining
8             # <-- START -->
9              
10             require Carp;
11             {
12 2     2   10 no warnings 'once';
  2         3  
  2         187  
13             $Carp::Internal{+__PACKAGE__} = 1;
14             }
15              
16             BEGIN {
17             # these are not strictly equivalent, but for out use we don't care
18             # about order
19             *_isa = ($] >= 5.010 && require mro) ? \&mro::get_linear_isa : sub {
20 2     2   15 no strict 'refs';
  2         3  
  2         257  
21 0         0 my @packages = ($_[0]);
22 0         0 my %seen;
23 0         0 for my $package (@packages) {
24 0         0 push @packages, grep !$seen{$_}++, @{"$package\::ISA"};
  0         0  
25             }
26 0         0 return \@packages;
27             }
28 2 50 33 2   986 }
29              
30             my %SPEC = (
31             '^' => {reader => 1, writer => 0, dep_writer => 1, read_only => 0, strip => 1},
32             '-' => {reader => 1, writer => 0, dep_writer => 0, read_only => 1, strip => 1},
33             '>' => {reader => 0, writer => 1, dep_writer => 0, read_only => 0, strip => 1},
34             '<' => {reader => 1, writer => 0, dep_writer => 0, read_only => 0, strip => 1},
35             '+' => {reader => 0, writer => 0, dep_writer => 0, read_only => 0, strip => 1},
36             );
37              
38             sub import {
39 21     21   1095 my $class = shift;
40 21         42 my $into = caller;
41              
42             # Make sure we list the OLDEST version used to create this class.
43 21   33     54 my $ver = $Object::HashBase::HB_VERSION || $Object::HashBase::VERSION;
44 21 50 33     111 $Object::HashBase::VERSION{$into} = $ver if !$Object::HashBase::VERSION{$into} || $Object::HashBase::VERSION{$into} > $ver;
45              
46 21         88 my $isa = _isa($into);
47 21   50     98 my $attr_list = $Object::HashBase::ATTR_LIST{$into} ||= [];
48 21   50     79 my $attr_subs = $Object::HashBase::ATTR_SUBS{$into} ||= {};
49              
50             my %subs = (
51             ($into->can('new') ? () : (new => \&_new)),
52 6 50       44 (map %{$Object::HashBase::ATTR_SUBS{$_} || {}}, @{$isa}[1 .. $#$isa]),
  21         61  
53             (
54             map {
55 21 100       219 my $p = substr($_, 0, 1);
  45         90  
56 45         64 my $x = $_;
57              
58 45   100     175 my $spec = $SPEC{$p} || {reader => 1, writer => 1};
59              
60 45 100       92 substr($x, 0, 1) = '' if $spec->{strip};
61 45         93 push @$attr_list => $x;
62 45         101 my ($sub, $attr) = (uc $x, $x);
63              
64 45     0   355 $attr_subs->{$sub} = sub() { $attr };
  0         0  
65 45         112 my %out = ($sub => $attr_subs->{$sub});
66              
67 45 100   17   148 $out{$attr} = sub { $_[0]->{$attr} } if $spec->{reader};
  17         68  
68 45 100   8   256 $out{"set_$attr"} = sub { $_[0]->{$attr} = $_[1] } if $spec->{writer};
  8         23  
69 45 100   1   103 $out{"set_$attr"} = sub { Carp::croak("'$attr' is read-only") } if $spec->{read_only};
  1         211  
70 45 100   1   94 $out{"set_$attr"} = sub { Carp::carp("set_$attr() is deprecated"); $_[0]->{$attr} = $_[1] } if $spec->{dep_writer};
  1         93  
  1         4  
71              
72 45         209 %out;
73             } @_
74             ),
75             );
76              
77 2     2   15 no strict 'refs';
  2         10  
  2         953  
78 21         96 *{"$into\::$_"} = $subs{$_} for keys %subs;
  153         2270  
79             }
80              
81             sub attr_list {
82 3     3 1 6 my $class = shift;
83              
84 3         9 my $isa = _isa($class);
85              
86 3         6 my %seen;
87 15         36 my @list = grep { !$seen{$_}++ } map {
88 3         6 my @out;
  6         9  
89              
90 6 50 50     22 if (0.004 > ($Object::HashBase::VERSION{$_} || 0)) {
91 0         0 Carp::carp("$_ uses an inlined version of Object::HashBase too old to support attr_list()");
92             }
93             else {
94 6         8 my $list = $Object::HashBase::ATTR_LIST{$_};
95 6 50       17 @out = $list ? @$list : ()
96             }
97              
98 6         15 @out;
99             } reverse @$isa;
100              
101 3         14 return @list;
102             }
103              
104             sub _new {
105 11     11   26 my $class = shift;
106              
107 11         17 my $self;
108              
109 11 100       35 if (@_ == 1) {
110 3         6 my $arg = shift;
111 3         5 my $type = ref($arg);
112              
113 3 100       8 if ($type eq 'HASH') {
114 1         4 $self = bless({%$arg}, $class)
115             }
116             else {
117 2 50       7 Carp::croak("Not sure what to do with '$type' in $class constructor")
118             unless $type eq 'ARRAY';
119              
120 2         3 my %proto;
121 2         4 my @attributes = attr_list($class);
122 2         5 while (@$arg) {
123 9         15 my $val = shift @$arg;
124 9 100       110 my $key = shift @attributes or Carp::croak("Too many arguments for $class constructor");
125 8         18 $proto{$key} = $val;
126             }
127              
128 1         3 $self = bless(\%proto, $class);
129             }
130             }
131             else {
132 8         25 $self = bless({@_}, $class);
133             }
134              
135             $Object::HashBase::CAN_CACHE{$class} = $self->can('init')
136 10 100       46 unless exists $Object::HashBase::CAN_CACHE{$class};
137              
138 10 100       30 $self->init if $Object::HashBase::CAN_CACHE{$class};
139              
140 10         37 $self;
141             }
142              
143             1;
144              
145             __END__