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   203387 use strict;
  20         37  
  20         1026  
4 18     18   96 use warnings;
  18         31  
  18         644  
5 17     17   184 use v5.10;
  17         53  
  17         1069  
6              
7             # ABSTRACT: Configuration files for Clustericious nodes.
8             our $VERSION = '0.29'; # VERSION
9              
10              
11 16     16   7919 use Clustericious::Config::Password;
  16         43  
  16         506  
12 16     16   90 use List::Util;
  16         29  
  16         1184  
13 15     15   17089 use JSON::XS;
  15         105532  
  15         1052  
14 15     15   4952 use YAML::XS ();
  15         19581  
  15         296  
15 15     15   14316 use Mojo::Template;
  15         1066502  
  15         177  
16 15     15   22550 use Log::Log4perl qw/:easy/;
  15         891344  
  15         118  
17 15     15   26857 use Storable;
  15         57452  
  15         1044  
18 15     15   10270 use Clustericious::Config::Helpers ();
  15         46  
  15         331  
19 15     15   85 use Data::Dumper;
  15         33  
  15         612  
20 15     15   78 use Cwd ();
  15         34  
  15         223  
21 15     15   16046 use Module::Build;
  15         1640802  
  15         584  
22 15     15   4461 use File::HomeDir ();
  15         40460  
  15         22661  
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   108 my($class, $new) = @_;
36 52 100       187 $is_test = $new if defined $new;
37 52         6032 $is_test;
38             }
39              
40             our $class_suffix = {};
41             sub _uncache {
42 10     10   24 my($class, $name) = @_;
43 10         35 delete $Clustericious::Config::Singletons{$name};
44 10   100     74 $class_suffix->{$name} //= 1;
45 10         33 $class_suffix->{$name}++;
46             }
47              
48 19     19 0 31 sub pre_rendered { }
49 19     19 0 34 sub rendered { }
50              
51              
52             sub new {
53 25     25 1 3467 my $class = shift;
54 25 100       123 my %t_args = (ref $_[-1] eq 'ARRAY' ? @{( pop )} : () );
  3         8  
55 25         52 my $arg = $_[0];
56 25 50       82 ($arg = caller) =~ s/:.*$// unless $arg; # Determine from caller's class
57 25 50       100 return $Singletons{$arg} if exists($Singletons{$arg});
58              
59            
60 25         53 my $we_are_testing_this_module = 0;
61 25 100       108 if(__PACKAGE__->_testing) {
62 9         18 $we_are_testing_this_module = 0;
63             }
64              
65 25         90 my $conf_data;
66              
67 25         280 my $json = JSON::XS->new;
68            
69 25         55 state $package_counter = 0;
70 25         77 my $namespace = "Clustericious::Config::TemplatePackage::Package$package_counter";
71 13     13   109 eval qq{ package $namespace; use Clustericious::Config::Helpers; };
  13         23  
  13         1368  
  25         2440  
72 25 50       98 die $@ if $@;
73 25         46 $package_counter++;
74            
75 25         441 my $mt = Mojo::Template->new(namespace => $namespace)->auto_escape(0);
76 25         1592 $mt->prepend( join "\n", map " my \$$_ = q{$t_args{$_}};", sort keys %t_args );
77              
78 25         241 my $filename;
79 25 100 0     184 if (ref $arg eq 'SCALAR') {
    100 33        
    50          
80 4         25 $class->pre_rendered( $$arg );
81 4         24 my $rendered = $mt->render($$arg);
82 4         6685 $class->rendered( SCALAR => $rendered );
83 4 50       18 die $rendered if ( (ref($rendered)) =~ /Exception/ );
84 4 100       21 my $type = $rendered =~ /^---/ ? 'yaml' : 'json';
85             $conf_data = $type eq 'yaml' ?
86 1         229 eval { YAML::XS::Load( $rendered ); }
87 4 100       14 : eval { $json->decode( $rendered ); };
  3         47  
88 4 50       28 LOGDIE "Could not parse $type \n-------\n$rendered\n---------\n$@\n" if $@;
89             } elsif (ref $arg eq 'HASH') {
90 4         395 $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         25 my @conf_dirs;
100              
101 17 100       90 @conf_dirs = $ENV{CLUSTERICIOUS_CONF_DIR} if defined( $ENV{CLUSTERICIOUS_CONF_DIR} );
102              
103 17 100 66     147 push @conf_dirs, ( File::HomeDir->my_home . "/etc", "/util/etc", "/etc" ) unless $we_are_testing_this_module || __PACKAGE__->_testing;
104 17         306 my $conf_file = "$arg.conf";
105 17         42 $conf_file =~ s/::/-/g;
106 17     23   198 my ($dir) = List::Util::first { -e "$_/$conf_file" } @conf_dirs;
  23         506  
107 17 100       92 if ($dir) {
108 15         121 TRACE "reading from config file $dir/$conf_file";
109 15         230 $filename = "$dir/$conf_file";
110 15         71 $class->pre_rendered( $filename );
111 15         88 my $rendered = $mt->render_file($filename);
112 15         327 $class->rendered( $filename => $rendered );
113 15 50       61 die $rendered if ( (ref $rendered) =~ /Exception/ );
114 15 100       82 my $type = $rendered =~ /^---/ ? 'yaml' : 'json';
115 15 50       78 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         1081 ? eval { YAML::XS::Load($rendered) }
122 15 100       57 : eval { $json->decode($rendered) };
  2         27  
123 15 50       103 LOGDIE "Could not parse $type\n-------\n$rendered\n---------\n$@\n" if $@;
124             } else {
125 2 50       18 TRACE "could not find $conf_file file in: @conf_dirs" unless $dir;
126 2         24 $conf_data = {};
127             }
128             }
129 25   100     90 $conf_data ||= {};
130 25         186 Clustericious::Config::Helpers->_do_merges($conf_data);
131 25         19732 _add_heuristics($filename,$conf_data);
132             # Use derived classes so that AUTOLOADING keeps namespaces separate
133             # for various apps.
134 25 100       84 if ($class eq __PACKAGE__) {
135 22 100       77 if (ref $arg) {
136 5         14 $arg = "$arg";
137 5         25 $arg =~ tr/a-zA-Z0-9//cd;
138             }
139 22         83 $class = join '::', $class, 'App', $arg;
140 22 100       109 $class .= $class_suffix->{$arg} if $class_suffix->{$arg};
141 22         106 my $dome = '@'."$class"."::ISA = ('".__PACKAGE__. "')";
142 22         1519 eval $dome;
143 22 50       111 die "error setting ISA : $@" if $@;
144             }
145 25         425 bless $conf_data, $class;
146             }
147              
148             sub _add_heuristics {
149 25     25   46 my $filename = shift;
150             # Account for some mojo api changes
151 25         45 my $conf_data = shift;
152 25 50 33     128 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   9495 my $self = shift;
176 52         104 my %args = @_;
177 52         84 my $default = $args{default};
178 52         101 my $default_exists = exists $args{default};
179 52         55 our $AUTOLOAD;
180 52         73 my $called = $AUTOLOAD;
181 52         317 $called =~ s/.*:://g;
182 52 100 66     192 if ($default_exists && !exists($self->{$called})) {
183 4         11 $self->{$called} = $args{default};
184             }
185 52 100 66     404 Carp::cluck "config element '$called' not found for ".(ref $self)." (".(join ',',keys(%$self)).")"
186             if $called =~ /^_/ || !exists($self->{$called});
187 52         101 my $value = $self->{$called};
188 52         58 my $obj;
189 52         76 my $invocant = ref $self;
190 52 100       133 if (ref $value eq 'HASH') {
191 3         23 $obj = $invocant->new($value);
192             }
193 15     15   123 no strict 'refs';
  15         33  
  15         2571  
194 52         269 *{ $invocant . "::$called" } = sub {
195 82     82   122 my $self = shift;
196 82 100 100     286 $self->{$called} = $default if $default_exists && !exists($self->{$called});
197 82 100       428 die "'$called' not found in ".join ',',keys(%$self)
198             unless exists($self->{$called});
199 79         137 my $value = $self->{$called};
200 79 50 100     814 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         262 };
206 15     15   81 use strict 'refs';
  15         30  
  15         1661  
207 52         149 $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__