File Coverage

blib/lib/Dancer2/Core/Role/ConfigReader.pm
Criterion Covered Total %
statement 93 105 88.5
branch 18 26 69.2
condition 2 3 66.6
subroutine 22 24 91.6
pod 4 4 100.0
total 139 162 85.8


line stmt bran cond sub pod time code
1             # ABSTRACT: Config role for Dancer2 core objects
2             $Dancer2::Core::Role::ConfigReader::VERSION = '0.400000';
3             use Moo::Role;
4 142     142   83572  
  142         334  
  142         980  
5             use File::Spec;
6 142     142   56859 use Config::Any;
  142         314  
  142         3024  
7 142     142   59344 use Hash::Merge::Simple;
  142         1104582  
  142         4901  
8 142     142   60967 use Carp 'croak';
  142         63188  
  142         9178  
9 142     142   959 use Module::Runtime 'require_module';
  142         326  
  142         6128  
10 142     142   819  
  142         328  
  142         902  
11             use Dancer2::Core::Factory;
12 142     142   5568 use Dancer2::Core;
  142         303  
  142         2710  
13 142     142   672 use Dancer2::Core::Types;
  142         309  
  142         2433  
14 142     142   697 use Dancer2::FileUtils 'path';
  142         368  
  142         1213  
15 142     142   1174697  
  142         346  
  142         209319  
16             with 'Dancer2::Core::Role::HasLocation';
17              
18             has default_config => (
19             is => 'ro',
20             isa => HashRef,
21             lazy => 1,
22             builder => '_build_default_config',
23             );
24              
25             has config_location => (
26             is => 'ro',
27             isa => ReadableFilePath,
28             lazy => 1,
29             default => sub { $ENV{DANCER_CONFDIR} || $_[0]->location },
30             );
31              
32             # The type for this attribute is Str because we don't require
33             # an existing directory with configuration files for the
34             # environments. An application without environments is still
35             # valid and works.
36             has environments_location => (
37             is => 'ro',
38             isa => Str,
39             lazy => 1,
40             default => sub {
41             $ENV{DANCER_ENVDIR}
42             || File::Spec->catdir( $_[0]->config_location, 'environments' )
43             || File::Spec->catdir( $_[0]->location, 'environments' );
44             },
45             );
46              
47             has config => (
48             is => 'ro',
49             isa => HashRef,
50             lazy => 1,
51             builder => '_build_config',
52             );
53              
54             has environment => (
55             is => 'ro',
56             isa => Str,
57             lazy => 1,
58             builder => '_build_environment',
59             );
60              
61             has config_files => (
62             is => 'ro',
63             lazy => 1,
64             isa => ArrayRef,
65             builder => '_build_config_files',
66             );
67              
68             has local_triggers => (
69             is => 'ro',
70             isa => HashRef,
71             default => sub { +{} },
72             );
73              
74             has global_triggers => (
75             is => 'ro',
76             isa => HashRef,
77             default => sub {
78             my $triggers = {
79             traces => sub {
80             my ( $self, $traces ) = @_;
81             # Carp is already a dependency
82             $Carp::Verbose = $traces ? 1 : 0;
83             },
84             };
85              
86             my $runner_config = defined $Dancer2::runner
87             ? Dancer2->runner->config
88             : {};
89              
90             for my $global ( keys %$runner_config ) {
91             next if exists $triggers->{$global};
92             $triggers->{$global} = sub {
93             my ($self, $value) = @_;
94             Dancer2->runner->config->{$global} = $value;
95             }
96             }
97              
98             return $triggers;
99             },
100             );
101              
102              
103 0     0   0  
104             my ($self) = @_;
105 36     36   804  
106             my $location = $self->config_location;
107             # an undef location means no config files for the caller
108 240     240   4509 return [] unless defined $location;
109              
110 240         3457 my $running_env = $self->environment;
111             my @available_exts = Config::Any->extensions;
112 240 50       2375 my @files;
113              
114 240         3288 my @exts = @available_exts;
115 240         3729 if (my $ext = $ENV{DANCER_CONFIG_EXT}) {
116 240         1647718 if (grep { $ext eq $_ } @available_exts) {
117             @exts = $ext;
118 240         877 warn "Only looking for configs ending in '$ext'\n"
119 240 50       1265 if $ENV{DANCER_CONFIG_VERBOSE};
120 0 0       0 } else {
  0         0  
121 0         0 warn "DANCER_CONFIG_EXT environment variable set to '$ext' which\n" .
122             "is not recognized by Config::Any. Looking for config file\n" .
123 0 0       0 "using default list of extensions:\n" .
124             "\t@available_exts\n";
125 0         0 }
126             }
127              
128             foreach my $file ( [ $location, "config" ],
129             [ $self->environments_location, $running_env ] )
130             {
131             foreach my $ext (@exts) {
132 240         7050 my $path = path( $file->[0], $file->[1] . ".$ext" );
133             next if !-r $path;
134              
135 480         14626 # Look for *_local.ext files
136 4800         19072 my $local = path( $file->[0], $file->[1] . "_local.$ext" );
137 4800 100       51809 push @files, $path, ( -r $local ? $local : () );
138             }
139             }
140 140         1046  
141 140 100       2134 return \@files;
142             }
143              
144             my ($self) = @_;
145 240         6081  
146             my $location = $self->config_location;
147             my $default = $self->default_config;
148              
149 238     238   1831 my $config = Hash::Merge::Simple->merge(
150             $default,
151 238         3835 map {
152 238         19203 warn "Merging config file $_\n" if $ENV{DANCER_CONFIG_VERBOSE};
153             $self->load_config_file($_)
154             } @{ $self->config_files }
155             );
156              
157 139 50       4179 $config = $self->_normalize_config($config);
158 139         707 return $config;
159 238         12947 }
  238         3867  
