File Coverage

blib/lib/MooseX/ConfigCascade/Util.pm
Criterion Covered Total %
statement 31 38 81.5
branch 6 10 60.0
condition 13 21 61.9
subroutine 7 7 100.0
pod n/a
total 57 76 75.0


line stmt bran cond sub pod time code
1             package MooseX::ConfigCascade::Util;
2              
3 6     6   45 use Moose;
  6         16  
  6         46  
4 6     6   58337 use MooseX::ClassAttribute;
  6         635454  
  6         36  
5 6     6   2169527 use Carp;
  6         21  
  6         549  
6 6     6   52 use Module::Runtime qw(require_module);
  6         15  
  6         60  
7              
8             class_has conf => (is => 'rw', isa => 'HashRef', lazy => 1, default => sub{
9             $_[0]->parser->($_[0]->path);
10             });
11              
12             class_has path => (is => 'rw', isa => 'Str', trigger => sub{
13             $_[0]->conf( $_[0]->parser->($_[0]->path) )
14             });
15              
16             class_has parser => (is => 'rw', isa => 'CodeRef', lazy => 1, default => sub{sub{
17             return {} unless $_[0];
18             open my $fh,'<',$_[0] or confess "Could not open file ".$_[0].": $!";
19             my $file_text = '';
20             while( my $row = <$fh> ){ $file_text.=$row }
21             if ( $file_text =~ /^\s*\{/s ){
22             require_module('JSON');
23             return JSON::decode_json( $file_text )
24             } elsif ( $file_text =~ /^\s*\-/s ){
25             require_module('YAML');
26             return YAML::Load($file_text);
27             }
28             confess "Error reading $_: Could not understand file format";
29             }});
30              
31              
32             has _stack => (is => 'rw', isa => 'ArrayRef[HashRef]', default => sub{[]});
33             has _to_set => (is => 'ro', isa => 'Object'); # $self for the object that has the MooseX::ConfigCascade role
34             has _role_name => (is => 'ro', isa => 'Str'); # 'MooseX::ConfigCascade' unless the name changes
35             has _att_name => (is => 'rw', isa => 'Str'); # the name of the attribute in the parent that has this object
36             has _args => (is => 'rw', isa => 'HashRef', default => sub{{}}); #original arguments passed to the constructor
37              
38              
39              
40             sub _parse_atts{
41 276     276   719 my $self = shift;
42              
43 276         9135 $self->_set_atts( $self->conf );
44 60         24386 while( my $conf_h = pop @{$self->_stack} ){
  60         2125  
45 0         0 $self->_set_atts( $conf_h );
46             }
47             }
48              
49              
50             sub _get_att_list{
51 276     276   911 my ($self,$conf_h) = @_;
52              
53 276         734 my $att_list = [];
54              
55 276 100 66     8807 if ( ! $self->_att_name && $conf_h->{ref($self->_to_set)} ){
    50 33        
56 275         7934 push @$att_list, $conf_h->{ref($self->_to_set)};
57             } elsif ( $self->_att_name && $conf_h->{ $self->_att_name } ){
58 0         0 push @$att_list, $conf_h->{ $self->_att_name };
59             }
60 276         799 return $att_list;
61             }
62              
63            
64             sub _set_atts{
65 276     276   799 my ($self, $conf_h ) = @_;
66              
67 276         8813 my $to_set = $self->_to_set;
68 276         1030 my $att_list = $self->_get_att_list( $conf_h );
69            
70 276         854 foreach my $att_set (@$att_list){
71 275         2894 foreach my $att_name (keys %$att_set){
72 3907 100 66     1633454 if ($to_set->can( $att_name ) && ! defined $self->_args->{ $att_name }){
73            
74 3899         15865 my $att = $to_set->meta->find_attribute_by_name($att_name);
75 3899         349088 my $tc = $att->type_constraint;
76            
77 3899 50 100     35822 if ($tc->is_a_type_of('Str') ||
    0 100        
      66        
      0        
78             $tc->is_a_type_of('HashRef') ||
79             $tc->is_a_type_of('ArrayRef') ||
80             $tc->is_a_type_of('Bool')){
81              
82 3899         2521468 $att->set_value($to_set,$att_set->{$att_name});
83              
84             } elsif (
85             $tc->is_a_type_of('Object')
86             && $to_set->$att_name->DOES( $self->_role_name )
87             ){
88            
89 0           my $util = $to_set->$att_name->cascade_util;
90 0           $util->_att_name( $att_name );
91 0           unshift @{$util->_stack}, $att_set;
  0            
92 0           $util->_parse_atts;
93             }
94             }
95             }
96             }
97             }
98              
99             1;
100             __END__
101             =head1 NAME
102              
103             MooseX::ConfigCascade::Util - utility module for MooseX::ConfigCascade
104              
105             =head1 SYNOPSIS
106              
107             use MooseX::ConfigCascade::Util;
108              
109             MooseX::ConfigCascade::Util->path( # set the path to the config file
110              
111             '/path/to/config.json'
112              
113             );
114              
115              
116             MooseX::ConfigCascade::Util->conf( # set the config hash directly
117              
118             \%conf
119              
120             );
121              
122              
123             MooseX::ConfigCascade::Util->parser( # set the sub that parses the
124             # config file
125             $subroutine_reference
126              
127             );
128              
129              
130             =head1 DESCRIPTION
131              
132             This is module is the workhorse of MooseX::ConfigCascade. See the L<MooseX::ConfigCascade> documentation for a general overview of how to implement MooseX::ConfigCascade in your project.
133              
134             =head1 METHODS
135              
136             MooseX provides an attribute 'conf' which stores a hash of config directives, and 2 attributes L<path> and L<parser> which control how L<conf> is loaded
137              
138             =head2 conf
139              
140             This is a hashref containing config information. See the documentation for L<MooseX::ConfigCascade> to learn how this should be structured. It can be set directly
141              
142             MooseX::ConfigCascade::Util->conf( \%conf );
143              
144             Alternatively it is set indirectly when 'path' is changed
145              
146             =head2 path
147              
148             Call this to set the path to your config file. For more information about the format of your config file, see the documentation for L<MooseX::ConfigCascade>.
149              
150             MooseX::ConfigCascade::Util->path( '/path/to/my_config.json' );
151              
152             When L<path> is changed it reads the specified file and overwrites L<conf> with the new values. Any new objects created after that will get the new values.
153              
154             =head2 parser
155              
156             This is the subroutine responsible for converting the file specified in path to a hashref. Setting this to a new value means you can use MooseX::ConfigCascade with a config file of arbitrary format. But look at the expected format of this sub below, and use with caution:
157              
158             Your parser subroutine should collect L<path> from the input arguments, do whatever is necessary to convert the file, and finally output the hashref which will be stored in L<conf>':
159              
160             my $parser = sub {
161             my $path = shift;
162              
163             open my $fh, '<', $path or die "Could not open $path: $!";
164              
165              
166             my %conf;
167              
168             # .... read the values into %conf
169              
170              
171             return \%conf;
172             }
173              
174             =head1 SEE ALSO
175              
176             L<MooseX::ConfigCascade::Util>
177             L<Moose>
178             L<MooseX::ClassAttribute>
179              
180             =head1 AUTHOR
181              
182             Tom Gracey E<lt>tomgracey@gmail.comE<gt>
183              
184             =head1 COPYRIGHT AND LICENSE
185              
186             Copyright (C) 2017 by Tom Gracey
187              
188             This library is free software; you can redistribute it and/or modify
189             it under the same terms as Perl itself.
190              
191             =cut
192              
193              
194            
195              
196              
197              
198              
199              
200              
201              
202              
203              
204              
205              
206              
207              
208              
209              
210              
211              
212              
213              
214              
215              
216              
217