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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4687cf9a7a8ee20387726a47452a2e2b204742096d7070885f176e53e1fc285
4
- data.tar.gz: 36dc3777a2cce19788c0e189f740f0dccc708a02bf240a5ded89533e85929f6e
3
+ metadata.gz: 414ea061d65832bc84e8cd08504abbea04e356431f2e85210fb458780bc9623b
4
+ data.tar.gz: 5ebbb3638e8ad4b870de6a8a8d3a35560a0cc4f0f13095da950e50aeb04dc928
5
5
  SHA512:
6
- metadata.gz: bf601ef44033901a08350ba687b0d9f7ad112160b3cc6a6058168fe44e7093ef6cf1e28c382feb7c70fd0177b27b2d79cf893bf93a1ccb9b61143624ebd49f32
7
- data.tar.gz: af6505ce86371203fb4e1744123191904b99b837892ff6b79fd722f051ed1f2b01fcbb0075bcb92f0e20ff07cea56597262987145dea1ba20d36732b99ae4059
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.5.0
109
- - JRuby >= 9.2.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 >= 5.0; for Rails 4.x use version 1.x of the gem.
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.
@@ -3,7 +3,6 @@
3
3
  require "pathname"
4
4
  require "anyway/ext/hash"
5
5
 
6
- using RubyNext
7
6
  using Anyway::Ext::Hash
8
7
 
9
8
  module Anyway
@@ -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 { |_1| it = _1;it.merge_values(val, **opts) }
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 { |_1| it = _1;it.default_proc = nil }
87
+ value.transform_values(&:to_h).tap { |it| it.default_proc = nil }
88
88
  else
89
89
  {value: value, source: source}
90
90
  end
@@ -34,7 +34,7 @@ module Anyway
34
34
 
35
35
  if array
36
36
  raw_arr = raw.is_a?(String) ? raw.split(/\s*,\s*/) : Array(raw)
37
- raw_arr.map { |_1| it = _1;caster.call(it) }
37
+ raw_arr.map { |it| caster.call(it) }
38
38
  else
39
39
  caster.call(raw)
40
40
  end
@@ -13,9 +13,9 @@ module Anyway
13
13
 
14
14
  case val
15
15
  when Hash
16
- val.transform_values { it = _1;call(it) }
16
+ val.transform_values { |it| call(it) }
17
17
  when ARRAY_RXP
18
- val.split(/\s*,\s*/).map { it = _1;call(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 = _1;NamedCallback.new(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 = _1;it.apply_to(self) }
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
- Anyway.loaders.each do |(_id, loader)|
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
@@ -3,7 +3,6 @@
3
3
  require "pathname"
4
4
  require "anyway/ext/hash"
5
5
 
6
- using RubyNext
7
6
  using Anyway::Ext::Hash
8
7
 
9
8
  module Anyway
@@ -1,9 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Anyway
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 = _1;it.merge_values(val, **opts) }
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 = _1;it.default_proc = nil }
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 = _1;NamedCallback.new(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 = _1;it.apply_to(self) }
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
- Anyway.loaders.each do |(_id, loader)|
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
@@ -4,7 +4,6 @@ module Anyway
4
4
  # Parses environment variables and provides
5
5
  # method-like access
6
6
  class Env
7
- using RubyNext
8
7
  using Anyway::Ext::DeepDup
9
8
  using Anyway::Ext::Hash
10
9
 
@@ -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 = _1;it.merge_values(val, **opts) }
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 = _1;it.default_proc = nil }
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 = _1;NamedCallback.new(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 = _1;it.apply_to(self) }
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
- Anyway.loaders.each do |(_id, loader)|
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 = _1;it.merge_values(val, **opts) }
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 = _1;it.default_proc = nil }
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 = _1;call(it) }
16
+ val.transform_values { |it| call(it) }
17
17
  when ARRAY_RXP
18
- val.split(/\s*,\s*/).map { it = _1;call(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 = _1;NamedCallback.new(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 = _1;it.apply_to(self) }
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
- Anyway.loaders.each do |(_id, loader)|
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 = _1;it.merge_values(val, **opts) }
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 = _1;it.default_proc = nil }
87
+ value.transform_values(&:to_h).tap { |it| it.default_proc = nil }
88
88
  else
89
89
  {value:, source:}
90
90
  end
@@ -34,7 +34,7 @@ module Anyway
34
34
 
35
35
  if array
36
36
  raw_arr = raw.is_a?(String) ? raw.split(/\s*,\s*/) : Array(raw)
37
- raw_arr.map { it = _1;caster.call(it) }
37
+ raw_arr.map { |it| caster.call(it) }
38
38
  else
39
39
  caster.call(raw)
40
40
  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
- Anyway.loaders.each do |(_id, loader)|
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
@@ -4,7 +4,6 @@ module Anyway
4
4
  # Parses environment variables and provides
5
5
  # method-like access
6
6
  class Env
7
- using RubyNext
8
7
  using Anyway::Ext::DeepDup
9
8
  using Anyway::Ext::Hash
10
9
 
@@ -5,8 +5,6 @@ require "net/http"
5
5
  require "json"
6
6
 
7
7
  module Anyway
8
- using RubyNext
9
-
10
8
  module Loaders
11
9
  class Doppler < Base
12
10
  class RequestError < StandardError; end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anyway
4
- using RubyNext
5
-
6
4
  module Loaders
7
5
  class Env < Base
8
6
  def call(env_prefix:, **_options)
@@ -3,7 +3,6 @@
3
3
  require "pathname"
4
4
  require "anyway/ext/hash"
5
5
 
6
- using RubyNext
7
6
  using Anyway::Ext::Hash
8
7
 
9
8
  module Anyway
@@ -1,9 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Anyway
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("_", "-")}#{flag ? "" : " VALUE"}"]
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
@@ -4,8 +4,6 @@
4
4
  # when Anyway Config is loaded before Rails (e.g., in config/puma.rb).
5
5
  module Anyway
6
6
  module Rails
7
- using RubyNext
8
-
9
7
  class << self
10
8
  attr_reader :tracer, :name_method
11
9
  attr_accessor :disable_postponed_load_warning
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anyway
4
- using RubyNext
5
-
6
4
  module Rails
7
5
  module Loaders
8
6
  class Credentials < Anyway::Loaders::Base
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anyway
4
- using RubyNext
5
-
6
4
  module Rails
7
5
  module Loaders
8
6
  class Secrets < Anyway::Loaders::Base
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anyway # :nodoc:
4
- VERSION = "2.7.2"
4
+ VERSION = "2.8.0"
5
5
  end
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
- if ENV.key?("DOPPLER_TOKEN") && ENV["ANYWAY_CONFIG_DISABLE_DOPPLER"] != "true"
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
@@ -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.7.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: 2025-04-01 00:00:00.000000000 Z
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.5'
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