File Coverage

blib/lib/Class/Field.pm
Criterion Covered Total %
statement 98 100 98.0
branch 25 40 62.5
condition 13 24 54.1
subroutine 20 21 95.2
pod 2 6 33.3
total 158 191 82.7


line stmt bran cond sub pod time code
1 4     4   81032 use strict; use warnings;
  4     4   9  
  4         164  
  4         22  
  4         7  
  4         221  
2             package Class::Field;
3             our $VERSION = '0.23';
4              
5 4     4   19 use base 'Exporter';
  4         17  
  4         423  
6              
7             our @EXPORT_OK = qw(field const);
8              
9 4     4   11412 use Encode;
  4         85804  
  4         1156  
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 40 my $package = caller;
37 6         10 my ($args, @values) = do {
38 4     4   39 no warnings;
  4         8  
  4         1706  
39 6     6   33 local *boolean_arguments = sub { (qw(-weak)) };
  6         13  
40 6     6   27 local *paired_arguments = sub { (qw(-package -init)) };
  6         18  
41 6         33 Class::Field->parse_arguments(@_);
42             };
43 6         14 my ($field, $default) = @values;
44 6 50       19 $package = $args->{-package} if defined $args->{-package};
45 6 50 66     22 die "Cannot have a default for a weakened field ($field)"
46             if defined $default && $args->{-weak};
47 6 50       7 return if defined &{"${package}::$field"};
  6         41  
48 6 50       19 require Scalar::Util if $args->{-weak};
49 6 100 66     61 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         33 my $code = sprintf $code{sub_start}, $package, $field;
57 6 100       23 if ($args->{-init}) {
58 2 50       11 my $fragment = $args->{-weak} ? $code{weak_init} : $code{init};
59 2         13 $code .= sprintf $fragment, $field, $args->{-init}, ($field) x 4;
60             }
61 6 100       19 $code .= sprintf $code{set_default}, $field, $default_string, $field
62             if defined $default;
63 6         18 $code .= sprintf $code{return_if_get}, $field;
64 6         17 $code .= sprintf $code{set}, $field;
65 6 50       19 $code .= sprintf $code{weaken}, $field, $field
66             if $args->{-weak};
67 6         16 $code .= sprintf $code{sub_end}, $field;
68              
69 6 50 66 2   1463 my $sub = eval $code;
  2 50       1682  
  2         29  
  1         2  
  1         4  
  1         3  
  1         5  
  1         8  
70 6 50       18 die $@ if $@;
71 4     4   35 no strict 'refs';
  4         9  
  4         133  
72 4     4   4486 use utf8;
  4         47  
  4         24  
73 6         51 my $method = "${package}::$field";
74 6         21 $method = Encode::decode_utf8($method);
75 6         196 *{$method} = $sub;
  6         75  
76 6 100       144 return $code if defined wantarray;
77             }
78              
79             sub default_as_code {
80 4     4   829 no warnings 'once';
  4         10  
  4         627  
81 4     4 0 7901 require Data::Dumper;
82 4         45484 local $Data::Dumper::Sortkeys = 1;
83 4         44 my $code = Data::Dumper::Dumper(shift);
84 4         331 $code =~ s/^\$VAR1 = //;
85 4         13 $code =~ s/;$//;
86 4         12 return $code;
87             }
88              
89             sub const {
90 1     1 1 9 my $package = caller;
91 1         2 my ($args, @values) = do {
92 4     4   23 no warnings;
  4         8  
  4         616  
93 1     1   7 local *paired_arguments = sub { (qw(-package)) };
  1         3  
94 1         8 Class::Field->parse_arguments(@_);
95             };
96 1         4 my ($field, $default) = @values;
97 1 50       5 $package = $args->{-package} if defined $args->{-package};
98 4     4   52 no strict 'refs';
  4         9  
  4         1494  
99 1 50       2 return if defined &{"${package}::$field"};
  1         9  
100 1     3   7 *{"${package}::$field"} = sub { $default }
  3         637  
101 1         4 }
102              
103             sub parse_arguments {
104 7     7 0 15 my $class = shift;
105 7         17 my ($args, @values) = ({}, ());
106 7         24 my %booleans = map { ($_, 1) } $class->boolean_arguments;
  6         30  
107 7         29 my %pairs = map { ($_, 1) } $class->paired_arguments;
  13         33  
108 7         23 while (@_) {
109 12         20 my $elem = shift;
110 12 50 33     131 if (defined $elem and defined $booleans{$elem}) {
    100 66        
      66        
111 0 0 0     0 $args->{$elem} = (@_ and $_[0] =~ /^[01]$/)
112             ? shift
113             : 1;
114             }
115             elsif (defined $elem and defined $pairs{$elem} and @_) {
116 2         10 $args->{$elem} = shift;
117             }
118             else {
119 10         77 push @values, $elem;
120             }
121             }
122 7 50       64 return wantarray ? ($args, @values) : $args;
123             }
124              
125 1     1 0 3 sub boolean_arguments { () }
126 0     0 0 0 sub paired_arguments { () }
127              
128             1;