-
Notifications
You must be signed in to change notification settings - Fork 283
Feature/new config system 2 recursive #1736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
784cf27
0f6e4d2
50e40ab
a852aad
c4d0e97
adb70a7
9403209
82b23b7
e34deac
7b815da
c8dbe9a
af57075
ad468e9
984b95d
06042eb
38dd888
53dfb01
199e30c
93613d8
7bc1194
74e28c4
b06a7bc
4a62c1b
c30f818
0738b39
1cf0d60
03955b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,12 +8,16 @@ use Config::Any; | |||||||||||||||
| use Hash::Merge::Simple; | ||||||||||||||||
| use Carp 'croak'; | ||||||||||||||||
| use Module::Runtime qw{ use_module }; | ||||||||||||||||
| use Ref::Util qw/ is_arrayref /; | ||||||||||||||||
| use Scalar::Util qw/ blessed /; | ||||||||||||||||
|
|
||||||||||||||||
| use Dancer2::Core::Factory; | ||||||||||||||||
| use Dancer2::Core; | ||||||||||||||||
| use Dancer2::Core::Types; | ||||||||||||||||
| use Dancer2::ConfigUtils 'normalize_config_entry'; | ||||||||||||||||
|
|
||||||||||||||||
| our $MAX_CONFIGS = $ENV{DANCER_MAX_CONFIGS} || 100; | ||||||||||||||||
|
|
||||||||||||||||
| has location => ( | ||||||||||||||||
| is => 'ro', | ||||||||||||||||
| isa => Str, | ||||||||||||||||
|
|
@@ -71,17 +75,59 @@ has config_readers => ( | |||||||||||||||
| sub _build_config { | ||||||||||||||||
| my ($self) = @_; | ||||||||||||||||
|
|
||||||||||||||||
| my $default = $self->default_config; | ||||||||||||||||
| my $config = Hash::Merge::Simple->merge( | ||||||||||||||||
| $default, | ||||||||||||||||
| map { | ||||||||||||||||
| warn "Merging config from @{[ $_->name() ]}\n" if $ENV{DANCER_CONFIG_VERBOSE}; | ||||||||||||||||
| $_->read_config() | ||||||||||||||||
| } @{ $self->config_readers } | ||||||||||||||||
| ); | ||||||||||||||||
| my $config = $self->default_config; | ||||||||||||||||
|
|
||||||||||||||||
| $config = $self->_normalize_config($config); | ||||||||||||||||
| return $config; | ||||||||||||||||
| my $nbr_config = 0; | ||||||||||||||||
|
|
||||||||||||||||
| my @readers = @{ $self->config_readers }; | ||||||||||||||||
|
|
||||||||||||||||
| my $config_to_object = sub { | ||||||||||||||||
| my $thing = $_; | ||||||||||||||||
|
|
||||||||||||||||
| return $thing if blessed $thing; | ||||||||||||||||
|
|
||||||||||||||||
| $thing = { $thing => {} } unless ref $thing; | ||||||||||||||||
|
|
||||||||||||||||
| die "additional_config_readers entry can have only one key\n" | ||||||||||||||||
| if 1 < keys %$thing; | ||||||||||||||||
|
Comment on lines
+91
to
+92
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it'd be a bit more readable as
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. Yanick is AFK for the next few weeks, so I will update and merge today. |
||||||||||||||||
|
|
||||||||||||||||
| my( $class, $args ) = %$thing; | ||||||||||||||||
|
|
||||||||||||||||
| return use_module($class)->new( | ||||||||||||||||
| location => $self->location, | ||||||||||||||||
| environment => $self->environment, | ||||||||||||||||
| %$args, | ||||||||||||||||
| ); | ||||||||||||||||
| }; | ||||||||||||||||
|
|
||||||||||||||||
| while( my $r = shift @readers ) { | ||||||||||||||||
| die <<"END" if $nbr_config++ >= $MAX_CONFIGS; | ||||||||||||||||
| MAX_CONFIGS exceeded: read over $MAX_CONFIGS configurations | ||||||||||||||||
|
|
||||||||||||||||
| Looks like you have an infinite recursion in your configuration system. | ||||||||||||||||
| Re-run with DANCER_CONFIG_VERBOSE=1 to see what is going on. | ||||||||||||||||
|
|
||||||||||||||||
| If your application really read that many configs (may \$dog have mercy | ||||||||||||||||
| on your soul), you can increase the limit via the environment variable | ||||||||||||||||
| DANCER_MAX_CONFIGS. | ||||||||||||||||
|
|
||||||||||||||||
| END | ||||||||||||||||
| warn "Reading config from @{[ $r->name() ]}\n" if $ENV{DANCER_CONFIG_VERBOSE}; | ||||||||||||||||
| my $local_config = $r->read_config; | ||||||||||||||||
|
|
||||||||||||||||
| if( my $additionals = delete $local_config->{additional_config_readers} ) { | ||||||||||||||||
|
|
||||||||||||||||
| warn "Additional config readers found\n" if $ENV{DANCER_CONFIG_VERBOSE}; | ||||||||||||||||
|
|
||||||||||||||||
| unshift @readers, map { $config_to_object->($_) } is_arrayref($additionals) ? @$additionals : ($additionals); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| $config = Hash::Merge::Simple->merge( | ||||||||||||||||
| $config, $local_config | ||||||||||||||||
| ); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| return $self->_normalize_config($config); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| sub _normalize_config { | ||||||||||||||||
|
|
@@ -116,26 +162,18 @@ __END__ | |||||||||||||||
|
|
||||||||||||||||
| =head1 DESCRIPTION | ||||||||||||||||
|
|
||||||||||||||||
| This class provides a C<config> attribute that - when accessing | ||||||||||||||||
| the first time - feeds itself by executing one or more | ||||||||||||||||
| B<ConfigReader> packages. | ||||||||||||||||
| This class provides a C<config> attribute that | ||||||||||||||||
| is populated by executing one or more B<ConfigReader> packages. | ||||||||||||||||
| The default ConfigReader used by default is C<Dancer2::ConfigReader::Config::Any>. | ||||||||||||||||
|
Comment on lines
+165
to
+167
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slightly less tautology:
Suggested change
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Much better! I will make this change and merge today. |
||||||||||||||||
|
|
||||||||||||||||
| Also provides a C<setting()> method which is supposed to be used by externals to | ||||||||||||||||
| read/write config entries. | ||||||||||||||||
|
|
||||||||||||||||
| You can control which B<ConfigReader> | ||||||||||||||||
| class or classes to use to create the config. | ||||||||||||||||
|
|
||||||||||||||||
| Use C<DANCER_CONFIG_READERS> environment variable to define | ||||||||||||||||
| which class or classes you want. | ||||||||||||||||
| If more than one config reader is used, their configurations are merged | ||||||||||||||||
| in left-to-write order where the previous config items get overwritten by subsequent ones. | ||||||||||||||||
|
|
||||||||||||||||
| DANCER_CONFIG_READERS='Dancer2::ConfigReader::Config::Any,Dancer2::ConfigReader::CustomConfig' | ||||||||||||||||
|
|
||||||||||||||||
| If you want several, separate them with a comma (","). | ||||||||||||||||
| Configs are added in left-to-write order where the previous | ||||||||||||||||
| config items get overwritten by subsequent ones. | ||||||||||||||||
|
|
||||||||||||||||
| For example, if config | ||||||||||||||||
| For example, assuming we are using 3 config readers, | ||||||||||||||||
| if the first config reader returns | ||||||||||||||||
|
|
||||||||||||||||
| item1: content1 | ||||||||||||||||
| item2: content2 | ||||||||||||||||
|
|
@@ -149,7 +187,7 @@ For example, if config | |||||||||||||||
| subitem1: subcontent1 | ||||||||||||||||
| subitem2: subcontent2 | ||||||||||||||||
|
|
||||||||||||||||
| was followed by config | ||||||||||||||||
| and the second returns | ||||||||||||||||
|
|
||||||||||||||||
| item2: content9 | ||||||||||||||||
| item3: | ||||||||||||||||
|
|
@@ -161,7 +199,7 @@ was followed by config | |||||||||||||||
| subsubitem5: subsubcontent5 | ||||||||||||||||
| item4: content4 | ||||||||||||||||
|
|
||||||||||||||||
| then the final config would be | ||||||||||||||||
| then the final config is | ||||||||||||||||
|
|
||||||||||||||||
| item1: content1 | ||||||||||||||||
| item2: content9 | ||||||||||||||||
|
|
@@ -175,19 +213,42 @@ then the final config would be | |||||||||||||||
| subsubitem5: subsubcontent5 | ||||||||||||||||
| item4: content4 | ||||||||||||||||
|
|
||||||||||||||||
| The default B<ConfigReader> is C<Dancer2::ConfigReader::Config::Any>. | ||||||||||||||||
| =head2 Configuring the ConfigReaders via DANCER_CONFIG_READERS | ||||||||||||||||
|
|
||||||||||||||||
| You can control which B<ConfigReader> | ||||||||||||||||
| class or classes to use to create the config | ||||||||||||||||
| via the C<DANCER_CONFIG_READERS> environment. | ||||||||||||||||
|
|
||||||||||||||||
| DANCER_CONFIG_READERS='Dancer2::ConfigReader::Config::Any,Dancer2::ConfigReader::CustomConfig' | ||||||||||||||||
|
|
||||||||||||||||
| If you want several, separate them with a comma (","). | ||||||||||||||||
|
|
||||||||||||||||
| =head2 Bootstrapping the ConfigReaders via C<additional_config_readers> | ||||||||||||||||
|
|
||||||||||||||||
| If the key C<additional_config_readers> is found in one in one or more of the configurations provided by the ConfigReaders, it'll be | ||||||||||||||||
| instantiated and added to the list of configurations to merge. This way you can, for example, create a basic F<config.yml> that is | ||||||||||||||||
|
|
||||||||||||||||
| additional_config_readers: | ||||||||||||||||
| - Dancer2::ConfigReader::SQLite: | ||||||||||||||||
| path: /path/to/sqlite.db | ||||||||||||||||
| table: config | ||||||||||||||||
|
|
||||||||||||||||
| The default ConfigReader L<Dancer2::ConfigReader::::Config::Any> will pick that file and proceed to instantiate C<Dancer2::ConfigReader::SQLite> | ||||||||||||||||
| with the provided parameters. | ||||||||||||||||
|
|
||||||||||||||||
| C<additional_config_readers> can take one or a list of reader configurations, which can be either the name of the ConfigReader's class, or the | ||||||||||||||||
| key/value pair of the class name and its constructor's arguments. | ||||||||||||||||
|
|
||||||||||||||||
| You can also create your own custom B<ConfigReader> classes. | ||||||||||||||||
| =head2 Creating your own custom B<ConfigReader> classes. | ||||||||||||||||
|
|
||||||||||||||||
| If you want, you can also extend class C<Dancer2::ConfigReader::Config::Any>. | ||||||||||||||||
| Here is an example: | ||||||||||||||||
| Here's an example extending class C<Dancer2::ConfigReader::Config::Any>. | ||||||||||||||||
|
|
||||||||||||||||
| package Dancer2::ConfigReader::FileExtended; | ||||||||||||||||
| package Dancer2::ConfigReader::Config::Any::Extended; | ||||||||||||||||
| use Moo; | ||||||||||||||||
| extends 'Dancer2::ConfigReader::Config::Any'; | ||||||||||||||||
| has name => ( | ||||||||||||||||
| is => 'ro', | ||||||||||||||||
| default => sub {'FileExtended'}, | ||||||||||||||||
| default => sub {'Config::Any::Extended'}, | ||||||||||||||||
| ); | ||||||||||||||||
| around read_config => sub { | ||||||||||||||||
| my ($orig, $self) = @_; | ||||||||||||||||
|
|
||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package Dancer2::ConfigReader::Additional; | ||
| use Moo; | ||
| use Dancer2::Core::Factory; | ||
| use Dancer2::Core; | ||
| use Dancer2::Core::Types; | ||
| use Dancer2::FileUtils 'path'; | ||
|
|
||
| with 'Dancer2::Core::Role::ConfigReader'; | ||
|
|
||
| has name => ( | ||
| is => 'ro', | ||
| isa => Str, | ||
| lazy => 0, | ||
| default => 'Additional', | ||
| ); | ||
|
|
||
| has config_files => ( | ||
| is => 'ro', | ||
| lazy => 1, | ||
| isa => ArrayRef, | ||
| default => sub { | ||
| my ($self) = @_; | ||
| return []; | ||
| }, | ||
| ); | ||
|
|
||
| sub read_config { | ||
| return { | ||
| additional_config_readers => [qw/ | ||
| Dancer2::ConfigReader::Config::Any | ||
| Dancer2::ConfigReader::TestDummy | ||
| /] | ||
| }; | ||
| } | ||
|
|
||
| 1; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package Dancer2::ConfigReader::Recursive; | ||
| use Moo; | ||
| use Dancer2::Core::Factory; | ||
| use Dancer2::Core; | ||
| use Dancer2::Core::Types; | ||
| use Dancer2::FileUtils 'path'; | ||
|
|
||
| with 'Dancer2::Core::Role::ConfigReader'; | ||
|
|
||
| has name => ( | ||
| is => 'ro', | ||
| isa => Str, | ||
| lazy => 0, | ||
| default => 'Recursive', | ||
| ); | ||
|
|
||
| has config_files => ( | ||
| is => 'ro', | ||
| lazy => 1, | ||
| isa => ArrayRef, | ||
| default => sub { | ||
| my ($self) = @_; | ||
| return []; | ||
| }, | ||
| ); | ||
|
|
||
| sub read_config { | ||
| return { | ||
| additional_config_readers => | ||
| 'Dancer2::ConfigReader::Recursive' | ||
| }; | ||
| } | ||
|
|
||
| 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we mean "generating" here, or "reading"?