File Coverage

blib/lib/Clustericious/Config.pm
Criterion Covered Total %
statement 154 174 88.5
branch 59 74 79.7
condition 20 31 64.5
subroutine 27 32 84.3
pod 3 5 60.0
total 263 316 83.2


line stmt bran cond sub pod time code
1             package Clustericious::Config;
2              
3 20     20   127414 use strict;
  20         30  
  20         933  
4 18     18   86 use warnings;
  18         27  
  18         611  
5 17     17   143 use v5.10;
  17         37  
  17         902  
6              
7             # ABSTRACT: Configuration files for Clustericious nodes.
8             our $VERSION = '0.30'; # VERSION
9              
10              
11 16     16   5348 use Clustericious::Config::Password;
  16         35  
  16         532  
12 16     16   84 use List::Util;
  16         23  
  16         984  
13 15     15   10817 use JSON::XS;
  15         73305  
  15         898  
14 15     15   2747 use YAML::XS ();
  15         12730  
  15         301  
15 15     15   7379 use Mojo::Template;
  15         692721  
  15         147  
16 15     15   13376 use Log::Log4perl qw/:easy/;
  15         550396  
  15         92  
17 15     15   16244 use Storable;
  15         38725  
  15         881  
18 15     15   5970 use Clustericious::Config::Helpers ();
  15         33  
  15         278  
19 15     15   65 use Data::Dumper;
  15         22  
  15         552  
20 15     15   58 use Cwd ();
  15         20  
  15         166  
21 15     15   8432 use Module::Build;
  15         1078844  
  15         445  
22 15     15   2394 use File::HomeDir ();
  15         22987  
  15         17071  