160              
161             my ( $self, @args ) = @_;
162 237         11667 my $no = scalar @args;
163 237         1059 while (@args) {
164             $self->_set_config_entry( shift(@args), shift(@args) );
165             }
166             return $no;
167 142     142   459 }
168 142         305  
169 142         462 my ( $self, $name, $value ) = @_;
170 147         653  
171             $value = $self->_normalize_config_entry( $name, $value );
172 142         179221 $value = $self->_compile_config_entry( $name, $value, $self->config );
173             $self->config->{$name} = $value;
174             }
175              
176 147     147   386 my ( $self, $config ) = @_;
177              
178 147         526 foreach my $key ( keys %{$config} ) {
179 147         2965 my $value = $config->{$key};
180 147         4548 $config->{$key} = $self->_normalize_config_entry( $key, $value );
181             }
182             return $config;
183             }
184 238     238   1376  
185             my ( $self, $config ) = @_;
186 238         518  
  238         1220  
187 2333         3217 foreach my $key ( keys %{$config} ) {
188 2333         3834 my $value = $config->{$key};
189             $config->{$key} =
190 237         667 $self->_compile_config_entry( $key, $value, $config );
191             }
192             return $config;
193             }
194 0     0   0  
195              
196 0         0 my $self = shift;
  0         0  
197 0         0 my @args = @_;
198 0         0  
199             return ( scalar @args == 1 )
200             ? $self->settings->{ $args[0] }
201 0         0 : $self->_set_config_entries(@args);
202             }
203              
204 2     2 1 42 my ( $self, $name ) = @_;
205             return exists $self->config->{$name};
206             }
207 486     486 1 3621  
208 486         1323 my ( $self, $file ) = @_;
209             my $config;
210              
211 486 100       2700 eval {
212             my @files = ($file);
213             my $tmpconfig =
214             Config::Any->load_files( { files => \@files, use_ext => 1 } )->[0];
215             ( $file, $config ) = %{$tmpconfig} if defined $tmpconfig;
216 2     2 1 2033 };
217 2         43 if ( my $err = $@ || ( !$config ) ) {
218             croak "Unable to parse the configuration file: $file: $@";
219             }
220              
221 139     139 1 487 # TODO handle mergeable entries
222 139         275 return $config;
223             }
224 139         303  
225 139         335 # private
226 139         1239  
227             my $_normalizers = {
228 138 50       806052 charset => sub {
  138         703  
229             my ($charset) = @_;
230 139 100 66     4983 return $charset if !length( $charset || '' );
231 1         72  
232             require_module('Encode');
233             my $encoding = Encode::find_encoding($charset);
234             croak
235 138         1680 "Charset defined in configuration is wrong : couldn't identify '$charset'"
236             unless defined $encoding;
237             my $name = $encoding->name;
238              
239             # Perl makes a distinction between the usual perl utf8, and the strict
240             # utf8 charset. But we don't want to make this distinction
241             $name = 'utf-8' if $name eq 'utf-8-strict';
242             return $name;
243             },
244             };
245              
246             my ( $self, $name, $value ) = @_;
247             $value = $_normalizers->{$name}->($value)
248             if exists $_normalizers->{$name};
249             return $value;
250             }
251              
252             my ( $self, $name, $value, $config ) = @_;
253              
254             my $trigger = exists $self->local_triggers->{$name} ?
255             $self->local_triggers->{$name} :
256             $self->global_triggers->{$name};
257              
258             defined $trigger or return $value;
259              
260 2480     2480   3934 return $trigger->( $self, $value, $config );
261             }
262 2480 100       5207  
263 2479         4568 1;
264              
265              
266             =pod
267 147     147   1917  
268             =encoding UTF-8
269              
270             =head1 NAME
271 147 100       1169  
272             Dancer2::Core::Role::ConfigReader - Config role for Dancer2 core objects
273 147 100       575  
274             =head1 VERSION
275 91         446  
276             version 0.400000
277              
278             =head1 DESCRIPTION
279              
280             Provides a C<config> attribute that feeds itself by finding and parsing
281             configuration files.
282              
283             Also provides a C<setting()> method which is supposed to be used by externals to
284             read/write config entries.
285              
286             =head1 ATTRIBUTES
287              
288             =head2 location
289              
290             Absolute path to the directory where the server started.
291              
292             =head2 config_location
293              
294             Gets the location from the configuration. Same as C<< $object->location >>.
295              
296             =head2 environments_location
297              
298             Gets the directory were the environment files are stored.
299              
300             =head2 config
301              
302             Returns the whole configuration.
303              
304             =head2 environments
305              
306             Returns the name of the environment.
307              
308             =head2 config_files
309              
310             List of all the configuration files.
311              
312             =head1 METHODS
313              
314             =head2 settings
315              
316             Alias for config. Equivalent to <<$object->config>>.
317              
318             =head2 setting
319              
320             Get or set an element from the configuration.
321              
322             =head2 has_setting
323              
324             Verifies that a key exists in the configuration.
325              
326             =head2 load_config_file
327              
328             Load the configuration files.
329              
330             =head1 AUTHOR
331              
332             Dancer Core Developers
333              
334             =head1 COPYRIGHT AND LICENSE
335              
336             This software is copyright (c) 2022 by Alexis Sukrieh.
337              
338             This is free software; you can redistribute it and/or modify it under
339             the same terms as the Perl 5 programming language system itself.
340              
341             =cut