File Coverage

blib/lib/Pickles/Config.pm
Criterion Covered Total %
statement 113 113 100.0
branch 20 26 76.9
condition 6 7 85.7
subroutine 17 17 100.0
pod 4 7 57.1
total 160 170 94.1


line stmt bran cond sub pod time code
1             package Pickles::Config;
2 6     6   44213 use strict;
  6         12  
  6         194  
3 6     6   36 use Carp ();
  6         12  
  6         88  
4 6     6   29 use File::Spec;
  6         10  
  6         136  
5 6     6   1602 use Path::Class;
  6         133769  
  6         818  
6 6     6   1945 use Plack::Util::Accessor qw(appname home);
  6         527  
  6         81  
7 6     6   1255 use Pickles::Util qw(env_value);
  6         12  
  6         4264  
8              
9             sub new {
10 11     11 1 41672 my $class = shift;
11 11         49 my %args = @_;
12 11         50 my $self = bless {}, $class;
13 11         72 $self->{appname} = Pickles::Util::appname( $class );
14 11         153 $self->setup_home( $args{home} );
15 11         336 $self->{ACTION_PREFIX} = '';
16 11   100     193 $self->load_files( $args{files} || [] );
17 11         46 $self;
18             }
19              
20             sub construct {
21 8     8 1 17723 my $class = shift;
22 8         119 my $self = $class->new;
23 8         52 my $files = $self->get_config_files;
24 8         24 $self->load_files( $files );
25 8         54 $self;
26             }
27              
28             sub get {
29 8     8 1 3352 my( $self, $key, $default ) = @_;
30 8 100       86 return defined $self->{$key} ? $self->{$key} : $default;
31             }
32              
33             sub setup_home {
34 11     11 0 38 my( $self, $home ) = @_;
35 11   66     133 my $dir =
36             $home || env_value( 'HOME', $self->appname ) || $ENV{'PICKLES_HOME'};
37 11 100       42 if ( $dir ) {
38 2         14 $self->{home} = dir( $dir );
39             }
40             else {
41 9         113 my $class = ref $self;
42 9         102 (my $file = "$class.pm") =~ s|::|/|g;
43 9 50       50 if (my $inc_path = $INC{$file}) {
44 9         123 (my $path = $inc_path) =~ s/$file$//;
45 9         58 my $home = dir($path)->absolute->cleanup;
46 9         4555 $home = $home->parent while $home =~ /b?lib$/;
47 9         1907 $self->{home} = $home;
48             }
49             }
50             }
51              
52             sub load_files {
53 19     19 0 31 my( $self, $files ) = @_;
54 19         30 my %config;
55              
56             # In 5.8.8 at least, putting $self in an evaled code produces
57             # extra warnings (and possibly break the behavior of __path_to)
58             # so we create a private closure, and plant the closure into
59             # the generated packes
60 19         53 $self->{__FILES} = [];
61              
62 19     7   109 my $path_to = sub { $self->path_to(@_) };
  7         52  
63             my $load_file = sub {
64 1     1   16 my $file = $path_to->( @_ );
65 1         253 delete $INC{$file};
66 1         14257 my $subconf = require $file;
67             # death context should be at the calling config file level
68 1 50       37 Carp::croak("Could not parse $file: $@") if $@;
69 1 50       6 Carp::croak("Could not do $file: $!") if ! defined $subconf;
70 1 50       4 Carp::croak("Could not run $file") if ! $subconf;
71              
72 1         3 push @{$self->{__FILES}}, $file;
  1         6  
73 1         5 return $subconf;
74 19         96 };
75              
76 19         79 for my $file( @{$files} ) {
  19         46  
77             # only do this if the file exists
78 14 50       416 next unless -e $file;
79              
80 14         28 my $pkg = $file;
81 14         92 $pkg =~ s/([^A-Za-z0-9_])/sprintf("_%2x", unpack("C", $1))/eg;
  146         631  
82              
83 14         88 my $fqname = sprintf '%s::%s', ref $self, $pkg;
84             { # XXX This is where we plant that closure
85 6     6   34 no strict 'refs';
  6         112  
  6         199  
  14         22  
86 6     6   31 no warnings 'redefine';
  6         13  
  6         6928  
87 14         21 *{"$fqname\::__path_to"} = $path_to;
  14         220  
88 14         20 *{"$fqname\::load_file"} = $load_file;
  14         121  
89             }
90              
91 14         41 my $config_pkg = sprintf <<'SANDBOX', $fqname;
92             package %s;
93             {
94             my $conf = require $file or die $!;
95             $conf;
96             }
97             SANDBOX
98 14         39 delete $INC{$file};
99 14   100     1568 my $conf = eval $config_pkg || +{};
100 14 100       160 if ($@) {
101 2         19 warn "Error while trying to read config file $file: $@";
102             }
103             %config = (
104 14         117 %config,
105 14         37 %{$conf},
106             );
107             }
108 19         40 push @{$self->{__FILES}}, @$files;
  19         53  
109 19         76 $self->{__TIME} = time;
110 19         93 for my $key( keys %config ) {
111 28         61 $self->{$key} = $config{$key};
112             }
113 19         130 \%config;
114             }
115              
116             sub get_config_files {
117 8     8 0 14 my $self = shift;
118 8         12 my @files;
119              
120 8 100       27 if ( my $config_file = env_value('CONFIG', $self->appname) ) {
121 2         16 push @files, $self->path_to( $config_file );
122             }
123             else {
124 6         70 my @base_files = ( File::Spec->catfile('etc', 'config.pl'), 'config.pl' );
125 6         16 foreach my $f (@base_files) {
126 12         42 my $base = $self->path_to($f);
127 12 100       2486 push @files, $base if -e $base;
128             }
129             }
130            
131 8 100       39 if ( my $env = env_value('ENV', $self->appname) ) {
132 3         4 my @env_files;
133 3         12 for my $file( @files ) {
134 3         60 my ($v, $d, $fname) = File::Spec->splitpath( $file );
135 3 50       33 $fname =~ s/(\.[^\.]+)?$/$1 ? "_%s$1" : "%s"/e;
  3         15  
136 3         28 my $template = File::Spec->catpath( $v, $d, $fname );
137 3         15 my $filename = sprintf $template, $env;
138 3         7 push @env_files, $self->path_to( $filename );
139              
140             }
141 3         6 push @files, @env_files;
142             }
143 8         76 return \@files;
144             }
145              
146             sub path_to {
147 42     42 1 2160 my( $self, @path ) = @_;
148 42 100       356 if ( File::Spec->file_name_is_absolute( $path[0] ) ) {
149 6         51 return File::Spec->catfile( @path );
150             }
151 36         144 file( $self->home, @path )->stringify;
152             }
153              
154             1;
155              
156             __END__