23              
24             our %Singletons;
25              
26             sub _is_subdir {
27 0     0   0 my ($child,$parent) = @_;
28 0         0 my $p = Cwd::abs_path($parent);
29 0         0 my $c = Cwd::abs_path($child);
30 0 0       0 return ($c =~ m[^\Q$p\E]) ? 1 : 0;
31             }
32              
33             my $is_test = 0;
34             sub _testing {
35 52     52   96 my($class, $new) = @_;
36 52 100       163 $is_test = $new if defined $new;
37 52         4865 $is_test;
38             }
39              
40             our $class_suffix = {};
41             sub _uncache {
42 10     10   24 my($class, $name) = @_;
43 10         25 delete $Clustericious::Config::Singletons{$name};
44 10   100     66 $class_suffix->{$name} //= 1;
45 10         32 $class_suffix->{$name}++;
46             }
47              
48 19     19 0 26 sub pre_rendered { }
49 19     19 0 30 sub rendered { }
50              
51              
52             sub new {
53 25     25 1 3110 my $class = shift;
54 25 100       123 my %t_args = (ref $_[-1] eq 'ARRAY' ? @{( pop )} : () );
  3         6  
55 25         47 my $arg = $_[0];
56 25 50       74 ($arg = caller) =~ s/:.*$// unless $arg; # Determine from caller's class
57 25 50       77 return $Singletons{$arg} if exists($Singletons{$arg});
58              
59            
60 25         43 my $we_are_testing_this_module = 0;
61 25 100       83 if(__PACKAGE__->_testing) {
62 9         17 $we_are_testing_this_module = 0;
63             }
64              
65 25         82 my $conf_data;
66              
67 25         229 my $json = JSON::XS->new;
68            
69 25         48 state $package_counter = 0;
70 25         65 my $namespace = "Clustericious::Config::TemplatePackage::Package$package_counter";
71 13     13   89 eval qq{ package $namespace; use Clustericious::Config::Helpers; };
  13         18  
  13         1171  
  25         2223  
72 25 50       92 die $@ if $@;
73 25         41 $package_counter++;
74            
75 25         354 my $mt = Mojo::Template->new(namespace => $namespace)->auto_escape(0);
76 25         1513 $mt->prepend( join "\n", map " my \$$_ = q{$t_args{$_}};", sort keys %t_args );
77              
78 25         215 my $filename;
79 25 100 0     150 if (ref $arg eq 'SCALAR') {
    100 33        
    50          
80 4         16 $class->pre_rendered( $$arg );
81 4         23 my $rendered = $mt->render($$arg);
82 4         5136 $class->rendered( SCALAR => $rendered );
83 4 50       17 die $rendered if ( (ref($rendered)) =~ /Exception/ );
84 4 100       20 my $type = $rendered =~ /^---/ ? 'yaml' : 'json';
85             $conf_data = $type eq 'yaml' ?
86 1         164 eval { YAML::XS::Load( $rendered ); }
87 4 100       13 : eval { $json->decode( $rendered ); };
  3         33  
88 4 50       17 LOGDIE "Could not parse $type \n-------\n$rendered\n---------\n$@\n" if $@;
89             } elsif (ref $arg eq 'HASH') {
90 4         267 $conf_data = Storable::dclone $arg;
91             } elsif (
92             $we_are_testing_this_module
93             && !(
94             $ENV{CLUSTERICIOUS_CONF_DIR}
95             && _is_subdir( $ENV{CLUSTERICIOUS_CONF_DIR}, Cwd::getcwd() )
96             )) {
97 0         0 $conf_data = {};
98             } else {
99 17         20 my @conf_dirs;
100              
101 17 100       72 @conf_dirs = $ENV{CLUSTERICIOUS_CONF_DIR} if defined( $ENV{CLUSTERICIOUS_CONF_DIR} );
102              
103 17 100 66     92 push @conf_dirs, ( File::HomeDir->my_home . "/etc", "/util/etc", "/etc" ) unless $we_are_testing_this_module || __PACKAGE__->_testing;
104 17         233 my $conf_file = "$arg.conf";
105 17         45 $conf_file =~ s/::/-/g;
106 17     23   161 my ($dir) = List::Util::first { -e "$_/$conf_file" } @conf_dirs;
  23         317  
107 17 100       74 if ($dir) {
108 15         96 TRACE "reading from config file $dir/$conf_file";
109 15         193 $filename = "$dir/$conf_file";
110 15         57 $class->pre_rendered( $filename );
111 15         69 my $rendered = $mt->render_file($filename);
112 15         217 $class->rendered( $filename => $rendered );
113 15 50       49 die $rendered if ( (ref $rendered) =~ /Exception/ );
114 15 100       70 my $type = $rendered =~ /^---/ ? 'yaml' : 'json';
115 15 50       57 if ($ENV{CL_CONF_TRACE}) {
116 0         0 warn "configuration ($type) : \n";
117 0         0 warn $rendered;
118             }
119             $conf_data =
120             $type eq 'yaml'
121 13         793 ? eval { YAML::XS::Load($rendered) }
122 15 100       123 : eval { $json->decode($rendered) };
  2         20  
123 15 50       96 LOGDIE "Could not parse $type\n-------\n$rendered\n---------\n$@\n" if $@;
124             } else {
125 2 50       17 TRACE "could not find $conf_file file in: @conf_dirs" unless $dir;
126 2         18 $conf_data = {};
127             }
128             }
129 25   100     79 $conf_data ||= {};
130 25         155 Clustericious::Config::Helpers->_do_merges($conf_data);
131 25         9754 _add_heuristics($filename,$conf_data);
132             # Use derived classes so that AUTOLOADING keeps namespaces separate
133             # for various apps.
134 25 100       69 if ($class eq __PACKAGE__) {
135 22 100       58 if (ref $arg) {
136 5         10 $arg = "$arg";
137 5         19 $arg =~ tr/a-zA-Z0-9//cd;
138             }
139 22         68 $class = join '::', $class, 'App', $arg;
140 22 100       88 $class .= $class_suffix->{$arg} if $class_suffix->{$arg};
141 22         81 my $dome = '@'."$class"."::ISA = ('".__PACKAGE__. "')";
142 22         1406 eval $dome;
143 22 50       107 die "error setting ISA : $@" if $@;
144             }
145 25         389 bless $conf_data, $class;
146             }
147              
148             sub _add_heuristics {
149 25     25   42 my $filename = shift;
150             # Account for some mojo api changes
151 25         29 my $conf_data = shift;
152 25 50 33     110 if ($conf_data->{hypnotoad} && !ref($conf_data->{hypnotoad}{listen})) {
153 0         0 warn "# hypnotoad->listen should be an arrayref in $filename\n";
154 0         0 $conf_data->{hypnotoad}{listen} = [ $conf_data->{hypnotoad}{listen} ];
155             }
156              
157              
158             }
159              
160              
161             sub dump_as_yaml {
162 0     0 1 0 my $c = shift;
163 0         0 return YAML::XS::Dump($c);
164             }
165              
166             sub _stringify {
167 0     0   0 my $self = shift;
168 0         0 return join ' ', map { ($_, $self->{$_}) } sort keys %$self;
  0         0  
169             }
170              
171 0     0   0 sub DESTROY {
172             }
173              
174             sub AUTOLOAD {
175 52     52   9298 my $self = shift;
176 52         104 my %args = @_;
177 52         78 my $default = $args{default};
178 52         76 my $default_exists = exists $args{default};
179 52         52 our $AUTOLOAD;
180 52         60 my $called = $AUTOLOAD;
181 52         291 $called =~ s/.*:://g;
182 52 100 66     188 if ($default_exists && !exists($self->{$called})) {
183 4         10 $self->{$called} = $args{default};
184             }
185 52 100 66     375 Carp::cluck "config element '$called' not found for ".(ref $self)." (".(join ',',keys(%$self)).")"
186             if $called =~ /^_/ || !exists($self->{$called});
187 52         88 my $value = $self->{$called};
188 52         79 my $obj;
189 52         75 my $invocant = ref $self;
190 52 100       122 if (ref $value eq 'HASH') {
191 3         25 $obj = $invocant->new($value);
192             }
193 15     15   89 no strict 'refs';
  15         22  
  15         2138  
194 52         250 *{ $invocant . "::$called" } = sub {
195 82     82   96 my $self = shift;
196 82 100 100     219 $self->{$called} = $default if $default_exists && !exists($self->{$called});
197 82 100       579 die "'$called' not found in ".join ',',keys(%$self)
198             unless exists($self->{$called});
199 79         110 my $value = $self->{$called};
200 79 50 100     703 return wantarray && (ref $value eq 'HASH' ) ? %$value
    100 66        
    100          
    100          
201             : wantarray && (ref $value eq 'ARRAY') ? @$value
202             : defined($obj) ? $obj
203             : Clustericious::Config::Password->is_sentinel($value) ? Clustericious::Config::Password->get
204             : $value;
205 52         244 };
206 15     15   66 use strict 'refs';
  15         20  
  15         1266  
207 52         133 $self->$called;
208             }
209              
210              
211             sub set_singleton {
212 0     0 1   my $class = shift;
213 0           my $app = shift;
214 0           my $obj = shift;
215 0           our %Singletons;
216 0           $Singletons{$app} = $obj;
217             }
218              
219              
220             1;
221              
222              
223             __END__