File Coverage

blib/lib/Class/Accessor/Assert.pm
Criterion Covered Total %
statement 57 87 65.5
branch 19 44 43.1
condition 16 30 53.3
subroutine 10 15 66.6
pod 3 4 75.0
total 105 180 58.3


line stmt bran cond sub pod time code
1             package Class::Accessor::Assert;
2 1     1   26401 use 5.006;
  1         4  
  1         47  
3 1     1   6 use strict;
  1         2  
  1         58  
4 1     1   7 use warnings;
  1         2  
  1         39  
5 1     1   7 use base qw(Class::Accessor Class::Data::Inheritable);
  1         2  
  1         1680  
6 1     1   3522 use Carp qw(croak confess);
  1         2  
  1         155  
7             our $VERSION = '1.41';
8              
9             sub _mk_accessors {
10 1     1   20 my ( $self, $maker, @fields ) = @_;
11 1 50       28 $self->mk_classdata("accessor_specs")
12             unless $self->can("accessor_specs");
13              
14 1         29 my %spec = $self->parse_fields(@fields);
15 1 50       3 $self->accessor_specs( { %spec, %{ $self->accessor_specs || {} } } );
  1         5  
16              
17 1         33 $self->SUPER::_mk_accessors( 'rw', keys %spec );
18              
19             {
20 1     1   5 no strict 'refs';
  1         2  
  1         1212  
  1         155  
21              
22             # additional methods for magic array methods
23 1   33     6 my $class = ref $self || $self;
24             # Note how we curry the subs with the lexical "$field":
25             # The subs are closures and therefore have access to their lexical
26             # scope. Clarity suffers from this, but the performance should be
27             # about 25% higher than a cleaner approach due to a saved subroutine
28             # call for every ary_*(...) call.
29 1         3 for my $field ( grep { $spec{$_}{array} } keys %spec ) {
  3         12  
30             # foo_push sub
31 0         0 *{"${class}::${field}_push"} = sub {
32 0     0   0 my ( $self, @values ) = @_;
33 0 0       0 $self->{$field} = [] unless defined $self->{$field};
34 0         0 push @{ $self->{$field} }, @values;
  0         0  
35 0         0 };
36             # foo_pop sub
37 0         0 *{"${class}::${field}_pop"} = sub {
38 0     0   0 my ( $self ) = @_;
39 0 0       0 return pop @{ $self->{$field} || [] };
  0         0  
40 0         0 };
41             # foo_unshift sub
42 0         0 *{"${class}::${field}_unshift"} = sub {
43 0     0   0 my ( $self, @values ) = @_;
44 0 0       0 $self->{$field} = [] unless defined $self->{$field};
45 0         0 unshift @{ $self->{$field} }, @values;
  0         0  
46 0         0 };
47             # foo_shift sub
48 0         0 *{"${class}::${field}_shift"} = sub {
49 0     0   0 my ( $self ) = @_;
50 0 0       0 return shift @{ $self->{$field} || [] };
  0         0  
51 0         0 };
52             }
53             }
54             }
55              
56             sub new {
57 5     5 1 2720 my ( $self, $stuff ) = @_;
58 5 100       6 my $not_a_void_context = eval { %{ $stuff || {} } };
  5         8  
  5         31  
59 5 100       305 croak "$stuff doesn't look much like a hash to me" if $@;
60 4 50       21 if ( $self->can("accessor_specs") ) {
61 4         13 my $spec = $self->accessor_specs;
62 4         37 for my $k ( keys %$spec ) {
63 11 100 100     275 confess "Required member $k not given to constructor"
64             if $spec->{$k}->{required}
65             and not exists $stuff->{$k};
66 10 100 100     225 confess "Member $k needs to be of type " . $spec->{$k}->{class}
      100        
67             if exists $spec->{$k}->{class}
68             and exists $stuff->{$k}
69             and !UNIVERSAL::isa( $stuff->{$k}, $spec->{$k}->{class} );
70             }
71             }
72 2         14 return $self->SUPER::new($stuff);
73             }
74              
75             sub set {
76 3 50   3 1 1262 return shift->SUPER::set(@_) unless $_[0]->can("accessor_specs");
77 3         9 my ( $self, $key ) = splice( @_, 0, 2 );
78 3         11 my $spec = $self->accessor_specs;
79 3 50 33     34 return $self->SUPER::set( $key, @_ )
80             if !exists $spec->{$key}
81             or @_ > 1; # No support for arrays
82 3 100 33     377 confess "Member $key needs to be of type " . $spec->{$key}->{class}
      66        
83             if defined $_[0]
84             and exists $spec->{$key}->{class}
85             and !UNIVERSAL::isa( $_[0], $spec->{$key}->{class} );
86              
87 1 50 33     9 $_[0] = [ $_[0] ]
      33        
88             if defined $_[0]
89             and $spec->{$key}->{array}
90             and ref $_[0] ne 'ARRAY';
91              
92 1         31 $self->{$key} = $_[0];
93             }
94              
95             sub get {
96 0 0   0 1 0 return shift->SUPER::get(@_) unless $_[0]->can("accessor_specs");
97 0         0 my ( $self, $key ) = splice( @_, 0, 2 );
98 0         0 my $spec = $self->accessor_specs;
99 0 0 0     0 return $self->SUPER::get( $key, @_ )
100             if !exists $spec->{$key}
101             or @_ > 1; # No support for arrays
102 0 0       0 if ( $spec->{$key}{array} ) {
103             wantarray
104 0 0       0 ? @{ $self->SUPER::get( $key, @_ ) || [] }
  0 0       0  
105             : $self->SUPER::get( $key, @_ );
106             }
107             else {
108 0         0 $self->SUPER::get( $key, @_ );
109             }
110             }
111              
112             sub parse_fields {
113 1     1 0 3 my ( $self, @fields ) = @_;
114 1         1 my %spec;
115 1         3 for my $f (@fields) {
116 3         5 my $orig_f = $f; # For error reporting
117 3         4 my %subspec;
118              
119             # All the tests go here
120 3         9 $subspec{required} = $f =~ s/^\+//;
121 3 100       19 $f =~ s/=(.*)// and $subspec{class} = $1;
122 3         12 $subspec{array} = $f =~ s/^\@//;
123 3 50       14 $f =~ /^\w+$/
124             or croak "Couldn't understand field specification $orig_f";
125 3         9 $spec{$f} = \%subspec;
126             }
127 1         8 return %spec;
128             }
129              
130             1;
131             __END__