File Coverage

blib/lib/Object/Properties.pm
Criterion Covered Total %
statement 86 86 100.0
branch 17 18 94.4
condition 3 3 100.0
subroutine 21 21 100.0
pod n/a
total 127 128 99.2


line stmt bran cond sub pod time code
1 2     2   135936 use 5.006; # for us
  2         22  
2 2     2   28 use 5.008008; # for Sentinel
  2         7  
3 2     2   10 use strict;
  2         4  
  2         45  
4 2     2   11 use warnings;
  2         2  
  2         120  
5              
6             package Object::Properties;
7              
8             our $VERSION = '1.003';
9              
10 2     2   862 use Sentinel ();
  2         2251  
  2         1467  
11              
12             sub _make_init {
13 7     7   8 my @field = @{ $_[0] };
  7         17  
14 7         9 my @setter = @{ $_[1] };
  7         11  
15             return sub {
16 42     42   4990 my $self = shift;
17 42         63 my ( $hash ) = @_;
18 42         57 my ( @v, @s );
19 42         96 for my $i ( 0 .. $#field ) {
20 138 100       245 next unless exists $hash->{ $field[ $i ] };
21 113         131 push @s, $setter[ $i ];
22 113         192 push @v, delete $hash->{ $field[ $i ] };
23             }
24 42 50       101 delete @$self{ @field } if $hash != $self;
25 42         75 for my $i ( 0 .. $#v ) { $self->$_( $v[$i] ) for $s[$i] }
  113         408  
26 7         28 };
27             }
28              
29             sub _make_getter {
30 27     27   39 my ( $prop ) = @_;
31 27     8   63 return sub { $_[0]{ $prop } };
  8         4186  
32             }
33              
34             sub _make_getter_setter {
35 1     1   3 my ( $prop ) = @_;
36 1     3   4 return sub : lvalue { $_[0]{ $prop } };
  3         3307  
37             }
38              
39             sub _make_setter {
40 23     23   36 my ( $prop, $munger ) = @_;
41             return sub {
42 116     116   169 local $Carp::Internal{ (__PACKAGE__) } = 1;
43 116         165 $_[0]{ $prop } = $_, return for &$munger;
44 23         74 };
45             }
46              
47             sub _make_accessor {
48 3     3   4 my ( $getter, $setter ) = @_;
49 3     7   8 return sub : lvalue { Sentinel::sentinel get => $getter, set => $setter, obj => $_[0] };
  7         47  
50             }
51              
52             sub import {
53 9     9   5041 my $class = shift;
54 9         16 my $pkg = caller;
55              
56 9         17 my ( @prop, %ro, %setter );
57 9         16 for ( @_ ) {
58 51 100 100     148 if ( @prop and 'CODE' eq ref ) {
59 23         41 $setter{ $prop[-1] } = _make_setter $prop[-1], $_;
60 23         38 next;
61             }
62 28 100       117 die "Invalid accessor name '$_'" unless /\A([+]?)([^\W\d]\w*)\z/;
63 27 100       83 $ro{ $2 } = 1 unless $1;
64 27         65 push @prop, $2;
65             }
66              
67 8         24 for my $prop ( @prop ) {
68 27         46 my $getter = _make_getter $prop;
69 27         62 my $setter = $setter{ $prop };
70             my $accessor
71 27 100       53 = $ro{ $prop } ? $getter
    100          
72             : $setter ? _make_accessor $getter, $setter
73             : _make_getter_setter $prop;
74 2     2   15 { no strict 'refs'; *{ $pkg.'::'.$prop } = $accessor }
  2         4  
  2         184  
  27         30  
  27         28  
  27         87  
75             }
76              
77 8 100       23 if ( my @sprop = grep { exists $setter{ $_ } } @prop ) {
  27         71  
78 7         34 my $init = _make_init \@sprop, [ @setter{ @sprop } ];
79 2     2   12 { no strict 'refs'; *{ $pkg.'::PROPINIT' } = $init }
  2         4  
  2         82  
  7         14  
  7         8  
  7         21  
80             }
81              
82 2     2   10 my $ISA = do { no strict 'refs'; \@{ $pkg.'::ISA' } };
  2         4  
  2         306  
  8         8  
  8         10  
  8         20  
83 8 100       53 @$ISA = __PACKAGE__ . '::Base' unless @$ISA;
84              
85 8         80 return 1;
86             }
87              
88             package Object::Properties::Base;
89              
90             our $VERSION = '1.003';
91              
92 2     2   1495 use NEXT ();
  2         8148  
  2         172  
93              
94             sub new {
95 42     42   18792 my $class = shift;
96 42         115 my $self = bless { @_ }, $class;
97 42         83 local $Carp::Internal{ (__PACKAGE__) } = 1;
98 42         223 $self->EVERY::LAST::PROPINIT( $self );
99 40         333 return $self;
100             }
101              
102             1;
103              
104             __END__