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 19     19   203939 use strict;
  19         40  
  19         1003  
4 17     17   99 use warnings;
  17         32  
  17         662  
5 16     16   183 use v5.10;
  16         46  
  16         1133  
6              
7             # ABSTRACT: configuration files for Clustericious nodes.
8             our $VERSION = '0.28'; # VERSION
9              
10              
11 15     15   7646 use Clustericious::Config::Password;
  15         51  
  15         503  
12 15     15   94 use List::Util;
  15         31  
  15         1149  
13 14     14   16475 use JSON::XS;
  14         99888  
  14         969  
14 14     14   11732 use YAML::XS ();
  14         18126  
  14         318  
15 14     14   12654 use Mojo::Template;
  14         1057216  
  14         188  
16 14     14   32779 use Log::Log4perl qw/:easy/;
  14         831942  
  14         116  
17 14     14   25308 use Storable;
  14         53135  
  14         937  
18 14     14   9478 use Clustericious::Config::Helpers ();
  14         43  
  14         365  
19 14     14   88 use Data::Dumper;
  14         24  
  14         598  
20 14     14   71 use Cwd ();
  14         24  
  14         210  
21 14     14   15459 use Module::Build;
  14         1581490  
  14         602  
22 14     14   4381 use File::HomeDir ();
  14         34000  
  14         23526  
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 49     49   99 my($class, $new) = @_;
36 49 100       182 $is_test = $new if defined $new;
37 49         5629 $is_test;
38             }
39              
40             our $class_suffix = {};
41             sub _uncache {
42 9     9   31 my($class, $name) = @_;
43 9         25 delete $Clustericious::Config::Singletons{$name};
44 9   100     70 $class_suffix->{$name} //= 1;
45 9         30 $class_suffix->{$name}++;
46             }
47              
48 18     18 0 28 sub pre_rendered { }
49 18     18 0 32 sub rendered { }
50              
51              
52             sub new {
53 24     24 1 4138 my $class = shift;
54 24 100       126 my %t_args = (ref $_[-1] eq 'ARRAY' ? @{( pop )} : () );
  3         10  
55 24         56 my $arg = $_[0];
56 24 50       85 ($arg = caller) =~ s/:.*$// unless $arg; # Determine from caller's class
57 24 50       99 return $Singletons{$arg} if exists($Singletons{$arg});
58              
59            
60 24         44 my $we_are_testing_this_module = 0;
61 24 100       113 if(__PACKAGE__->_testing) {
62 8         17 $we_are_testing_this_module = 0;
63             }
64              
65 24         85 my $conf_data;
66              
67 24         282 my $json = JSON::XS->new;
68            
69 24         54 state $package_counter = 0;
70 24         75 my $namespace = "Clustericious::Config::TemplatePackage::Package$package_counter";
71 12     12   106 eval qq{ package $namespace; use Clustericious::Config::Helpers; };
  12         24  
  12         1279  
  24         2459  
72 24 50       100 die $@ if $@;
73 24         81 $package_counter++;
74            
75 24         386 my $mt = Mojo::Template->new(namespace => $namespace)->auto_escape(0);
76 24         1582 $mt->prepend( join "\n", map " my \$$_ = q{$t_args{$_}};", sort keys %t_args );
77              
78 24         235 my $filename;
79 24 100 0     187 if (ref $arg eq 'SCALAR') {
    100 33        
    50          
80 4         21 $class->pre_rendered( $$arg );
81 4         24 my $rendered = $mt->render($$arg);
82 4         10735 $class->rendered( SCALAR => $rendered );
83 4 50       16 die $rendered if ( (ref($rendered)) =~ /Exception/ );
84 4 100       19 my $type = $rendered =~ /^---/ ? 'yaml' : 'json';
85             $conf_data = $type eq 'yaml' ?
86 1         143 eval { YAML::XS::Load( $rendered ); }
87 4 100       15 : eval { $json->decode( $rendered ); };
  3         42  
88 4 50       17 LOGDIE "Could not parse $type \n-------\n$rendered\n---------\n$@\n" if $@;
89             } elsif (ref $arg eq 'HASH') {
90 4         446 $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 16         27 my @conf_dirs;
100              
101 16 100       100 @conf_dirs = $ENV{CLUSTERICIOUS_CONF_DIR} if defined( $ENV{CLUSTERICIOUS_CONF_DIR} );
102              
103 16 100 66     92 push @conf_dirs, ( File::HomeDir->my_home . "/etc", "/util/etc", "/etc" ) unless $we_are_testing_this_module || __PACKAGE__->_testing;
104 16         381 my $conf_file = "$arg.conf";
105 16         51 $conf_file =~ s/::/-/g;
106 16     22   219 my ($dir) = List::Util::first { -e "$_/$conf_file" } @conf_dirs;
  22         505  
107 16 100       87 if ($dir) {
108 14         117 TRACE "reading from config file $dir/$conf_file";
109 14         226 $filename = "$dir/$conf_file";
110 14         103 $class->pre_rendered( $filename );
111 14         110 my $rendered = $mt->render_file($filename);
112 14         252 $class->rendered( $filename => $rendered );
113 14 50       56 die $rendered if ( (ref $rendered) =~ /Exception/ );
114 14 100       75 my $type = $rendered =~ /^---/ ? 'yaml' : 'json';
115 14 50       67 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 12         1113 ? eval { YAML::XS::Load($rendered) }
122 14 100       56 : eval { $json->decode($rendered) };
  2         28  
123 14 50       106 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         23 $conf_data = {};
127             }
128             }
129 24   100     82 $conf_data ||= {};
130 24         192 Clustericious::Config::Helpers->_do_merges($conf_data);
131 24         14306 _add_heuristics($filename,$conf_data);
132             # Use derived classes so that AUTOLOADING keeps namespaces separate
133             # for various apps.
134 24 100       85 if ($class eq __PACKAGE__) {
135 21 100       66 if (ref $arg) {
136 5         15 $arg = "$arg";
137 5         15 $arg =~ tr/a-zA-Z0-9//cd;
138             }
139 21         72 $class = join '::', $class, 'App', $arg;
140 21 100       129 $class .= $class_suffix->{$arg} if $class_suffix->{$arg};
141 21         86 my $dome = '@'."$class"."::ISA = ('".__PACKAGE__. "')";
142 21         1577 eval $dome;
143 21 50       152 die "error setting ISA : $@" if $@;
144             }
145 24         413 bless $conf_data, $class;
146             }
147              
148             sub _add_heuristics {
149 24     24   51 my $filename = shift;
150             # Account for some mojo api changes
151 24         58 my $conf_data = shift;
152 24 50 33     159 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 48     48   11574 my $self = shift;
176 48         98 my %args = @_;
177 48         91 my $default = $args{default};
178 48         93 my $default_exists = exists $args{default};
179 48         61 our $AUTOLOAD;
180 48         70 my $called = $AUTOLOAD;
181 48         257 $called =~ s/.*:://g;
182 48 100 66     190 if ($default_exists && !exists($self->{$called})) {
183 4         14 $self->{$called} = $args{default};
184             }
185 48 100 66     799 Carp::cluck "config element '$called' not found for ".(ref $self)." (".(join ',',keys(%$self)).")"
186             if $called =~ /^_/ || !exists($self->{$called});
187 48         492 my $value = $self->{$called};
188 48         61 my $obj;
189 48         80 my $invocant = ref $self;
190 48 100       137 if (ref $value eq 'HASH') {
191 3         23 $obj = $invocant->new($value);
192             }
193 14     14   113 no strict 'refs';
  14         32  
  14         2547  
194 48         254 *{ $invocant . "::$called" } = sub {
195 67     67   98 my $self = shift;
196 67 100 100     225 $self->{$called} = $default if $default_exists && !exists($self->{$called});
197 67 100       395 die "'$called' not found in ".join ',',keys(%$self)
198             unless exists($self->{$called});
199 64         113 my $value = $self->{$called};
200 64 50 100     674 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 48         265 };
206 14     14   75 use strict 'refs';
  14         30  
  14         1545  
207 48         152 $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__