File Coverage

blib/lib/Class/Field.pm
Criterion Covered Total %
statement 98 101 97.0
branch 25 40 62.5
condition 13 24 54.1
subroutine 20 21 95.2
pod 2 6 33.3
total 158 192 82.2


line stmt bran cond sub pod time code
1 4     4   177993 use strict; use warnings;
  4     4   30  
  4         94  
  4         17  
  4         6  
  4         162  
2             package Class::Field;
3             our $VERSION = '0.24';
4              
5 4     4   17 use base 'Exporter';
  4         6  
  4         489  
6              
7             our @EXPORT_OK = qw(field const);
8              
9 4     4   2252 use Encode;
  4         34587  
  4         616  
10              
11             my %code = (
12             sub_start =>
13             "sub {\n local \*__ANON__ = \"%s::%s\";\n",
14             set_default =>
15             " \$_[0]->{%s} = %s\n unless exists \$_[0]->{%s};\n",
16             init =>
17             " return \$_[0]->{%s} = do { my \$self = \$_[0]; %s }\n" .
18             " unless \$#_ > 0 or defined \$_[0]->{%s};\n",
19             weak_init =>
20             " return do {\n" .
21             " \$_[0]->{%s} = do { my \$self = \$_[0]; %s };\n" .
22             " Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n" .
23             " \$_[0]->{%s};\n" .
24             " } unless \$#_ > 0 or defined \$_[0]->{%s};\n",
25             return_if_get =>
26             " return \$_[0]->{%s} unless \$#_ > 0;\n",
27             set =>
28             " \$_[0]->{%s} = \$_[1];\n",
29             weaken =>
30             " Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n",
31             sub_end =>
32             " return \$_[0]->{%s};\n}\n",
33             );
34              
35             sub field {
36 6     6 1 148 my $package = caller;
37 6         8 my ($args, @values) = do {
38 4     4   27 no warnings;
  4         7  
  4         1097  
39 6     6   25 local *boolean_arguments = sub { (qw(-weak)) };
  6         10  
40 6     6   39 local *paired_arguments = sub { (qw(-package -init)) };
  6         9  
41 6         24 Class::Field->parse_arguments(@_);
42             };
43 6         13 my ($field, $default) = @values;
44 6 50       14 $package = $args->{-package} if defined $args->{-package};
45             die "Cannot have a default for a weakened field ($field)"
46 6 50 66     12 if defined $default && $args->{-weak};
47 6 50       7 return if defined &{"${package}::$field"};
  6         32  
48 6 50       11 require Scalar::Util if $args->{-weak};
49 6 100 66     33 my $default_string =
    100 66        
50             ( ref($default) eq 'ARRAY' and not @$default )
51             ? '[]'
52             : (ref($default) eq 'HASH' and not keys %$default )
53             ? '{}'
54             : default_as_code($default);
55              
56 6         23 my $code = sprintf $code{sub_start}, $package, $field;
57 6 100       16 if ($args->{-init}) {
58 2 50       5 if ($args->{-weak}) {
59 0         0 $code .= sprintf $code{weak_init}, $field, $args->{-init}, ($field) x 4;
60             } else {
61 2         10 $code .= sprintf $code{init}, $field, $args->{-init}, $field;
62             }
63             }
64 6 100       15 $code .= sprintf $code{set_default}, $field, $default_string, $field
65             if defined $default;
66 6         16 $code .= sprintf $code{return_if_get}, $field;
67 6         12 $code .= sprintf $code{set}, $field;
68             $code .= sprintf $code{weaken}, $field, $field
69 6 50       12 if $args->{-weak};
70 6         10 $code .= sprintf $code{sub_end}, $field;
71              
72 6 50 66 1   589 my $sub = eval $code;
  1 50       28  
  1         9  
  1         1  
  1         3  
  1         3  
  1         5  
  1         6  
73 6 50       30 die $@ if $@;
74 4     4   26 no strict 'refs';
  4         7  
  4         136  
75 4     4   2026 use utf8;
  4         50  
  4         17  
76 6         13 my $method = "${package}::$field";
77 6         19 $method = Encode::decode_utf8($method);
78 6         176 *{$method} = $sub;
  6         30  
79 6 100       72 return $code if defined wantarray;
80             }
81              
82             sub default_as_code {
83 4     4   448 no warnings 'once';
  4         8  
  4         436  
84 4     4 0 975 require Data::Dumper;
85 4         9687 local $Data::Dumper::Sortkeys = 1;
86 4         12 my $code = Data::Dumper::Dumper(shift);
87 4         170 $code =~ s/^\$VAR1 = //;
88 4         12 $code =~ s/;$//;
89 4         10 return $code;
90             }
91              
92             sub const {
93 1     1 1 83 my $package = caller;
94 1         2 my ($args, @values) = do {
95 4     4   24 no warnings;
  4         5  
  4         342  
96 1     1   8 local *paired_arguments = sub { (qw(-package)) };
  1         2  
97 1         23 Class::Field->parse_arguments(@_);
98             };
99 1         4 my ($field, $default) = @values;
100 1 50       4 $package = $args->{-package} if defined $args->{-package};
101 4     4   25 no strict 'refs';
  4         7  
  4         1097  
102 1 50       2 return if defined &{"${package}::$field"};
  1         19  
103 1     3   7 *{"${package}::$field"} = sub { $default }
  3         797  
104 1         6 }
105              
106             sub parse_arguments {
107 7     7 0 12 my $class = shift;
108 7         16 my ($args, @values) = ({}, ());
109 7         18 my %booleans = map { ($_, 1) } $class->boolean_arguments;
  6         19  
110 7         18 my %pairs = map { ($_, 1) } $class->paired_arguments;
  13         28  
111 7         21 while (@_) {
112 12         16 my $elem = shift;
113 12 50 33     78 if (defined $elem and defined $booleans{$elem}) {
    100 66        
      66        
114 0 0 0     0 $args->{$elem} = (@_ and $_[0] =~ /^[01]$/)
115             ? shift
116             : 1;
117             }
118             elsif (defined $elem and defined $pairs{$elem} and @_) {
119 2         7 $args->{$elem} = shift;
120             }
121             else {
122 10         25 push @values, $elem;
123             }
124             }
125 7 50       45 return wantarray ? ($args, @values) : $args;
126             }
127              
128 1     1 0 3 sub boolean_arguments { () }
129 0     0 0 0 sub paired_arguments { () }
130              
131             1;