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   1724793 use 5.008;
  28         296  
4 28     28   154 use strict;
  28         47  
  28         604  
5 28     28   133 use warnings;
  28         60  
  28         1000  
6              
7 28     28   185 use Carp ();
  28         64  
  28         462  
8 28     28   14300 use Hash::Util ();
  28         72790  
  28         1799  
9              
10             our $VERSION = '0.17';
11             our $AUTHORITY = 'cpan:STEVAN';
12              
13 28 50   28   15339 BEGIN { $] >= 5.010 ? require mro : require MRO::Compat }
14              
15             sub new {
16 90     90 1 25610 my $class = shift;
17 90 100       265 $class = ref $class if ref $class;
18              
19 90         333 my $proto = $class->BUILDARGS( @_ );
20              
21 88 50 33     516 Carp::confess('BUILDARGS must return a HASH reference, not '.$proto)
22             unless $proto && ref $proto eq 'HASH';
23              
24 88         322 my $self = $class->BLESS( $proto );
25              
26 85 50 33     528 Carp::confess('BLESS must return a blessed reference, not '.$self)
27             unless defined $self && UNIVERSAL::isa( $self, 'UNIVERSAL' );
28              
29 85 100       591 $self->can('BUILD') && UNIVERSAL::Object::Util::BUILDALL( $self, $proto );
30              
31 85         421 return $self;
32             }
33              
34             sub BUILDARGS {
35 86     86 1 200 my $class = shift;
36 86 100 100     332 if ( scalar @_ == 1 && ref $_[0] ) {
37 4 100       298 Carp::confess('Invalid BUILDARGS args for '.$class.', expected a HASH reference but got a '.$_[0])
38             unless ref $_[0] eq 'HASH';
39 3         4 return +{ %{ $_[0] } };
  3         11  
40             }
41             else {
42 82 100       429 Carp::confess('Invalid BUILDARGS args for '.$class.', expected an even sized list, but got '.(scalar @_).' element(s) instead')
43             unless ((scalar @_) % 2) == 0;
44 81         249 return +{ @_ };
45             }
46             }
47              
48             sub BLESS {
49 86     86 1 159 my $class = $_[0];
50 86 50       196 $class = ref $class if ref $class;
51 86         134 my $proto = $_[1];
52              
53 86 50 33     825 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 86         301 my $instance = $class->CREATE( $proto );
57              
58 83 50 33     448 Carp::confess('CREATE must return a reference to bless, not '.$instance)
59             unless defined $instance && ref $instance;
60              
61 83         171 my $repr = ref $instance;
62 83         192 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 83 100       235 if ( $repr eq 'HASH' ) {
70 67         159 my %slots = $self->SLOTS;
71 67         497 Hash::Util::lock_keys( %$self, keys %slots );
72             }
73              
74 83         2991 return $self;
75             }
76              
77             sub CREATE {
78 76     76 1 176 my $class = $_[0];
79 76 50       198 $class = ref $class if ref $class;
80 76         176 my $proto = $_[1];
81              
82 76         261 my $self = $class->REPR( $proto );
83 76         369 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       596 foreach sort keys %slots;
95              
96 73         571 return $self;
97             }
98              
99 64     64 1 127 sub REPR () { +{} }
100              
101             sub SLOTS {
102 140     140 1 254 my $class = $_[0];
103 140 100       344 $class = ref $class if ref $class;
104 28     28   185 no strict 'refs';
  28         61  
  28         972  
105 28     28   177 no warnings 'once';
  28         87  
  28         7977  
106 140         186 return %{$class . '::HAS'};
  140         669  
107             }
108              
109             sub DESTROY {
110 56     56   100345 my $self = $_[0];
111 56 100       370 $self->can('DEMOLISH') && UNIVERSAL::Object::Util::DEMOLISHALL( $self );
112 56         2473 return;
113             }
114              
115             ## Utils
116              
117             sub UNIVERSAL::Object::Util::BUILDALL {
118 4     4   10 my $self = $_[0];
119 4         5 my $proto = $_[1];
120 4         5 foreach my $super ( reverse @{ mro::get_linear_isa( ref $self ) } ) {
  4         23  
121 11         50 my $fully_qualified_name = $super . '::BUILD';
122             $self->$fully_qualified_name( $proto )
123 11 100       15 if defined &{ $fully_qualified_name };
  11         61  
124             }
125             }
126              
127             sub UNIVERSAL::Object::Util::DEMOLISHALL {
128 3     3   4 my $self = $_[0];
129 3         5 foreach my $super ( @{ mro::get_linear_isa( ref $self ) } ) {
  3         13  
130 9         57 my $fully_qualified_name = $super . '::DEMOLISH';
131             $self->$fully_qualified_name()
132 9 100       11 if defined &{ $fully_qualified_name };
  9         39  
133             }
134             }
135              
136             1;
137              
138             __END__