anyway_config 2.7.2 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +23 -5
- data/lib/.rbnext/2.7/anyway/loaders/yaml.rb +0 -1
- data/lib/.rbnext/2.7/anyway/tracing.rb +2 -2
- data/lib/.rbnext/2.7/anyway/type_casting.rb +1 -1
- data/lib/.rbnext/3.0/anyway/auto_cast.rb +2 -2
- data/lib/.rbnext/3.0/anyway/config.rb +15 -4
- data/lib/.rbnext/3.0/anyway/loaders/yaml.rb +0 -1
- data/lib/.rbnext/3.0/anyway/loaders.rb +9 -8
- data/lib/.rbnext/3.0/anyway/tracing.rb +2 -2
- data/lib/.rbnext/3.1/anyway/config.rb +15 -4
- data/lib/.rbnext/3.1/anyway/env.rb +0 -1
- data/lib/.rbnext/3.1/anyway/tracing.rb +2 -2
- data/lib/.rbnext/3.2/anyway/config.rb +15 -4
- data/lib/.rbnext/3.2/anyway/tracing.rb +2 -2
- data/lib/.rbnext/3.4/anyway/auto_cast.rb +2 -2
- data/lib/.rbnext/3.4/anyway/config.rb +15 -4
- data/lib/.rbnext/3.4/anyway/tracing.rb +2 -2
- data/lib/.rbnext/3.4/anyway/type_casting.rb +1 -1
- data/lib/anyway/config.rb +13 -2
- data/lib/anyway/env.rb +0 -1
- data/lib/anyway/loaders/doppler.rb +0 -2
- data/lib/anyway/loaders/env.rb +0 -2
- data/lib/anyway/loaders/yaml.rb +0 -1
- data/lib/anyway/loaders.rb +9 -8
- data/lib/anyway/option_parser_builder.rb +1 -1
- data/lib/anyway/rails/autoload.rb +0 -2
- data/lib/anyway/rails/loaders/credentials.rb +0 -2
- data/lib/anyway/rails/loaders/secrets.rb +0 -2
- data/lib/anyway/version.rb +1 -1
- data/lib/anyway_config.rb +5 -2
- data/sig/anyway_config.rbs +3 -0
- metadata +3 -5
- data/lib/.rbnext/2.7/anyway/auto_cast.rb +0 -53
- data/lib/.rbnext/2.7/anyway/config.rb +0 -475
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 414ea061d65832bc84e8cd08504abbea04e356431f2e85210fb458780bc9623b
|
|
4
|
+
data.tar.gz: 5ebbb3638e8ad4b870de6a8a8d3a35560a0cc4f0f13095da950e50aeb04dc928
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3a4c7e785d4a9153b64247c1b5730bf72c73ced33967a15359c5651f2da9390b8f3ad4baba2d226eb8e66e4084cc5871d69dbf9790b67c5aa8f795b2c0142f99
|
|
7
|
+
data.tar.gz: b5a2e8c483fb9120f53439e90391f6b4ef9d605cd2531a91223a0a47e43e01912cf3da8e739054a1d2d12240aadd79dbf3e1558374ee5985c7a04d0f912c291e
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## master
|
|
4
4
|
|
|
5
|
+
- Do not require optional loader dependencies to speed up loading. ([@ardecvz][])
|
|
6
|
+
|
|
7
|
+
## 2.8.0 (2025-06-24)
|
|
8
|
+
|
|
9
|
+
- Add `configuration_sources` option to config classes to specify which data sources to use. ([@palkan][])
|
|
10
|
+
|
|
5
11
|
## 2.7.2 (2025-04-01)
|
|
6
12
|
|
|
7
13
|
- Address NameError: instance variable @secrets not defined issue ([@fbuys][])
|
|
@@ -604,3 +610,4 @@ No we're dependency-free!
|
|
|
604
610
|
[@OlegChuev]: https://github.com/OlegChuev
|
|
605
611
|
[@dominikb]: https://github.com/dominikb
|
|
606
612
|
[@klondaiker]: https://github.com/klondaiker
|
|
613
|
+
[@ardecvz]: https://github.com/ardecvz
|
data/README.md
CHANGED
|
@@ -105,8 +105,8 @@ gem "anyway_config", "~> 2.0"
|
|
|
105
105
|
|
|
106
106
|
### Supported Ruby versions
|
|
107
107
|
|
|
108
|
-
- Ruby (MRI) >= 2.
|
|
109
|
-
- JRuby >= 9.
|
|
108
|
+
- Ruby (MRI) >= 2.7.0
|
|
109
|
+
- JRuby >= 9.3.0
|
|
110
110
|
|
|
111
111
|
## Usage
|
|
112
112
|
|
|
@@ -347,7 +347,7 @@ end
|
|
|
347
347
|
|
|
348
348
|
## Using with Rails
|
|
349
349
|
|
|
350
|
-
**NOTE:** version 2.x supports Rails >=
|
|
350
|
+
**NOTE:** version 2.x supports Rails >= 6.0; for older versions use version 1.x of the gem.
|
|
351
351
|
|
|
352
352
|
We recommend going through [Data population](#data-population) and [Organizing configs](#organizing-configs) sections first,
|
|
353
353
|
and then use [Rails generators](#generators) to make your application Anyway Config-ready.
|
|
@@ -653,8 +653,6 @@ MYCOOLGEM = "Nif-Nif, Naf-Naf and Nouf-Nouf"
|
|
|
653
653
|
|
|
654
654
|
## Type coercion
|
|
655
655
|
|
|
656
|
-
> 🆕 v2.2.0
|
|
657
|
-
|
|
658
656
|
You can define custom type coercion rules to convert string data to config values. To do that, use `.coerce_types` method:
|
|
659
657
|
|
|
660
658
|
```ruby
|
|
@@ -827,6 +825,8 @@ class MyConfig < Anyway::Config
|
|
|
827
825
|
end
|
|
828
826
|
```
|
|
829
827
|
|
|
828
|
+
**NOTE:** You can opt-out from EJSON loader by specifying the `ANYWAY_CONFIG_DISABLE_EJSON=true` env var (in case you have the `ejson` executable in your PATH, but don't want to use it with Anyway Config).
|
|
829
|
+
|
|
830
830
|
### Custom loaders
|
|
831
831
|
|
|
832
832
|
You can provide your own data loaders or change the existing ones using the Loaders API (which is very similar to Rack middleware builder):
|
|
@@ -884,6 +884,24 @@ def call(name:, **_opts)
|
|
|
884
884
|
end
|
|
885
885
|
```
|
|
886
886
|
|
|
887
|
+
### Specifying which loaders to use
|
|
888
|
+
|
|
889
|
+
Sometimes you may want to pick only specific loaders for a particular configuration class. For that, you can use the `.configuration_sources=` writer method:
|
|
890
|
+
|
|
891
|
+
```ruby
|
|
892
|
+
class MyConfig < Anyway::Config
|
|
893
|
+
# Only load configuraiton data from ENV and credentials
|
|
894
|
+
self.configuration_sources = [:env, :credentials]
|
|
895
|
+
# ...
|
|
896
|
+
end
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
You can access the list of available loader identifiers as follows:
|
|
900
|
+
|
|
901
|
+
```ruby
|
|
902
|
+
Anyway.loaders.keys #=> [:yml, :credentials, :env]
|
|
903
|
+
```
|
|
904
|
+
|
|
887
905
|
## Tracing
|
|
888
906
|
|
|
889
907
|
Since Anyway Config loads data from multiple source, it could be useful to know where a particular value came from.
|
|
@@ -29,7 +29,7 @@ module Anyway
|
|
|
29
29
|
def record_value(val, *path, **opts)
|
|
30
30
|
key = path.pop
|
|
31
31
|
trace = if val.is_a?(Hash)
|
|
32
|
-
Trace.new.tap { |
|
|
32
|
+
Trace.new.tap { |it| it.merge_values(val, **opts) }
|
|
33
33
|
else
|
|
34
34
|
Trace.new(:value, val, **opts)
|
|
35
35
|
end
|
|
@@ -84,7 +84,7 @@ module Anyway
|
|
|
84
84
|
|
|
85
85
|
def to_h
|
|
86
86
|
if trace?
|
|
87
|
-
value.transform_values(&:to_h).tap { |
|
|
87
|
+
value.transform_values(&:to_h).tap { |it| it.default_proc = nil }
|
|
88
88
|
else
|
|
89
89
|
{value: value, source: source}
|
|
90
90
|
end
|
|
@@ -13,9 +13,9 @@ module Anyway
|
|
|
13
13
|
|
|
14
14
|
case val
|
|
15
15
|
when Hash
|
|
16
|
-
val.transform_values { it
|
|
16
|
+
val.transform_values { |it| call(it) }
|
|
17
17
|
when ARRAY_RXP
|
|
18
|
-
val.split(/\s*,\s*/).map { it
|
|
18
|
+
val.split(/\s*,\s*/).map { |it| call(it) }
|
|
19
19
|
when /\A(true|t|yes|y)\z/i
|
|
20
20
|
true
|
|
21
21
|
when /\A(false|f|no|n)\z/i
|
|
@@ -4,7 +4,6 @@ require "anyway/optparse_config"
|
|
|
4
4
|
require "anyway/dynamic_config"
|
|
5
5
|
|
|
6
6
|
module Anyway # :nodoc:
|
|
7
|
-
using RubyNext
|
|
8
7
|
using Anyway::Ext::DeepDup
|
|
9
8
|
using Anyway::Ext::DeepFreeze
|
|
10
9
|
using Anyway::Ext::Hash
|
|
@@ -154,7 +153,7 @@ module Anyway # :nodoc:
|
|
|
154
153
|
if block
|
|
155
154
|
load_callbacks << BlockCallback.new(block)
|
|
156
155
|
else
|
|
157
|
-
load_callbacks.push(*names.map { it
|
|
156
|
+
load_callbacks.push(*names.map { |it| NamedCallback.new(it) })
|
|
158
157
|
end
|
|
159
158
|
end
|
|
160
159
|
|
|
@@ -261,6 +260,15 @@ module Anyway # :nodoc:
|
|
|
261
260
|
@fallback_type_caster = ::Anyway::NoCast
|
|
262
261
|
end
|
|
263
262
|
|
|
263
|
+
def configuration_sources
|
|
264
|
+
return @configuration_sources if instance_variable_defined?(:@configuration_sources)
|
|
265
|
+
return @configuration_sources = superclass.configuration_sources.dup if superclass.respond_to?(:configuration_sources)
|
|
266
|
+
|
|
267
|
+
@configuration_sources = nil
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
attr_writer :configuration_sources
|
|
271
|
+
|
|
264
272
|
private
|
|
265
273
|
|
|
266
274
|
def define_config_accessor(*names)
|
|
@@ -380,7 +388,7 @@ module Anyway # :nodoc:
|
|
|
380
388
|
trace&.keep_if { |key| self.class.config_attributes.include?(key.to_sym) }
|
|
381
389
|
|
|
382
390
|
# Run on_load callbacks
|
|
383
|
-
self.class.load_callbacks.each { it
|
|
391
|
+
self.class.load_callbacks.each { |it| it.apply_to(self) }
|
|
384
392
|
|
|
385
393
|
# Set trace after we write all the values to
|
|
386
394
|
# avoid changing the source to accessor
|
|
@@ -390,7 +398,10 @@ module Anyway # :nodoc:
|
|
|
390
398
|
end
|
|
391
399
|
|
|
392
400
|
def load_from_sources(base_config, **opts)
|
|
393
|
-
|
|
401
|
+
source_filter = self.class.configuration_sources
|
|
402
|
+
|
|
403
|
+
Anyway.loaders.each do |(id, loader)|
|
|
404
|
+
next if source_filter && !source_filter.include?(id)
|
|
394
405
|
Utils.deep_merge!(base_config, loader.call(**opts))
|
|
395
406
|
end
|
|
396
407
|
base_config
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
using RubyNext
|
|
3
|
+
require "anyway/loaders/base"
|
|
5
4
|
|
|
5
|
+
module Anyway
|
|
6
6
|
module Loaders
|
|
7
|
+
autoload :YAML, "anyway/loaders/yaml"
|
|
8
|
+
autoload :Env, "anyway/loaders/env"
|
|
9
|
+
autoload :EJSON, "anyway/loaders/ejson"
|
|
10
|
+
autoload :Doppler, "anyway/loaders/doppler"
|
|
11
|
+
|
|
7
12
|
class Registry
|
|
8
13
|
attr_reader :registry
|
|
9
14
|
|
|
@@ -55,6 +60,8 @@ module Anyway
|
|
|
55
60
|
registry.each(&block)
|
|
56
61
|
end
|
|
57
62
|
|
|
63
|
+
def keys() ; registry.map(&:first); end
|
|
64
|
+
|
|
58
65
|
def freeze() ; registry.freeze; end
|
|
59
66
|
|
|
60
67
|
private
|
|
@@ -71,9 +78,3 @@ module Anyway
|
|
|
71
78
|
end
|
|
72
79
|
end
|
|
73
80
|
end
|
|
74
|
-
|
|
75
|
-
require "anyway/loaders/base"
|
|
76
|
-
require "anyway/loaders/yaml"
|
|
77
|
-
require "anyway/loaders/env"
|
|
78
|
-
require "anyway/loaders/doppler"
|
|
79
|
-
require "anyway/loaders/ejson"
|
|
@@ -29,7 +29,7 @@ module Anyway
|
|
|
29
29
|
def record_value(val, *path, **opts)
|
|
30
30
|
key = path.pop
|
|
31
31
|
trace = if val.is_a?(Hash)
|
|
32
|
-
Trace.new.tap { it
|
|
32
|
+
Trace.new.tap { |it| it.merge_values(val, **opts) }
|
|
33
33
|
else
|
|
34
34
|
Trace.new(:value, val, **opts)
|
|
35
35
|
end
|
|
@@ -84,7 +84,7 @@ module Anyway
|
|
|
84
84
|
|
|
85
85
|
def to_h
|
|
86
86
|
if trace?
|
|
87
|
-
value.transform_values(&:to_h).tap { it
|
|
87
|
+
value.transform_values(&:to_h).tap { |it| it.default_proc = nil }
|
|
88
88
|
else
|
|
89
89
|
{value: value, source: source}
|
|
90
90
|
end
|
|
@@ -4,7 +4,6 @@ require "anyway/optparse_config"
|
|
|
4
4
|
require "anyway/dynamic_config"
|
|
5
5
|
|
|
6
6
|
module Anyway # :nodoc:
|
|
7
|
-
using RubyNext
|
|
8
7
|
using Anyway::Ext::DeepDup
|
|
9
8
|
using Anyway::Ext::DeepFreeze
|
|
10
9
|
using Anyway::Ext::Hash
|
|
@@ -154,7 +153,7 @@ module Anyway # :nodoc:
|
|
|
154
153
|
if block
|
|
155
154
|
load_callbacks << BlockCallback.new(block)
|
|
156
155
|
else
|
|
157
|
-
load_callbacks.push(*names.map { it
|
|
156
|
+
load_callbacks.push(*names.map { |it| NamedCallback.new(it) })
|
|
158
157
|
end
|
|
159
158
|
end
|
|
160
159
|
|
|
@@ -261,6 +260,15 @@ module Anyway # :nodoc:
|
|
|
261
260
|
@fallback_type_caster = ::Anyway::NoCast
|
|
262
261
|
end
|
|
263
262
|
|
|
263
|
+
def configuration_sources
|
|
264
|
+
return @configuration_sources if instance_variable_defined?(:@configuration_sources)
|
|
265
|
+
return @configuration_sources = superclass.configuration_sources.dup if superclass.respond_to?(:configuration_sources)
|
|
266
|
+
|
|
267
|
+
@configuration_sources = nil
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
attr_writer :configuration_sources
|
|
271
|
+
|
|
264
272
|
private
|
|
265
273
|
|
|
266
274
|
def define_config_accessor(*names)
|
|
@@ -380,7 +388,7 @@ module Anyway # :nodoc:
|
|
|
380
388
|
trace&.keep_if { |key| self.class.config_attributes.include?(key.to_sym) }
|
|
381
389
|
|
|
382
390
|
# Run on_load callbacks
|
|
383
|
-
self.class.load_callbacks.each { it
|
|
391
|
+
self.class.load_callbacks.each { |it| it.apply_to(self) }
|
|
384
392
|
|
|
385
393
|
# Set trace after we write all the values to
|
|
386
394
|
# avoid changing the source to accessor
|
|
@@ -390,7 +398,10 @@ module Anyway # :nodoc:
|
|
|
390
398
|
end
|
|
391
399
|
|
|
392
400
|
def load_from_sources(base_config, **opts)
|
|
393
|
-
|
|
401
|
+
source_filter = self.class.configuration_sources
|
|
402
|
+
|
|
403
|
+
Anyway.loaders.each do |(id, loader)|
|
|
404
|
+
next if source_filter && !source_filter.include?(id)
|
|
394
405
|
Utils.deep_merge!(base_config, loader.call(**opts))
|
|
395
406
|
end
|
|
396
407
|
base_config
|
|
@@ -29,7 +29,7 @@ module Anyway
|
|
|
29
29
|
def record_value(val, *path, **opts)
|
|
30
30
|
key = path.pop
|
|
31
31
|
trace = if val.is_a?(Hash)
|
|
32
|
-
Trace.new.tap { it
|
|
32
|
+
Trace.new.tap { |it| it.merge_values(val, **opts) }
|
|
33
33
|
else
|
|
34
34
|
Trace.new(:value, val, **opts)
|
|
35
35
|
end
|
|
@@ -84,7 +84,7 @@ module Anyway
|
|
|
84
84
|
|
|
85
85
|
def to_h
|
|
86
86
|
if trace?
|
|
87
|
-
value.transform_values(&:to_h).tap { it
|
|
87
|
+
value.transform_values(&:to_h).tap { |it| it.default_proc = nil }
|
|
88
88
|
else
|
|
89
89
|
{value: value, source: source}
|
|
90
90
|
end
|
|
@@ -4,7 +4,6 @@ require "anyway/optparse_config"
|
|
|
4
4
|
require "anyway/dynamic_config"
|
|
5
5
|
|
|
6
6
|
module Anyway # :nodoc:
|
|
7
|
-
using RubyNext
|
|
8
7
|
using Anyway::Ext::DeepDup
|
|
9
8
|
using Anyway::Ext::DeepFreeze
|
|
10
9
|
using Anyway::Ext::Hash
|
|
@@ -154,7 +153,7 @@ module Anyway # :nodoc:
|
|
|
154
153
|
if block
|
|
155
154
|
load_callbacks << BlockCallback.new(block)
|
|
156
155
|
else
|
|
157
|
-
load_callbacks.push(*names.map { it
|
|
156
|
+
load_callbacks.push(*names.map { |it| NamedCallback.new(it) })
|
|
158
157
|
end
|
|
159
158
|
end
|
|
160
159
|
|
|
@@ -261,6 +260,15 @@ module Anyway # :nodoc:
|
|
|
261
260
|
@fallback_type_caster = ::Anyway::NoCast
|
|
262
261
|
end
|
|
263
262
|
|
|
263
|
+
def configuration_sources
|
|
264
|
+
return @configuration_sources if instance_variable_defined?(:@configuration_sources)
|
|
265
|
+
return @configuration_sources = superclass.configuration_sources.dup if superclass.respond_to?(:configuration_sources)
|
|
266
|
+
|
|
267
|
+
@configuration_sources = nil
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
attr_writer :configuration_sources
|
|
271
|
+
|
|
264
272
|
private
|
|
265
273
|
|
|
266
274
|
def define_config_accessor(*names)
|
|
@@ -380,7 +388,7 @@ module Anyway # :nodoc:
|
|
|
380
388
|
trace&.keep_if { |key| self.class.config_attributes.include?(key.to_sym) }
|
|
381
389
|
|
|
382
390
|
# Run on_load callbacks
|
|
383
|
-
self.class.load_callbacks.each { it
|
|
391
|
+
self.class.load_callbacks.each { |it| it.apply_to(self) }
|
|
384
392
|
|
|
385
393
|
# Set trace after we write all the values to
|
|
386
394
|
# avoid changing the source to accessor
|
|
@@ -390,7 +398,10 @@ module Anyway # :nodoc:
|
|
|
390
398
|
end
|
|
391
399
|
|
|
392
400
|
def load_from_sources(base_config, **opts)
|
|
393
|
-
|
|
401
|
+
source_filter = self.class.configuration_sources
|
|
402
|
+
|
|
403
|
+
Anyway.loaders.each do |(id, loader)|
|
|
404
|
+
next if source_filter && !source_filter.include?(id)
|
|
394
405
|
Utils.deep_merge!(base_config, loader.call(**opts))
|
|
395
406
|
end
|
|
396
407
|
base_config
|
|
@@ -29,7 +29,7 @@ module Anyway
|
|
|
29
29
|
def record_value(val, *path, **opts)
|
|
30
30
|
key = path.pop
|
|
31
31
|
trace = if val.is_a?(Hash)
|
|
32
|
-
Trace.new.tap { it
|
|
32
|
+
Trace.new.tap { |it| it.merge_values(val, **opts) }
|
|
33
33
|
else
|
|
34
34
|
Trace.new(:value, val, **opts)
|
|
35
35
|
end
|
|
@@ -84,7 +84,7 @@ module Anyway
|
|
|
84
84
|
|
|
85
85
|
def to_h
|
|
86
86
|
if trace?
|
|
87
|
-
value.transform_values(&:to_h).tap { it
|
|
87
|
+
value.transform_values(&:to_h).tap { |it| it.default_proc = nil }
|
|
88
88
|
else
|
|
89
89
|
{value:, source:}
|
|
90
90
|
end
|
|
@@ -13,9 +13,9 @@ module Anyway
|
|
|
13
13
|
|
|
14
14
|
case val
|
|
15
15
|
when Hash
|
|
16
|
-
val.transform_values { it
|
|
16
|
+
val.transform_values { |it| call(it) }
|
|
17
17
|
when ARRAY_RXP
|
|
18
|
-
val.split(/\s*,\s*/).map { it
|
|
18
|
+
val.split(/\s*,\s*/).map { |it| call(it) }
|
|
19
19
|
when /\A(true|t|yes|y)\z/i
|
|
20
20
|
true
|
|
21
21
|
when /\A(false|f|no|n)\z/i
|
|
@@ -4,7 +4,6 @@ require "anyway/optparse_config"
|
|
|
4
4
|
require "anyway/dynamic_config"
|
|
5
5
|
|
|
6
6
|
module Anyway # :nodoc:
|
|
7
|
-
using RubyNext
|
|
8
7
|
using Anyway::Ext::DeepDup
|
|
9
8
|
using Anyway::Ext::DeepFreeze
|
|
10
9
|
using Anyway::Ext::Hash
|
|
@@ -154,7 +153,7 @@ module Anyway # :nodoc:
|
|
|
154
153
|
if block
|
|
155
154
|
load_callbacks << BlockCallback.new(block)
|
|
156
155
|
else
|
|
157
|
-
load_callbacks.push(*names.map { it
|
|
156
|
+
load_callbacks.push(*names.map { |it| NamedCallback.new(it) })
|
|
158
157
|
end
|
|
159
158
|
end
|
|
160
159
|
|
|
@@ -261,6 +260,15 @@ module Anyway # :nodoc:
|
|
|
261
260
|
@fallback_type_caster = ::Anyway::NoCast
|
|
262
261
|
end
|
|
263
262
|
|
|
263
|
+
def configuration_sources
|
|
264
|
+
return @configuration_sources if instance_variable_defined?(:@configuration_sources)
|
|
265
|
+
return @configuration_sources = superclass.configuration_sources.dup if superclass.respond_to?(:configuration_sources)
|
|
266
|
+
|
|
267
|
+
@configuration_sources = nil
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
attr_writer :configuration_sources
|
|
271
|
+
|
|
264
272
|
private
|
|
265
273
|
|
|
266
274
|
def define_config_accessor(*names)
|
|
@@ -380,7 +388,7 @@ module Anyway # :nodoc:
|
|
|
380
388
|
trace&.keep_if { |key| self.class.config_attributes.include?(key.to_sym) }
|
|
381
389
|
|
|
382
390
|
# Run on_load callbacks
|
|
383
|
-
self.class.load_callbacks.each { it
|
|
391
|
+
self.class.load_callbacks.each { |it| it.apply_to(self) }
|
|
384
392
|
|
|
385
393
|
# Set trace after we write all the values to
|
|
386
394
|
# avoid changing the source to accessor
|
|
@@ -390,7 +398,10 @@ module Anyway # :nodoc:
|
|
|
390
398
|
end
|
|
391
399
|
|
|
392
400
|
def load_from_sources(base_config, **opts)
|
|
393
|
-
|
|
401
|
+
source_filter = self.class.configuration_sources
|
|
402
|
+
|
|
403
|
+
Anyway.loaders.each do |(id, loader)|
|
|
404
|
+
next if source_filter && !source_filter.include?(id)
|
|
394
405
|
Utils.deep_merge!(base_config, loader.call(**opts))
|
|
395
406
|
end
|
|
396
407
|
base_config
|
|
@@ -29,7 +29,7 @@ module Anyway
|
|
|
29
29
|
def record_value(val, *path, **opts)
|
|
30
30
|
key = path.pop
|
|
31
31
|
trace = if val.is_a?(Hash)
|
|
32
|
-
Trace.new.tap { it
|
|
32
|
+
Trace.new.tap { |it| it.merge_values(val, **opts) }
|
|
33
33
|
else
|
|
34
34
|
Trace.new(:value, val, **opts)
|
|
35
35
|
end
|
|
@@ -84,7 +84,7 @@ module Anyway
|
|
|
84
84
|
|
|
85
85
|
def to_h
|
|
86
86
|
if trace?
|
|
87
|
-
value.transform_values(&:to_h).tap { it
|
|
87
|
+
value.transform_values(&:to_h).tap { |it| it.default_proc = nil }
|
|
88
88
|
else
|
|
89
89
|
{value:, source:}
|
|
90
90
|
end
|
data/lib/anyway/config.rb
CHANGED
|
@@ -4,7 +4,6 @@ require "anyway/optparse_config"
|
|
|
4
4
|
require "anyway/dynamic_config"
|
|
5
5
|
|
|
6
6
|
module Anyway # :nodoc:
|
|
7
|
-
using RubyNext
|
|
8
7
|
using Anyway::Ext::DeepDup
|
|
9
8
|
using Anyway::Ext::DeepFreeze
|
|
10
9
|
using Anyway::Ext::Hash
|
|
@@ -261,6 +260,15 @@ module Anyway # :nodoc:
|
|
|
261
260
|
@fallback_type_caster = ::Anyway::NoCast
|
|
262
261
|
end
|
|
263
262
|
|
|
263
|
+
def configuration_sources
|
|
264
|
+
return @configuration_sources if instance_variable_defined?(:@configuration_sources)
|
|
265
|
+
return @configuration_sources = superclass.configuration_sources.dup if superclass.respond_to?(:configuration_sources)
|
|
266
|
+
|
|
267
|
+
@configuration_sources = nil
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
attr_writer :configuration_sources
|
|
271
|
+
|
|
264
272
|
private
|
|
265
273
|
|
|
266
274
|
def define_config_accessor(*names)
|
|
@@ -390,7 +398,10 @@ module Anyway # :nodoc:
|
|
|
390
398
|
end
|
|
391
399
|
|
|
392
400
|
def load_from_sources(base_config, **opts)
|
|
393
|
-
|
|
401
|
+
source_filter = self.class.configuration_sources
|
|
402
|
+
|
|
403
|
+
Anyway.loaders.each do |(id, loader)|
|
|
404
|
+
next if source_filter && !source_filter.include?(id)
|
|
394
405
|
Utils.deep_merge!(base_config, loader.call(**opts))
|
|
395
406
|
end
|
|
396
407
|
base_config
|
data/lib/anyway/env.rb
CHANGED
data/lib/anyway/loaders/env.rb
CHANGED
data/lib/anyway/loaders/yaml.rb
CHANGED
data/lib/anyway/loaders.rb
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
using RubyNext
|
|
3
|
+
require "anyway/loaders/base"
|
|
5
4
|
|
|
5
|
+
module Anyway
|
|
6
6
|
module Loaders
|
|
7
|
+
autoload :YAML, "anyway/loaders/yaml"
|
|
8
|
+
autoload :Env, "anyway/loaders/env"
|
|
9
|
+
autoload :EJSON, "anyway/loaders/ejson"
|
|
10
|
+
autoload :Doppler, "anyway/loaders/doppler"
|
|
11
|
+
|
|
7
12
|
class Registry
|
|
8
13
|
attr_reader :registry
|
|
9
14
|
|
|
@@ -55,6 +60,8 @@ module Anyway
|
|
|
55
60
|
registry.each(&block)
|
|
56
61
|
end
|
|
57
62
|
|
|
63
|
+
def keys() = registry.map(&:first)
|
|
64
|
+
|
|
58
65
|
def freeze() = registry.freeze
|
|
59
66
|
|
|
60
67
|
private
|
|
@@ -71,9 +78,3 @@ module Anyway
|
|
|
71
78
|
end
|
|
72
79
|
end
|
|
73
80
|
end
|
|
74
|
-
|
|
75
|
-
require "anyway/loaders/base"
|
|
76
|
-
require "anyway/loaders/yaml"
|
|
77
|
-
require "anyway/loaders/env"
|
|
78
|
-
require "anyway/loaders/doppler"
|
|
79
|
-
require "anyway/loaders/ejson"
|
|
@@ -19,7 +19,7 @@ module Anyway # :nodoc:
|
|
|
19
19
|
private
|
|
20
20
|
|
|
21
21
|
def option_parser_on_args(key, flag: false, desc: nil, type: ::String)
|
|
22
|
-
on_args = ["--#{key.to_s.tr("_", "-")}#{
|
|
22
|
+
on_args = ["--#{key.to_s.tr("_", "-")}#{" VALUE" unless flag}"]
|
|
23
23
|
on_args << type unless flag
|
|
24
24
|
on_args << desc unless desc.nil?
|
|
25
25
|
on_args
|
data/lib/anyway/version.rb
CHANGED
data/lib/anyway_config.rb
CHANGED
|
@@ -37,10 +37,13 @@ module Anyway # :nodoc:
|
|
|
37
37
|
|
|
38
38
|
# Configure default loaders
|
|
39
39
|
loaders.append :yml, Loaders::YAML
|
|
40
|
-
loaders.append :ejson, Loaders::EJSON if Utils.which("ejson")
|
|
41
40
|
loaders.append :env, Loaders::Env
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
# Configure optional loaders
|
|
43
|
+
if ENV["ANYWAY_CONFIG_DISABLE_EJSON"] != "true" && Utils.which("ejson")
|
|
44
|
+
loaders.append :ejson, Loaders::EJSON
|
|
45
|
+
end
|
|
46
|
+
if ENV["ANYWAY_CONFIG_DISABLE_DOPPLER"] != "true" && ENV.key?("DOPPLER_TOKEN")
|
|
44
47
|
loaders.append :doppler, Loaders::Doppler
|
|
45
48
|
end
|
|
46
49
|
end
|
data/sig/anyway_config.rbs
CHANGED
|
@@ -83,6 +83,8 @@ module Anyway
|
|
|
83
83
|
def self.coerce_types: (**mappingType) -> void
|
|
84
84
|
def self.coercion_mapping: -> Hash[untyped, untyped]?
|
|
85
85
|
def self.disable_auto_cast!: -> void
|
|
86
|
+
def self.configuration_sources: -> Array[Symbol]?
|
|
87
|
+
def self.configuration_sources=: (Array[Symbol]) -> void
|
|
86
88
|
|
|
87
89
|
attr_reader config_name: String
|
|
88
90
|
attr_reader env_prefix: String
|
|
@@ -145,6 +147,7 @@ module Anyway
|
|
|
145
147
|
def override: (Symbol id, _Loader loader) -> void
|
|
146
148
|
| (Symbol id) { (**untyped) -> Hash[untyped, untyped] } -> void
|
|
147
149
|
def delete: (Symbol id) -> void
|
|
150
|
+
def keys: () -> Array[Symbol]
|
|
148
151
|
end
|
|
149
152
|
end
|
|
150
153
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: anyway_config
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vladimir Dementyev
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ruby-next-core
|
|
@@ -121,8 +121,6 @@ files:
|
|
|
121
121
|
- LICENSE.txt
|
|
122
122
|
- README.md
|
|
123
123
|
- lib/.rbnext/2.6/anyway/ejson_parser.rb
|
|
124
|
-
- lib/.rbnext/2.7/anyway/auto_cast.rb
|
|
125
|
-
- lib/.rbnext/2.7/anyway/config.rb
|
|
126
124
|
- lib/.rbnext/2.7/anyway/loaders/yaml.rb
|
|
127
125
|
- lib/.rbnext/2.7/anyway/rbs.rb
|
|
128
126
|
- lib/.rbnext/2.7/anyway/settings.rb
|
|
@@ -214,7 +212,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
214
212
|
requirements:
|
|
215
213
|
- - ">="
|
|
216
214
|
- !ruby/object:Gem::Version
|
|
217
|
-
version: '2.
|
|
215
|
+
version: '2.7'
|
|
218
216
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
219
217
|
requirements:
|
|
220
218
|
- - ">="
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Anyway
|
|
4
|
-
module AutoCast
|
|
5
|
-
# Regexp to detect array values
|
|
6
|
-
# Array value is a values that contains at least one comma
|
|
7
|
-
# and doesn't start/end with quote or curly braces
|
|
8
|
-
ARRAY_RXP = /\A[^'"{].*\s*,\s*.*[^'"}]\z/
|
|
9
|
-
|
|
10
|
-
class << self
|
|
11
|
-
def call(val)
|
|
12
|
-
return val unless val.is_a?(::Hash) || val.is_a?(::String)
|
|
13
|
-
|
|
14
|
-
case val
|
|
15
|
-
when Hash
|
|
16
|
-
val.transform_values { |_1| it = _1;call(it) }
|
|
17
|
-
when ARRAY_RXP
|
|
18
|
-
val.split(/\s*,\s*/).map { |_1| it = _1;call(it) }
|
|
19
|
-
when /\A(true|t|yes|y)\z/i
|
|
20
|
-
true
|
|
21
|
-
when /\A(false|f|no|n)\z/i
|
|
22
|
-
false
|
|
23
|
-
when /\A(nil|null)\z/i
|
|
24
|
-
nil
|
|
25
|
-
when /\A\d+\z/
|
|
26
|
-
val.to_i
|
|
27
|
-
when /\A\d*\.\d+\z/
|
|
28
|
-
val.to_f
|
|
29
|
-
when /\A['"].*['"]\z/
|
|
30
|
-
val.gsub(/(\A['"]|['"]\z)/, "")
|
|
31
|
-
else
|
|
32
|
-
val
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def cast_hash(obj)
|
|
37
|
-
obj.transform_values do |val|
|
|
38
|
-
val.is_a?(::Hash) ? cast_hash(val) : call(val)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def coerce(_key, val)
|
|
43
|
-
call(val)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
module NoCast
|
|
49
|
-
def self.call(val) ; val; end
|
|
50
|
-
|
|
51
|
-
def self.coerce(_key, val) ; val; end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
@@ -1,475 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "anyway/optparse_config"
|
|
4
|
-
require "anyway/dynamic_config"
|
|
5
|
-
|
|
6
|
-
module Anyway # :nodoc:
|
|
7
|
-
using RubyNext
|
|
8
|
-
using Anyway::Ext::DeepDup
|
|
9
|
-
using Anyway::Ext::DeepFreeze
|
|
10
|
-
using Anyway::Ext::Hash
|
|
11
|
-
using Anyway::Ext::FlattenNames
|
|
12
|
-
|
|
13
|
-
using(Module.new do
|
|
14
|
-
refine Object do
|
|
15
|
-
def vm_object_id() ; (object_id << 1).to_s(16); end
|
|
16
|
-
end
|
|
17
|
-
end)
|
|
18
|
-
|
|
19
|
-
# Base config class
|
|
20
|
-
# Provides `attr_config` method to describe
|
|
21
|
-
# configuration parameters and set defaults
|
|
22
|
-
class Config
|
|
23
|
-
PARAM_NAME = /^[a-z_](\w+)?$/
|
|
24
|
-
|
|
25
|
-
# List of names that couldn't be used as config names
|
|
26
|
-
# (the class instance methods we use)
|
|
27
|
-
RESERVED_NAMES = %i[
|
|
28
|
-
config_name
|
|
29
|
-
env_prefix
|
|
30
|
-
as_env
|
|
31
|
-
values
|
|
32
|
-
class
|
|
33
|
-
clear
|
|
34
|
-
deconstruct_keys
|
|
35
|
-
dig
|
|
36
|
-
dup
|
|
37
|
-
initialize
|
|
38
|
-
load
|
|
39
|
-
load_from_sources
|
|
40
|
-
option_parser
|
|
41
|
-
pretty_print
|
|
42
|
-
raise_validation_error
|
|
43
|
-
reload
|
|
44
|
-
resolve_config_path
|
|
45
|
-
tap
|
|
46
|
-
to_h
|
|
47
|
-
to_source_trace
|
|
48
|
-
write_config_attr
|
|
49
|
-
__type_caster__
|
|
50
|
-
].freeze
|
|
51
|
-
|
|
52
|
-
class Error < StandardError; end
|
|
53
|
-
|
|
54
|
-
class ValidationError < Error; end
|
|
55
|
-
|
|
56
|
-
include OptparseConfig
|
|
57
|
-
include DynamicConfig
|
|
58
|
-
|
|
59
|
-
class BlockCallback
|
|
60
|
-
attr_reader :block
|
|
61
|
-
|
|
62
|
-
def initialize(block)
|
|
63
|
-
@block = block
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def apply_to(config)
|
|
67
|
-
config.instance_exec(&block)
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
class NamedCallback
|
|
72
|
-
attr_reader :name
|
|
73
|
-
|
|
74
|
-
def initialize(name)
|
|
75
|
-
@name = name
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def apply_to(config) ; config.send(name); end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
class << self
|
|
82
|
-
def attr_config(*args, **hargs)
|
|
83
|
-
new_defaults = hargs.deep_dup
|
|
84
|
-
new_defaults.stringify_keys!
|
|
85
|
-
|
|
86
|
-
defaults.merge! new_defaults
|
|
87
|
-
|
|
88
|
-
new_keys = ((args + new_defaults.keys) - config_attributes)
|
|
89
|
-
|
|
90
|
-
validate_param_names! new_keys.map(&:to_s)
|
|
91
|
-
|
|
92
|
-
new_keys.map!(&:to_sym)
|
|
93
|
-
|
|
94
|
-
unless (reserved_names = (new_keys & RESERVED_NAMES)).empty?
|
|
95
|
-
raise ArgumentError, "Can not use the following reserved names as config attrubutes: " \
|
|
96
|
-
"#{reserved_names.sort.map(&:to_s).join(", ")}"
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
config_attributes.push(*new_keys)
|
|
100
|
-
|
|
101
|
-
define_config_accessor(*new_keys)
|
|
102
|
-
|
|
103
|
-
# Define predicate methods ("param?") for attributes
|
|
104
|
-
# having `true` or `false` as default values
|
|
105
|
-
new_defaults.each do |key, val|
|
|
106
|
-
next unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
|
107
|
-
alias_method :"#{key}?", :"#{key}"
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def defaults
|
|
112
|
-
return @defaults if instance_variable_defined?(:@defaults)
|
|
113
|
-
|
|
114
|
-
@defaults = if superclass < Anyway::Config
|
|
115
|
-
superclass.defaults.deep_dup
|
|
116
|
-
else
|
|
117
|
-
new_empty_config
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def config_attributes
|
|
122
|
-
return @config_attributes if instance_variable_defined?(:@config_attributes)
|
|
123
|
-
|
|
124
|
-
@config_attributes = if superclass < Anyway::Config
|
|
125
|
-
superclass.config_attributes.dup
|
|
126
|
-
else
|
|
127
|
-
[]
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def required(*names, env: nil, **nested)
|
|
132
|
-
unknown_names = names + nested.keys - config_attributes
|
|
133
|
-
raise ArgumentError, "Unknown config param: #{unknown_names.join(",")}" if unknown_names.any?
|
|
134
|
-
|
|
135
|
-
return unless Settings.matching_env?(env)
|
|
136
|
-
|
|
137
|
-
required_attributes.push(*names)
|
|
138
|
-
required_attributes.push(*nested.flatten_names)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def required_attributes
|
|
142
|
-
return @required_attributes if instance_variable_defined?(:@required_attributes)
|
|
143
|
-
|
|
144
|
-
@required_attributes = if superclass < Anyway::Config
|
|
145
|
-
superclass.required_attributes.dup
|
|
146
|
-
else
|
|
147
|
-
[]
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def on_load(*names, &block)
|
|
152
|
-
raise ArgumentError, "Either methods or block should be specified, not both" if block && !names.empty?
|
|
153
|
-
|
|
154
|
-
if block
|
|
155
|
-
load_callbacks << BlockCallback.new(block)
|
|
156
|
-
else
|
|
157
|
-
load_callbacks.push(*names.map { |_1| it = _1;NamedCallback.new(it) })
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def load_callbacks
|
|
162
|
-
return @load_callbacks if instance_variable_defined?(:@load_callbacks)
|
|
163
|
-
|
|
164
|
-
@load_callbacks = if superclass <= Anyway::Config
|
|
165
|
-
superclass.load_callbacks.dup
|
|
166
|
-
else
|
|
167
|
-
[]
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def config_name(val = nil)
|
|
172
|
-
return (@explicit_config_name = val.to_s) unless val.nil?
|
|
173
|
-
|
|
174
|
-
return @config_name if instance_variable_defined?(:@config_name)
|
|
175
|
-
|
|
176
|
-
@config_name = explicit_config_name || build_config_name
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def explicit_config_name
|
|
180
|
-
return @explicit_config_name if instance_variable_defined?(:@explicit_config_name)
|
|
181
|
-
|
|
182
|
-
@explicit_config_name =
|
|
183
|
-
if superclass.respond_to?(:explicit_config_name)
|
|
184
|
-
superclass.explicit_config_name
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def explicit_config_name?() ; !explicit_config_name.nil?; end
|
|
189
|
-
|
|
190
|
-
def env_prefix(val = nil)
|
|
191
|
-
return (@env_prefix = val.to_s.upcase) unless val.nil?
|
|
192
|
-
|
|
193
|
-
return @env_prefix if instance_variable_defined?(:@env_prefix)
|
|
194
|
-
|
|
195
|
-
@env_prefix = if superclass < Anyway::Config && superclass.explicit_config_name?
|
|
196
|
-
superclass.env_prefix
|
|
197
|
-
else
|
|
198
|
-
config_name.upcase
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def loader_options(val = nil)
|
|
203
|
-
return (@loader_options = val) unless val.nil?
|
|
204
|
-
|
|
205
|
-
return @loader_options if instance_variable_defined?(:@loader_options)
|
|
206
|
-
|
|
207
|
-
@loader_options = if superclass < Anyway::Config
|
|
208
|
-
superclass.loader_options
|
|
209
|
-
else
|
|
210
|
-
{}
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
def new_empty_config() ; {}; end
|
|
215
|
-
|
|
216
|
-
def coerce_types(mapping)
|
|
217
|
-
Utils.deep_merge!(coercion_mapping, mapping)
|
|
218
|
-
|
|
219
|
-
mapping.each do |key, val|
|
|
220
|
-
type = val.is_a?(::Hash) ? val[:type] : val
|
|
221
|
-
next if type != :boolean
|
|
222
|
-
|
|
223
|
-
alias_method :"#{key}?", :"#{key}"
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
def coercion_mapping
|
|
228
|
-
return @coercion_mapping if instance_variable_defined?(:@coercion_mapping)
|
|
229
|
-
|
|
230
|
-
@coercion_mapping = if superclass < Anyway::Config
|
|
231
|
-
superclass.coercion_mapping.deep_dup
|
|
232
|
-
else
|
|
233
|
-
{}
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
def type_caster(val = nil)
|
|
238
|
-
return @type_caster unless val.nil?
|
|
239
|
-
|
|
240
|
-
@type_caster ||=
|
|
241
|
-
if coercion_mapping.empty?
|
|
242
|
-
fallback_type_caster
|
|
243
|
-
else
|
|
244
|
-
::Anyway::TypeCaster.new(coercion_mapping, fallback: fallback_type_caster)
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def fallback_type_caster(val = nil)
|
|
249
|
-
return (@fallback_type_caster = val) unless val.nil?
|
|
250
|
-
|
|
251
|
-
return @fallback_type_caster if instance_variable_defined?(:@fallback_type_caster)
|
|
252
|
-
|
|
253
|
-
@fallback_type_caster = if superclass < Anyway::Config
|
|
254
|
-
superclass.fallback_type_caster.deep_dup
|
|
255
|
-
else
|
|
256
|
-
::Anyway::AutoCast
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
def disable_auto_cast!
|
|
261
|
-
@fallback_type_caster = ::Anyway::NoCast
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
private
|
|
265
|
-
|
|
266
|
-
def define_config_accessor(*names)
|
|
267
|
-
names.each do |name|
|
|
268
|
-
accessors_module.module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
269
|
-
def #{name}=(val)
|
|
270
|
-
__trace__&.record_value(val, "#{name}", **Tracing.current_trace_source)
|
|
271
|
-
values[:#{name}] = val
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def #{name}
|
|
275
|
-
values[:#{name}]
|
|
276
|
-
end
|
|
277
|
-
RUBY
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def accessors_module
|
|
282
|
-
return @accessors_module if instance_variable_defined?(:@accessors_module)
|
|
283
|
-
|
|
284
|
-
@accessors_module = Module.new.tap do |mod|
|
|
285
|
-
include mod
|
|
286
|
-
end
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
def build_config_name
|
|
290
|
-
unless name
|
|
291
|
-
raise "Please, specify config name explicitly for anonymous class " \
|
|
292
|
-
"via `config_name :my_config`"
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
# handle two cases:
|
|
296
|
-
# - SomeModule::Config => "some_module"
|
|
297
|
-
# - SomeConfig => "some"
|
|
298
|
-
unless name =~ /^(\w+)(::)?Config$/
|
|
299
|
-
raise "Couldn't infer config name, please, specify it explicitly " \
|
|
300
|
-
"via `config_name :my_config`"
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
# TODO(v3.0): Replace downcase with underscore
|
|
304
|
-
Regexp.last_match[1].tap(&:downcase!)
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
def validate_param_names!(names)
|
|
308
|
-
invalid_names = names.reject { |name| name =~ PARAM_NAME }
|
|
309
|
-
return if invalid_names.empty?
|
|
310
|
-
|
|
311
|
-
raise ArgumentError, "Invalid attr_config name: #{invalid_names.join(", ")}.\n" \
|
|
312
|
-
"Valid names must satisfy /#{PARAM_NAME.source}/."
|
|
313
|
-
end
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
on_load :validate_required_attributes!
|
|
317
|
-
|
|
318
|
-
attr_reader :config_name, :env_prefix
|
|
319
|
-
|
|
320
|
-
# Instantiate config instance.
|
|
321
|
-
#
|
|
322
|
-
# Example:
|
|
323
|
-
#
|
|
324
|
-
# my_config = Anyway::Config.new()
|
|
325
|
-
#
|
|
326
|
-
# # provide some values explicitly
|
|
327
|
-
# my_config = Anyway::Config.new({some: :value})
|
|
328
|
-
#
|
|
329
|
-
def initialize(overrides = nil)
|
|
330
|
-
@config_name = self.class.config_name
|
|
331
|
-
|
|
332
|
-
raise ArgumentError, "Config name is missing" unless @config_name
|
|
333
|
-
|
|
334
|
-
@env_prefix = self.class.env_prefix
|
|
335
|
-
@values = {}
|
|
336
|
-
|
|
337
|
-
load(overrides)
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
def reload(overrides = nil)
|
|
341
|
-
clear
|
|
342
|
-
load(overrides)
|
|
343
|
-
self
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
def clear
|
|
347
|
-
values.clear
|
|
348
|
-
@__trace__ = nil
|
|
349
|
-
self
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
def load(overrides = nil)
|
|
353
|
-
base_config = self.class.defaults.deep_dup
|
|
354
|
-
|
|
355
|
-
trace = Tracing.capture do
|
|
356
|
-
Tracing.trace!(:defaults) { base_config }
|
|
357
|
-
|
|
358
|
-
config_path = resolve_config_path(config_name, env_prefix)
|
|
359
|
-
|
|
360
|
-
load_from_sources(
|
|
361
|
-
base_config,
|
|
362
|
-
name: config_name,
|
|
363
|
-
env_prefix: env_prefix,
|
|
364
|
-
config_path: config_path,
|
|
365
|
-
**self.class.loader_options
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
if overrides
|
|
369
|
-
Tracing.trace!(:load) { overrides }
|
|
370
|
-
|
|
371
|
-
Utils.deep_merge!(base_config, overrides)
|
|
372
|
-
end
|
|
373
|
-
end
|
|
374
|
-
|
|
375
|
-
base_config.each do |key, val|
|
|
376
|
-
write_config_attr(key.to_sym, val)
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
# Trace may contain unknown attributes
|
|
380
|
-
trace&.keep_if { |key| self.class.config_attributes.include?(key.to_sym) }
|
|
381
|
-
|
|
382
|
-
# Run on_load callbacks
|
|
383
|
-
self.class.load_callbacks.each { |_1| it = _1;it.apply_to(self) }
|
|
384
|
-
|
|
385
|
-
# Set trace after we write all the values to
|
|
386
|
-
# avoid changing the source to accessor
|
|
387
|
-
@__trace__ = trace
|
|
388
|
-
|
|
389
|
-
self
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
def load_from_sources(base_config, **opts)
|
|
393
|
-
Anyway.loaders.each do |(_id, loader)|
|
|
394
|
-
Utils.deep_merge!(base_config, loader.call(**opts))
|
|
395
|
-
end
|
|
396
|
-
base_config
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
def dig(*__rest__) ; values.dig(*__rest__); end
|
|
400
|
-
|
|
401
|
-
def to_h() ; values.deep_dup.deep_freeze; end
|
|
402
|
-
|
|
403
|
-
def dup
|
|
404
|
-
self.class.allocate.tap do |new_config|
|
|
405
|
-
%i[config_name env_prefix __trace__].each do |ivar|
|
|
406
|
-
new_config.instance_variable_set(:"@#{ivar}", send(ivar).dup)
|
|
407
|
-
end
|
|
408
|
-
new_config.instance_variable_set(:@values, values.deep_dup)
|
|
409
|
-
end
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
def resolve_config_path(name, env_prefix)
|
|
413
|
-
Anyway.env.fetch(env_prefix).delete("conf") || Settings.default_config_path.call(name)
|
|
414
|
-
end
|
|
415
|
-
|
|
416
|
-
def deconstruct_keys(keys) ; values.deconstruct_keys(keys); end
|
|
417
|
-
|
|
418
|
-
def to_source_trace() ; __trace__&.to_h; end
|
|
419
|
-
|
|
420
|
-
def inspect
|
|
421
|
-
"#<#{self.class}:0x#{vm_object_id.rjust(16, "0")} config_name=\"#{config_name}\" env_prefix=\"#{env_prefix}\" " \
|
|
422
|
-
"values=#{values.inspect}>"
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
def pretty_print(q)
|
|
426
|
-
q.object_group self do
|
|
427
|
-
q.nest(1) do
|
|
428
|
-
q.breakable
|
|
429
|
-
q.text "config_name=#{config_name.inspect}"
|
|
430
|
-
q.breakable
|
|
431
|
-
q.text "env_prefix=#{env_prefix.inspect}"
|
|
432
|
-
q.breakable
|
|
433
|
-
q.text "values:"
|
|
434
|
-
q.pp __trace__
|
|
435
|
-
end
|
|
436
|
-
end
|
|
437
|
-
end
|
|
438
|
-
|
|
439
|
-
def as_env
|
|
440
|
-
Env.from_hash(to_h, prefix: env_prefix)
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
private
|
|
444
|
-
|
|
445
|
-
attr_reader :values, :__trace__
|
|
446
|
-
|
|
447
|
-
def validate_required_attributes!
|
|
448
|
-
return if Settings.suppress_required_validations
|
|
449
|
-
|
|
450
|
-
self.class.required_attributes.select do |name|
|
|
451
|
-
val = values.dig(*name.to_s.split(".").map(&:to_sym))
|
|
452
|
-
val.nil? || (val.is_a?(String) && val.empty?)
|
|
453
|
-
end.then do |missing|
|
|
454
|
-
next if missing.empty?
|
|
455
|
-
raise_validation_error "The following config parameters for `#{self.class.name}(config_name: #{self.class.config_name})` are missing or empty: #{missing.join(", ")}"
|
|
456
|
-
end
|
|
457
|
-
end
|
|
458
|
-
|
|
459
|
-
def write_config_attr(key, val)
|
|
460
|
-
key = key.to_sym
|
|
461
|
-
return unless self.class.config_attributes.include?(key)
|
|
462
|
-
|
|
463
|
-
val = __type_caster__.coerce(key, val)
|
|
464
|
-
public_send(:"#{key}=", val)
|
|
465
|
-
end
|
|
466
|
-
|
|
467
|
-
def raise_validation_error(msg)
|
|
468
|
-
raise ValidationError, msg
|
|
469
|
-
end
|
|
470
|
-
|
|
471
|
-
def __type_caster__
|
|
472
|
-
self.class.type_caster
|
|
473
|
-
end
|
|
474
|
-
end
|
|
475
|
-
end
|