File Coverage

blib/lib/Config/Entities.pm
Criterion Covered Total %
statement 111 115 96.5
branch 46 58 79.3
condition 15 21 71.4
subroutine 18 18 100.0
pod 4 4 100.0
total 194 216 89.8


line stmt bran cond sub pod time code
1 1     1   13153 use strict;
  1         1  
  1         23  
2 1     1   3 use warnings;
  1         1  
  1         41  
3              
4             package Config::Entities;
5             $Config::Entities::VERSION = '1.05';
6             # ABSTRACT: An multi-level overridable perl based configuration module
7             # PODNAME: Config::Entities
8              
9 1     1   3 use Cwd qw(abs_path);
  1         1  
  1         37  
10 1     1   602 use Data::Dumper;
  1         6352  
  1         48  
11 1     1   5 use File::Find;
  1         1  
  1         43  
12 1     1   4 use File::Spec;
  1         1  
  1         12  
13 1     1   392 use Log::Any;
  1         3288  
  1         3  
14              
15             my $logger = Log::Any->get_logger();
16              
17             sub new {
18 11     11 1 4618 my ( $class, @args ) = @_;
19 11         27 return bless( {}, $class )->_init(@args);
20             }
21              
22             sub as_hashref {
23 1     1 1 4 return _copy(@_);
24             }
25              
26             sub _copy {
27 10     10   7 my ($value) = @_;
28              
29 10         9 my $ref = ref($value);
30 10 100       11 if ($ref) {
31 4 50 66     17 if ( $ref eq 'ARRAY' ) {
    50          
    0          
32 0         0 return [ map { _copy($_) } @$value ];
  0         0  
33             }
34             elsif ( $ref eq 'HASH' || $value->isa('Config::Entities') ) {
35 4         6 return { map { $_ => _copy( $value->{$_} ) } keys(%$value) };
  9         13  
36             }
37             elsif ( $ref eq 'SCALAR' ) {
38 0         0 return $value;
39             }
40             else {
41 0         0 croak("unsupported type '$ref'");
42             }
43             }
44             else {
45 6         13 return $value;
46             }
47             }
48              
49             sub _add_properties {
50 13     13   52 my ( $self, $properties, $more_properties ) = @_;
51              
52 13         13 foreach my $key ( keys( %{$more_properties} ) ) {
  13         23  
53 20         47 $properties->{$key} = $more_properties->{$key};
54             }
55             }
56              
57             sub fill {
58 5     5 1 25 my ( $self, $coordinate, $hashref, %options ) = @_;
59              
60 5         10 my @entity = $self->get_entity( $coordinate, %options );
61 5         9 foreach my $key ( keys(%$hashref) ) {
62 11 100 66     43 if ( ref( $entity[0] ) eq 'HASH' && exists( $entity[0]->{$key} ) ) {
    100 66        
    50          
63 5         6 $hashref->{$key} = $entity[0]->{$key};
64             }
65             elsif ( $hashref->{$key} && $hashref->{$key} eq 'Config::Entities::entity' ) {
66 1         2 $hashref->{$key} = $entity[0];
67             }
68             elsif ( $options{ancestry} ) {
69 5         9 for ( my $index = 1; $index < scalar(@entity); $index++ ) {
70 14 100       25 if ( defined( $entity[$index]->{$key} ) ) {
71 5         4 $hashref->{$key} = $entity[$index]->{$key};
72 5         7 last;
73             }
74             }
75             }
76             }
77              
78 5         18 return $hashref;
79             }
80              
81             sub get_entity {
82 7     7 1 20 my ( $self, $coordinate, %options ) = @_;
83              
84 7         9 my @result = ($self);
85 7 50       23 if ($coordinate) {
86 7         18 foreach my $coordinate_part ( split( /\./, $coordinate ) ) {
87 22         19 my $child = $result[0]->{$coordinate_part};
88 22 50       28 return if ( !$child );
89 22         25 unshift( @result, $child );
90             }
91             }
92 7 100       32 return $options{ancestry} ? @result : shift(@result);
93             }
94              
95             sub _init {
96 11     11   13 my ( $self, @args ) = @_;
97              
98             # if last arg is a hash, it is an options hash
99 11 100       27 my $options =
100             ref( $args[$#args] ) eq 'HASH'
101             ? pop(@args)
102             : {};
103              
104             # all other args are entities roots
105 11         13 my @entities_roots = @args;
106              
107 11         10 my $properties = {};
108 11 100       21 if ( $options->{properties_file} ) {
109              
110             # merge in properties from files
111             my @properties_files =
112             ref( $options->{properties_file} ) eq 'ARRAY'
113 1         2 ? @{ $options->{properties_file} }
114 6 100       12 : ( $options->{properties_file} );
115              
116 6         8 foreach my $properties_file (@properties_files) {
117 7         664 $self->_add_properties( $properties, do($properties_file) );
118             }
119             }
120 11 100       20 if ( $options->{properties} ) {
121              
122             # merge in direct properties
123 6         11 $self->_add_properties( $properties, $options->{properties} );
124             }
125              
126 11 100       22 if ( $options->{entity} ) {
127 2         1 foreach my $key ( keys( %{ $options->{entity} } ) ) {
  2         7  
128 4         5 _merge( $self, $key, $options->{entity}{$key} );
129             }
130             }
131              
132 11 100       20 if ( scalar(@entities_roots) ) {
133             find(
134             sub {
135 52 100 66 52   1161 if ( $_ =~ /^(.*)\.pmc?$/ && -f $File::Find::name ) {
136 32         54 my $key = $1;
137              
138 32         23 my $hashref = $self;
139 32         178 my @directories = File::Spec->splitdir(
140             substr( $File::Find::dir, length($File::Find::topdir) ) );
141 32 100       67 if ( scalar(@directories) ) {
142 16         38 shift(@directories) while ( !$directories[0] );
143 16         17 foreach my $dir (@directories) {
144 24 100       37 if ( !defined( $hashref->{$dir} ) ) {
145 4         7 $hashref->{$dir} = {};
146             }
147 24         25 $hashref = $hashref->{$dir};
148             }
149             }
150              
151 32         22 my $entity;
152             {
153             # export %properties to the entity file
154 32         22 local $Config::Entities::properties = $properties;
  32         29  
155             ## no critic (ProhibitNoStrict)
156 1     1   602 no strict 'vars';
  1         1  
  1         291  
157 32 50       99 local %properties = $properties ? %$properties : ();
158 32         2798 $entity = do($File::Find::name);
159             ## use critic
160             }
161 32 50       310 $logger->warn( 'unable to compile ', $File::Find::name, ': ', $@, "\n" )
162             if ($@);
163 32         50 _merge( $hashref, $key, $entity );
164             }
165             },
166 8         30 map { Cwd::abs_path($_) } @entities_roots
  12         770  
167             );
168             }
169              
170 11         60 _inherit( undef, $self );
171              
172 11         50 return $self;
173             }
174              
175             sub _inherit {
176 116     116   87 my ( $parent, $child ) = @_;
177 116 100 100     428 if ( $child && ( ref($child) eq 'HASH' || ref($child) eq 'Config::Entities' ) ) {
      66        
178 47 100 66     84 if ( $parent && $child->{'Config::Entities::inherit'} ) {
179 4         5 my $inherit = delete( $child->{'Config::Entities::inherit'} );
180 4 50       7 if ($inherit) {
181 4         5 foreach my $key (@$inherit) {
182 4 50       10 if ( defined( $parent->{$key} ) ) {
183 4 50       11 $child->{$key} = $parent->{$key} unless ( defined( $child->{$key} ) );
184             }
185             }
186             }
187             }
188 47         115 _inherit( $child, $child->{$_} ) foreach keys(%$child);
189             }
190             }
191              
192             sub _merge {
193 109     109   105 my ( $hashref, $key, $value ) = @_;
194              
195 109 100       140 if ( ref($value) eq 'HASH' ) {
196              
197             # transfer key/value pairs from hashref
198             # will merge rather than replace...
199 36 100       58 if ( !defined( $hashref->{$key} ) ) {
200 32         36 $hashref->{$key} = {};
201             }
202 36         34 $hashref = $hashref->{$key};
203              
204 36         87 while ( my ( $sub_key, $sub_value ) = each(%$value) ) {
205 73         71 _merge( $hashref, $sub_key, $sub_value );
206             }
207             }
208             else {
209             # anything not a hashref will replace
210 73         804 $hashref->{$key} = $value;
211             }
212             }
213              
214             1;
215              
216             __END__