File Coverage

blib/lib/MooseX/ConfigCascade/Util.pm
Criterion Covered Total %
statement 38 38 100.0
branch 9 10 90.0
condition 18 21 85.7
subroutine 7 7 100.0
pod n/a
total 72 76 94.7


line stmt bran cond sub pod time code
1             package MooseX::ConfigCascade::Util;
2              
3 6     6   36 use Moose;
  6         12  
  6         27  
4 6     6   33704 use MooseX::ClassAttribute;
  6         422889  
  6         26  
5 6     6   1509800 use Carp;
  6         18  
  6         479  
6 6     6   41 use Module::Runtime qw(require_module);
  6         11  
  6         44  
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 302     302   501 my $self = shift;
42              
43 302         7577 $self->_set_atts( $self->conf );
44 86         23481 while( my $conf_h = pop @{$self->_stack} ){
  96         5053  
45 10         21 $self->_set_atts( $conf_h );
46             }
47             }
48              
49              
50             sub _get_att_list{
51 312     312   625 my ($self,$conf_h) = @_;
52              
53 312         617 my $att_list = [];
54              
55 312 100 100     7659 if ( ! $self->_att_name && $conf_h->{ref($self->_to_set)} ){
    100 100        
56 289         6891 push @$att_list, $conf_h->{ref($self->_to_set)};
57             } elsif ( $self->_att_name && $conf_h->{ $self->_att_name } ){
58 10         241 push @$att_list, $conf_h->{ $self->_att_name };
59             }
60 312         686 return $att_list;
61             }
62              
63            
64             sub _set_atts{
65 312     312   631 my ($self, $conf_h ) = @_;
66              
67 312         7603 my $to_set = $self->_to_set;
68 312         747 my $att_list = $self->_get_att_list( $conf_h );
69            
70 312         707 foreach my $att_set (@$att_list){
71 299         1626 foreach my $att_name (keys %$att_set){
72 4059 100 66     1359459 if ($to_set->can( $att_name ) && ! defined $self->_args->{ $att_name }){
73            
74 4051         11572 my $att = $to_set->meta->find_attribute_by_name($att_name);
75 4051         291756 my $tc = $att->type_constraint;
76            
77 4051 100 100     28860 if ($tc->is_a_type_of('Str') ||
    50 100        
      100        
      33        
78             $tc->is_a_type_of('HashRef') ||
79             $tc->is_a_type_of('ArrayRef') ||
80             $tc->is_a_type_of('Bool')){
81              
82 4041         2107168 $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 10         2107 my $util = $to_set->$att_name->cascade_util;
90 10         244 $util->_att_name( $att_name );
91 10         15 unshift @{$util->_stack}, $att_set;
  10         239  
92 10         27 $util->_parse_atts;
93             }
94             }
95             }
96             }
97             }
98              
99             1;
100             __END__
101             =head1 NAME
102              
103             MooseX::ConfigCascade::Util - utility module for L<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 L<MooseX::ConfigCascade>. See the L<MooseX::ConfigCascade> documentation for a general overview of how to implement L<MooseX::ConfigCascade> in your project.
133              
134             =head1 METHODS
135              
136             MooseX provides an attribute C<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 L<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