File Coverage

blib/lib/Config/Properties/Simple.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Config::Properties::Simple;
2              
3 1     1   35470 use 5.006;
  1         4  
  1         64  
4              
5             our $VERSION = '0.14';
6              
7 1     1   8 use strict;
  1         2  
  1         37  
8 1     1   5 use warnings;
  1         6  
  1         49  
9 1     1   6 use Carp;
  1         2  
  1         109  
10              
11 1     1   1465 use Config::Properties;
  1         20310  
  1         48  
12 1     1   477 use Config::Find;
  0            
  0            
13              
14             our @ISA=qw(Config::Properties Config::Find);
15              
16             sub new {
17             my ($class, %opts)=@_;
18              
19             my $defaults;
20             if (defined $opts{defaults}) {
21             if (UNIVERSAL::isa($opts{defaults}, 'Config::Properties')) {
22             $defaults=$opts{defaults}
23             }
24             else {
25             $defaults=Config::Properties->new();
26             for my $k (keys %{$opts{defaults}}) {
27             $defaults->setProperty($k, $opts{defaults}->{$k})
28             }
29             }
30             }
31              
32             my $this=$class->SUPER::new($defaults);
33              
34             $this->{simple_opts}=\%opts;
35              
36             exists $opts{format}
37             and $this->setFormat($opts{format});
38              
39             unless ($opts{noread}
40             or (exists $opts{mode} and $opts{mode}=~/^w(?:rite)?/i)) {
41             my $fn=$this->{simple_fn}=$this->find(%opts);
42             unless (defined $fn) {
43             return $this if ($opts{optional} and (!defined $opts{file} or $opts{optional} > 1));
44             croak 'configuration file not found';
45             }
46             my $fh=IO::File->new($fn, "r");
47             binmode $fh, ':utf8' if $this->{simple_opts}{utf8};
48             unless ($fh) {
49             return $this if ($opts{optional} and !defined $opts{file})
50             or croak 'unable to open configuration file for reading';
51             }
52             $this->load($fh);
53             close $fh
54             or croak 'unable to read configuration file';
55              
56             my $required=$opts{required};
57             if (defined $required) {
58             UNIVERSAL::isa($required, 'ARRAY')
59             or croak "invalid object passed for 'required' option, array reference expected";
60             foreach my $req (@{$required}) {
61             die "required property '$req' not found in $fn"
62             unless defined $this->getProperty($req);
63             }
64             }
65             }
66              
67             return $this;
68             }
69              
70             sub find {
71             my $this=shift;
72             return $this->SUPER::find(%{$this->{simple_opts}}, @_)
73             }
74              
75             sub file_name { shift->{simple_fn} }
76              
77             sub save {
78             my $this=shift;
79             my %opts= (%{$this->{simple_opts}}, mode => 'w', @_);
80             my $fh=$this->open(%opts)
81             or croak 'unable to open configuration file for writing';
82             binmode $fh, ':utf8' if $this->{simple_opts}{utf8};
83             my $header=$opts{header}
84             || 'Automatically generated configuration file';
85             $this->SUPER::save($fh, $header);
86             close $fh
87             or croak 'unable to write configuration file';
88             }
89              
90             sub fail {
91             my ($this, $error)=@_;
92             die "$error at ".$this->file_name." line ".$this->line_number."\n";
93             }
94              
95             sub validate {
96             my $this = shift;
97             my $okey = $_[0];
98             $this->validate_1(@_);
99             my $oln=$this->_property_line_number($_[0]);
100             if (defined $oln
101             and !$this->{simple_opts}{dups_ok}) {
102             $this->fail($okey eq $_[0]
103             ? "duplicated property '$okey' (previous appearance at line $oln)"
104             : "duplicated property '$okey' (resolves to '$_[0]', previous appearance at line $oln)" )
105             }
106             }
107              
108             sub validate_1 {
109             my $this = shift;
110             my $alias=$this->{simple_opts}{aliases};
111             if (defined $alias) {
112             $_[0]=$alias->{$_[0]} if exists $alias->{$_[0]};
113             }
114             my $vtor=$this->{simple_opts}{validate};
115             if (defined $vtor) {
116             my $fn=$this->{simple_fn};
117             if (UNIVERSAL::isa($vtor, 'CODE')) {
118             &$vtor(@_, $this) or $this->fail("invalid property '$_[0]' value '$_[1]'");
119             return;
120             }
121             if (UNIVERSAL::isa($vtor, 'ARRAY')) {
122             foreach my $vtor2 (@{$vtor}) {
123             if (UNIVERSAL::isa($vtor2, 'Regexp')) {
124             return if $_[0]=~$vtor2;
125             }
126             else {
127             return if $vtor2 eq $_[0];
128             }
129             }
130             $this->fail("unknown property '$_[0]' found");
131             }
132             if (UNIVERSAL::isa($vtor, 'HASH')) {
133             # warn "validate is hash";
134             my $vtor2;
135             if (exists $vtor->{$_[0]}) {
136             $vtor2=$vtor->{$_[0]}
137             }
138             elsif (exists $vtor->{__default}) {
139             $vtor2=$vtor->{__default}
140             }
141             else {
142             $this->fail("unknow property '$_[0]' found");
143             }
144             if (UNIVERSAL::isa($vtor2, 'CODE')) {
145             &$vtor2(@_, $this) or $this->fail("invalid property '$_[0]' value '$_[1]'");
146             return;
147             }
148             if (UNIVERSAL::isa($vtor2, 'Regexp')) {
149             return if $_[1]=~$vtor2;
150             $this->fail("property '$_[0]' value '$_[1]' not allowed: it doesn't match regexp '$vtor2'");
151             }
152             if (UNIVERSAL::isa($vtor2, 'ARRAY')) {
153             return if (grep { $_[1] eq $_} @{$vtor2});
154             $this->fail("property '$_[0]' value '$_[1]' is not allowed");
155             }
156             if (UNIVERSAL::isa($vtor2, 'HASH')) {
157             if (exists $vtor2->{$_[1]}) {
158             $_[1]=$vtor->{$_[1]};
159             return
160             }
161             $this->fail("property '$_[0]' value '$_[1]' is not allowed");
162             }
163             if ($vtor2=~/^s(?:tring)?$/i or $vtor2=~/^a(?:ny)?$/i) {
164             return;
165             }
166             if ($vtor2=~/^b(?:oolean)?$/i) {
167             if ( $_[1] eq '1' or
168             $_[1]=~/^y(?:es)?$/i or
169             $_[1]=~/^t(?:rue)?$/i) {
170             $_[1]=1;
171             return;
172             }
173             if ( $_[1] eq '' or
174             $_[1] eq '0' or
175             $_[1]=~/^no?$/i or
176             $_[1]=~/^f(?:alse)?$/i) {
177             $_[1]=0;
178             return;
179             }
180             $this->fail("property '$_[0]' value '$_[1]' is not allowed: boolean expected");
181             }
182             if ($vtor2=~/^u(?:nsigned)?$/i) {
183             if ($_[1]=~/^\d+$/) {
184             $_[1]=int $_[1];
185             return;
186             }
187             $this->fail("property '$_[0]' value '$_[1]' is not allowed: unsigned integer expected");
188             }
189             if ($vtor2=~/^i(?:nteger)?$/i) {
190             if ($_[1]=~/^[+\-]?\d+$/) {
191             $_[1]=int $_[1];
192             return;
193             }
194             $this->fail("property '$_[0]' value '$_[1]' is not allowed: integer expected");
195             }
196             if ($vtor2=~/^f(?:loat)?$/i or $vtor2=~/^n(?:umber)?$/i) {
197             if ($_[1]=~/^[+-]?(?:\d+|\d*\.\d+|\d+\.\d*)(?:[eE][+-]?\d+)?$/) {
198             $_[1]=$_[1]+0;
199             return;
200             }
201             $this->fail("property '$_[0]' value '$_[1]' is not allowed: number expected");
202             }
203              
204             croak "invalid object '$vtor2' for validate";
205             }
206             else {
207             croak "invalid object '$vtor' for validate";
208             }
209             }
210             }
211              
212             1;
213             __END__