File Coverage

blib/lib/UNIVERSAL/Object.pm
Criterion Covered Total %
statement 76 76 100.0
branch 29 36 80.5
condition 7 15 46.6
subroutine 17 17 100.0
pod 6 6 100.0
total 135 150 90.0


line stmt bran cond sub pod time code
1             package UNIVERSAL::Object;
2             # ABSTRACT: A useful base class
3 28     28   1727692 use 5.008;
  28         305  
4 28     28   186 use strict;
  28         73  
  28         752  
5 28     28   180 use warnings;
  28         81  
  28         965  
6              
7 28     28   180 use Carp ();
  28         56  
  28         547  
8 28     28   13939 use Hash::Util ();
  28         73218  
  28         1774  
9              
10             our $VERSION = '0.15';
11             our $AUTHORITY = 'cpan:STEVAN';
12              
13 28 50   28   15321 BEGIN { $] >= 5.010 ? require mro : require MRO::Compat }
14              
15             sub new {
16 89     89 1 23385 my $class = shift;
17 89 100       259 $class = ref $class if ref $class;
18              
19 89         353 my $proto = $class->BUILDARGS( @_ );
20              
21 87 50 33     516 Carp::confess('BUILDARGS must return a HASH reference, not '.$proto)
22             unless $proto && ref $proto eq 'HASH';
23              
24 87         331 my $self = $class->BLESS( $proto );
25              
26 84 50 33     638 Carp::confess('BLESS must return a blessed reference, not '.$self)
27             unless defined $self && UNIVERSAL::isa( $self, 'UNIVERSAL' );
28              
29 84 100       605 $self->can('BUILD') && UNIVERSAL::Object::Util::BUILDALL( $self, $proto );
30              
31 84         370 return $self;
32             }
33              
34             sub BUILDARGS {
35 85     85 1 216 my $class = shift;
36 85 100 100     320 if ( scalar @_ == 1 && ref $_[0] ) {
37 4 100       261 Carp::confess('Invalid BUILDARGS args for '.$class.', expected a HASH reference but got a '.$_[0])
38             unless ref $_[0] eq 'HASH';
39 3         5 return +{ %{ $_[0] } };
  3         10  
40             }
41             else {
42 81 100       428 Carp::confess('Invalid BUILDARGS args for '.$class.', expected an even sized list, but got '.(scalar @_).' element(s) instead')
43             unless ((scalar @_) % 2) == 0;
44 80         254 return +{ @_ };
45             }
46             }
47              
48             sub BLESS {
49 85     85 1 150 my $class = $_[0];
50 85 50       196 $class = ref $class if ref $class;
51 85         128 my $proto = $_[1];
52              
53 85 50 33     674 Carp::confess('Invalid BLESS args for '.$class.', You must specify an instance prototype as a HASH ref')
54             unless defined $proto && ref $proto eq 'HASH';
55              
56 85         280 my $instance = $class->CREATE( $proto );
57              
58 82 50 33     480 Carp::confess('CREATE must return a reference to bless, not '.$instance)
59             unless defined $instance && ref $instance;
60              
61 82         161 my $repr = ref $instance;
62 82         171 my $self = bless $instance => $class;
63              
64             # So,... for HASH based instances we'll
65             # lock the set of keys so as to prevent
66             # typos and other such silliness, if
67             # you use other $repr types, you are
68             # on your own, ... sorry ¯\_(ツ)_/¯
69 82 100       217 if ( $repr eq 'HASH' ) {
70 67         149 my %slots = $self->SLOTS;
71 67         522 Hash::Util::lock_keys( %$self, keys %slots );
72             }
73              
74 82         2952 return $self;
75             }
76              
77             sub CREATE {
78 76     76 1 153 my $class = $_[0];
79 76 50       188 $class = ref $class if ref $class;
80 76         128 my $proto = $_[1];
81              
82 76         275 my $self = $class->REPR( $proto );
83 76         349 my %slots = $class->SLOTS;
84              
85             # NOTE:
86             # We could check the return values of SLOTS
87             # and REPR, but they might change and so it
88             # is not something we would always know.
89             # - SL
90              
91             $self->{ $_ } = exists $proto->{ $_ }
92             ? $proto->{ $_ }
93             : $slots{ $_ }->( $self, $proto )
94 76 100       583 foreach sort keys %slots;
95              
96 73         554 return $self;
97             }
98              
99 64     64 1 119 sub REPR () { +{} }
100              
101             sub SLOTS {
102 140     140 1 244 my $class = $_[0];
103 140 100       362 $class = ref $class if ref $class;
104 28     28   187 no strict 'refs';
  28         59  
  28         1073  
105 28     28   173 no warnings 'once';
  28         50  
  28         7872  
106 140         187 return %{$class . '::HAS'};
  140         682  
107             }
108              
109             sub DESTROY {
110 65     65   122255 my $self = $_[0];
111 65 100       455 $self->can('DEMOLISH') && UNIVERSAL::Object::Util::DEMOLISHALL( $self );
112 65         3147 return;
113             }
114              
115             ## Utils
116              
117             sub UNIVERSAL::Object::Util::BUILDALL {
118 4     4   14 my $self = $_[0];
119 4         7 my $proto = $_[1];
120 4         7 foreach my $super ( reverse @{ mro::get_linear_isa( ref $self ) } ) {
  4         16  
121 11         45 my $fully_qualified_name = $super . '::BUILD';
122             $self->$fully_qualified_name( $proto )
123 11 100       15 if defined &{ $fully_qualified_name };
  11         59  
124             }
125             }
126              
127             sub UNIVERSAL::Object::Util::DEMOLISHALL {
128 3     3   14 my $self = $_[0];
129 3         4 foreach my $super ( @{ mro::get_linear_isa( ref $self ) } ) {
  3         17  
130 9         71 my $fully_qualified_name = $super . '::DEMOLISH';
131             $self->$fully_qualified_name()
132 9 100       12 if defined &{ $fully_qualified_name };
  9         42  
133             }
134             }
135              
136             1;
137              
138             __END__