Skip to content

Commit 868a846

Browse files
committed
Create ConfigReader role and an implementation
Create Dancer2::Core::Role::ConfigReader and Dancer2::ConfigReader::File::Simple. Signed-off-by: Mikko Johannes Koivunalho <[email protected]>
1 parent d85d812 commit 868a846

File tree

2 files changed

+272
-0
lines changed

2 files changed

+272
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package Dancer2::ConfigReader::File::Simple;
2+
3+
use Moo;
4+
5+
use File::Spec;
6+
use Config::Any;
7+
use Hash::Merge::Simple;
8+
use Carp 'croak';
9+
use Module::Runtime 'require_module';
10+
11+
use Dancer2::Core::Factory;
12+
use Dancer2::Core;
13+
use Dancer2::Core::Types;
14+
use Dancer2::FileUtils 'path';
15+
16+
with 'Dancer2::Core::Role::ConfigReader';
17+
18+
has name => (
19+
is => 'ro',
20+
isa => Str,
21+
lazy => 0,
22+
default => sub {'File::Simple'},
23+
);
24+
25+
has config_files => (
26+
is => 'ro',
27+
lazy => 1,
28+
isa => ArrayRef,
29+
builder => '_build_config_files',
30+
);
31+
32+
sub read_config {
33+
my ($self) = @_;
34+
35+
my $config = Hash::Merge::Simple->merge(
36+
map {
37+
warn "Merging config file $_\n" if $ENV{DANCER_CONFIG_VERBOSE};
38+
$self->_load_config_file($_)
39+
} @{ $self->config_files }
40+
);
41+
42+
return $config;
43+
}
44+
45+
sub _build_config_files {
46+
my ($self) = @_;
47+
48+
my $location = $self->config_location;
49+
# an undef location means no config files for the caller
50+
return [] unless defined $location;
51+
52+
my $running_env = $self->environment;
53+
my @available_exts = Config::Any->extensions;
54+
my @files;
55+
56+
my @exts = @available_exts;
57+
if (my $ext = $ENV{DANCER_CONFIG_EXT}) {
58+
if (grep { $ext eq $_ } @available_exts) {
59+
@exts = $ext;
60+
warn "Only looking for configs ending in '$ext'\n"
61+
if $ENV{DANCER_CONFIG_VERBOSE};
62+
} else {
63+
warn "DANCER_CONFIG_EXT environment variable set to '$ext' which\n" .
64+
"is not recognized by Config::Any. Looking for config file\n" .
65+
"using default list of extensions:\n" .
66+
"\t@available_exts\n";
67+
}
68+
}
69+
70+
foreach my $file ( [ $location, "config" ],
71+
[ $self->environments_location, $running_env ] )
72+
{
73+
foreach my $ext (@exts) {
74+
my $path = path( $file->[0], $file->[1] . ".$ext" );
75+
next if !-r $path;
76+
77+
# Look for *_local.ext files
78+
my $local = path( $file->[0], $file->[1] . "_local.$ext" );
79+
push @files, $path, ( -r $local ? $local : () );
80+
}
81+
}
82+
83+
return \@files;
84+
}
85+
86+
sub _load_config_file {
87+
my ( $self, $file ) = @_;
88+
my $config;
89+
90+
eval {
91+
my @files = ($file);
92+
my $tmpconfig =
93+
Config::Any->load_files( { files => \@files, use_ext => 1 } )->[0];
94+
( $file, $config ) = %{$tmpconfig} if defined $tmpconfig;
95+
};
96+
if ( my $err = $@ || ( !$config ) ) {
97+
croak "Unable to parse the configuration file: $file: $@";
98+
}
99+
100+
# TODO handle mergeable entries
101+
return $config;
102+
}
103+
104+
1;
105+
106+
__END__
107+
108+
=head1 DESCRIPTION
109+
110+
This class provides the same features to read configuration files
111+
as were earlier done by C<Dancer2::Core::Role::Config>.
112+
113+
If you need to add additional functionality to the reading
114+
mechanism, you can extend this class. An example of this is
115+
in: B</t/lib/Dancer2/ConfigReader/File/Extended.pm>
116+
117+
=head1 ATTRIBUTES
118+
119+
=attr name
120+
121+
The name of the class.
122+
123+
=attr location
124+
125+
Absolute path to the directory where the server started.
126+
127+
=attr config_location
128+
129+
Gets the location from the configuration. Same as C<< $object->location >>.
130+
131+
=attr environments_location
132+
133+
Gets the directory where the environment files are stored.
134+
135+
=attr environment
136+
137+
Returns the name of the environment.
138+
139+
=attr config_files
140+
141+
List of all the configuration files.
142+
143+
=head1 METHODS
144+
145+
=head2 read_config
146+
147+
Load the configuration files.

