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   1604605 use 5.008;
  28         324  
4 28     28   182 use strict;
  28         70  
  28         731  
5 28     28   167 use warnings;
  28         80  
  28         885  
6              
7 28     28   183 use Carp ();
  28         52  
  28         481  
8 28     28   12694 use Hash::Util ();
  28         67564  
  28         1724  
9              
10             our $VERSION = '0.16';
11             our $AUTHORITY = 'cpan:STEVAN';
12              
13 28 50   28   14532 BEGIN { $] >= 5.010 ? require mro : require MRO::Compat }
14              
15             sub new {
16 90     90 1 22724 my $class = shift;
17 90 100       266 $class = ref $class if ref $class;
18              
19 90         345 my $proto = $class->BUILDARGS( @_ );
20              
21 88 50 33     477 Carp::confess('BUILDARGS must return a HASH reference, not '.$proto)
22             unless $proto && ref $proto eq 'HASH';
23              
24 88         306 my $self = $class->BLESS( $proto );
25              
26 85 50 33     521 Carp::confess('BLESS must return a blessed reference, not '.$self)
27             unless defined $self && UNIVERSAL::isa( $self, 'UNIVERSAL' );
28              
29 85 100       534 $self->can('BUILD') && UNIVERSAL::Object::Util::BUILDALL( $self, $proto );
30              
31 85         350 return $self;
32             }
33              
34             sub BUILDARGS {
35 86     86 1 195 my $class = shift;
36 86 100 100     350 if ( scalar @_ == 1 && ref $_[0] ) {
37 4 100       239 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 82 100       413 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         255 return +{ @_ };
45             }
46             }
47              
48             sub BLESS {
49 86     86 1 188 my $class = $_[0];
50 86 50       222 $class = ref $class if ref $class;
51 86         127 my $proto = $_[1];
52              
53 86 50 33     615 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         294 my $instance = $class->CREATE( $proto );
57              
58 83 50 33     436 Carp::confess('CREATE must return a reference to bless, not '.$instance)
59             unless defined $instance && ref $instance;
60              
61 83         151 my $repr = ref $instance;
62 83         162 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       201 if ( $repr eq 'HASH' ) {
70 67         151 my %slots = $self->SLOTS;
71 67         485 Hash::Util::lock_keys( %$self, keys %slots );
72             }
73              
74 83         2812 return $self;
75             }
76              
77             sub CREATE {
78 76     76 1 148 my $class = $_[0];
79 76 50       179 $class = ref $class if ref $class;
80 76         124 my $proto = $_[1];
81              
82 76         252 my $self = $class->REPR( $proto );
83 76         344 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       564 foreach sort keys %slots;
95              
96 73         550 return $self;
97             }
98              
99 64     64 1 124 sub REPR () { +{} }
100              
101             sub SLOTS {
102 140     140 1 228 my $class = $_[0];
103 140 100       314 $class = ref $class if ref $class;
104 28     28   172 no strict 'refs';
  28         53  
  28         996  
105 28     28   195 no warnings 'once';
  28         64  
  28         7458  
106 140         189 return %{$class . '::HAS'};
  140         641  
107             }
108              
109             sub DESTROY {
110 65     65   115920 my $self = $_[0];
111 65 100       433 $self->can('DEMOLISH') && UNIVERSAL::Object::Util::DEMOLISHALL( $self );
112 65         2548 return;
113             }
114              
115             ## Utils
116              
117             sub UNIVERSAL::Object::Util::BUILDALL {
118 4     4   13 my $self = $_[0];
119 4         6 my $proto = $_[1];
120 4         5 foreach my $super ( reverse @{ mro::get_linear_isa( ref $self ) } ) {
  4         17  
121 11         43 my $fully_qualified_name = $super . '::BUILD';
122             $self->$fully_qualified_name( $proto )
123 11 100       14 if defined &{ $fully_qualified_name };
  11         48  
124             }
125             }
126              
127             sub UNIVERSAL::Object::Util::DEMOLISHALL {
128 3     3   13 my $self = $_[0];
129 3         5 foreach my $super ( @{ mro::get_linear_isa( ref $self ) } ) {
  3         28  
130 9         59 my $fully_qualified_name = $super . '::DEMOLISH';
131             $self->$fully_qualified_name()
132 9 100       12 if defined &{ $fully_qualified_name };
  9         41  
133             }
134             }
135              
136             1;
137              
138             __END__