lib/Dancer2/Core/Role/ConfigReader.pm

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# ABSTRACT: Config reader role for Dancer2 core objects
2+
package Dancer2::Core::Role::ConfigReader;
3+
4+
use Moo::Role;
5+
6+
use File::Spec;
7+
use Config::Any;
8+
use Hash::Merge::Simple;
9+
use Carp 'croak';
10+
use Module::Runtime 'require_module';
11+
12+
use Dancer2::Core::Factory;
13+
use Dancer2::Core;
14+
use Dancer2::Core::Types;
15+
use Dancer2::FileUtils 'path';
16+
17+
has config_location => (
18+
is => 'ro',
19+
isa => ReadableFilePath,
20+
lazy => 1,
21+
default => sub { $ENV{DANCER_CONFDIR} || $_[0]->location },
22+
);
23+
24+
# The type for this attribute is Str because we don't require
25+
# an existing directory with configuration files for the
26+
# environments. An application without environments is still
27+
# valid and works.
28+
has environments_location => (
29+
is => 'ro',
30+
isa => Str,
31+
lazy => 1,
32+
default => sub {
33+
$ENV{DANCER_ENVDIR}
34+
|| File::Spec->catdir( $_[0]->config_location, 'environments' )
35+
|| File::Spec->catdir( $_[0]->location, 'environments' );
36+
},
37+
);
38+
39+
# It is required to get environment from the caller.
40+
# Environment should be passed down from Dancer2::Core::App.
41+
has environment => (
42+
is => 'ro',
43+
isa => Str,
44+
required => 1,
45+
);
46+
47+
# It is required to get location from the caller.
48+
has location => (
49+
is => 'ro',
50+
isa => ReadableFilePath,
51+
required => 1,
52+
);
53+
54+
1;
55+
56+
__END__
57+
58+
=head1 DESCRIPTION
59+
60+
This role is implemented by different
61+
config readers. A config reader creates the
62+
configuration for Dancer2 app.
63+
Config can be created by reading configuration
64+
files, from environment variables, by fetching
65+
it from a cloud service, or any other means.
66+
67+
By default, the config loader
68+
which is used, is C<Dancer2::ConfigReader::File::Simple>
69+
but user can create his own config reader
70+
if he wants to replace or augment
71+
the default method of config creation.
72+
That method should implement this role.
73+
74+
The implementing module gets the following parameters
75+
during creation:
76+
77+
=attr environment
78+
79+
The name of the environment used, e.g.
80+
production, development, staging.
81+
82+
==attr location
83+
84+
Absolute path to the directory where the server started.
85+
86+
These arguments are passed when the object is created by
87+
C<Dancer2::Core::Role::Config>. How the config
88+
reader applies them as it needs.
89+
90+
Provides a C<config> attribute that - when accessing
91+
the first time - feeds itself by finding and parsing
92+
configuration files.
93+
94+
Also provides a C<setting()> method which is
95+
supposed to be used by externals to
96+
read/write config entries.
97+
98+
=head1 ATTRIBUTES
99+
100+
=attr location
101+
102+
Absolute path to the directory where the server started.
103+
104+
=attr config_location
105+
106+
Gets the location from the configuration. Same as C<< $object->location >>.
107+
108+
=attr environments_location
109+
110+
Gets the directory where the environment files are stored.
111+
112+
=attr config
113+
114+
Returns the whole configuration.
115+
116+
=attr environments
117+
118+
Returns the name of the environment.
119+
120+
=head1 METHODS
121+
122+
=head2 read_config
123+
124+
Load the configuration.
125+
Whatever source the config comes from, files, env vars, etc.

0 commit comments

Comments
 (0)