anyway_config 2.0.6 → 2.2.2

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +241 -181
  3. data/README.md +237 -12
  4. data/lib/.rbnext/1995.next/anyway/config.rb +438 -0
  5. data/lib/.rbnext/1995.next/anyway/dynamic_config.rb +31 -0
  6. data/lib/.rbnext/1995.next/anyway/env.rb +56 -0
  7. data/lib/.rbnext/1995.next/anyway/loaders/base.rb +21 -0
  8. data/lib/.rbnext/1995.next/anyway/tracing.rb +181 -0
  9. data/lib/.rbnext/2.7/anyway/auto_cast.rb +39 -19
  10. data/lib/.rbnext/2.7/anyway/config.rb +60 -15
  11. data/lib/.rbnext/2.7/anyway/rails/loaders/yaml.rb +30 -0
  12. data/lib/.rbnext/2.7/anyway/rbs.rb +92 -0
  13. data/lib/.rbnext/2.7/anyway/settings.rb +79 -0
  14. data/lib/.rbnext/2.7/anyway/tracing.rb +1 -1
  15. data/lib/.rbnext/2.7/anyway/type_casting.rb +143 -0
  16. data/lib/.rbnext/3.0/anyway/auto_cast.rb +53 -0
  17. data/lib/.rbnext/{2.8 → 3.0}/anyway/config.rb +60 -15
  18. data/lib/.rbnext/{2.8 → 3.0}/anyway/loaders/base.rb +0 -0
  19. data/lib/.rbnext/{2.8 → 3.0}/anyway/loaders.rb +0 -0
  20. data/lib/.rbnext/{2.8 → 3.0}/anyway/tracing.rb +1 -1
  21. data/lib/anyway/auto_cast.rb +39 -19
  22. data/lib/anyway/config.rb +74 -29
  23. data/lib/anyway/dynamic_config.rb +6 -2
  24. data/lib/anyway/env.rb +1 -1
  25. data/lib/anyway/ext/deep_dup.rb +12 -0
  26. data/lib/anyway/ext/hash.rb +0 -12
  27. data/lib/anyway/loaders/base.rb +1 -1
  28. data/lib/anyway/loaders/env.rb +3 -1
  29. data/lib/anyway/loaders/yaml.rb +9 -5
  30. data/lib/anyway/option_parser_builder.rb +1 -3
  31. data/lib/anyway/optparse_config.rb +5 -7
  32. data/lib/anyway/rails/loaders/credentials.rb +4 -4
  33. data/lib/anyway/rails/loaders/secrets.rb +6 -8
  34. data/lib/anyway/rails/loaders/yaml.rb +11 -0
  35. data/lib/anyway/rails/settings.rb +9 -2
  36. data/lib/anyway/rbs.rb +92 -0
  37. data/lib/anyway/settings.rb +52 -2
  38. data/lib/anyway/tracing.rb +6 -6
  39. data/lib/anyway/type_casting.rb +134 -0
  40. data/lib/anyway/utils/deep_merge.rb +21 -0
  41. data/lib/anyway/version.rb +1 -1
  42. data/lib/anyway_config.rb +4 -0
  43. data/sig/anyway_config.rbs +129 -0
  44. metadata +42 -15
  45. data/lib/.rbnext/2.7/anyway/option_parser_builder.rb +0 -31
data/lib/anyway/env.rb CHANGED
@@ -49,7 +49,7 @@ module Anyway
49
49
  path = key.sub(/^#{prefix}_/, "").downcase
50
50
 
51
51
  paths = path.split("__")
52
- trace!(:env, *paths, key: key) { data.bury(type_cast.call(val), *paths) }
52
+ trace!(:env, *paths, key:) { data.bury(type_cast.call(val), *paths) }
53
53
  end
54
54
  end
55
55
  end
@@ -30,6 +30,18 @@ module Anyway
30
30
  end
31
31
  end
32
32
 
33
+ refine ::Object do
34
+ def deep_dup
35
+ dup
36
+ end
37
+ end
38
+
39
+ refine ::Module do
40
+ def deep_dup
41
+ self
42
+ end
43
+ end
44
+
33
45
  using self
34
46
  end
35
47
  end
@@ -5,18 +5,6 @@ module Anyway
5
5
  # Extend Hash through refinements
6
6
  module Hash
7
7
  refine ::Hash do
8
- # From ActiveSupport http://api.rubyonrails.org/classes/Hash.html#method-i-deep_merge
9
- def deep_merge!(other_hash)
10
- merge!(other_hash) do |key, this_value, other_value|
11
- if this_value.is_a?(::Hash) && other_value.is_a?(::Hash)
12
- this_value.deep_merge!(other_value)
13
- this_value
14
- else
15
- other_value
16
- end
17
- end
18
- end
19
-
20
8
  def stringify_keys!
21
9
  keys.each do |key|
22
10
  value = delete(key)
@@ -7,7 +7,7 @@ module Anyway
7
7
 
8
8
  class << self
9
9
  def call(local: Anyway::Settings.use_local_files, **opts)
10
- new(local: local).call(**opts)
10
+ new(local:).call(**opts)
11
11
  end
12
12
  end
13
13
 
@@ -6,7 +6,9 @@ module Anyway
6
6
  module Loaders
7
7
  class Env < Base
8
8
  def call(env_prefix:, **_options)
9
- Anyway.env.fetch_with_trace(env_prefix).then do |(conf, trace)|
9
+ env = ::Anyway::Env.new(type_cast: ::Anyway::NoCast)
10
+
11
+ env.fetch_with_trace(env_prefix).then do |(conf, trace)|
10
12
  Tracing.current_trace&.merge!(trace)
11
13
  conf
12
14
  end
@@ -17,7 +17,7 @@ module Anyway
17
17
  local_path = local_config_path(config_path)
18
18
  local_config = trace!(:yml, path: relative_config_path(local_path).to_s) { load_local_yml(local_path) }
19
19
 
20
- base_config.deep_merge!(local_config)
20
+ Utils.deep_merge!(base_config, local_config)
21
21
  end
22
22
 
23
23
  private
@@ -25,15 +25,19 @@ module Anyway
25
25
  def parse_yml(path)
26
26
  return {} unless File.file?(path)
27
27
  require "yaml" unless defined?(::YAML)
28
+
29
+ # By default, YAML load will return `false` when the yaml document is
30
+ # empty. When this occurs, we return an empty hash instead, to match
31
+ # the interface when no config file is present.
28
32
  if defined?(ERB)
29
- ::YAML.load(ERB.new(File.read(path)).result) # rubocop:disable Security/YAMLLoad
33
+ ::YAML.load(ERB.new(File.read(path)).result) || {} # rubocop:disable Security/YAMLLoad
30
34
  else
31
- ::YAML.load_file(path)
35
+ ::YAML.load_file(path) || {}
32
36
  end
33
37
  end
34
38
 
35
- alias load_base_yml parse_yml
36
- alias load_local_yml parse_yml
39
+ alias_method :load_base_yml, :parse_yml
40
+ alias_method :load_local_yml, :parse_yml
37
41
 
38
42
  def local_config_path(path)
39
43
  path.sub(/\.yml/, ".local.yml")
@@ -8,8 +8,6 @@ module Anyway # :nodoc:
8
8
  class << self
9
9
  def call(options)
10
10
  OptionParser.new do |opts|
11
- opts.accept(AutoCast) { AutoCast.call(_1) }
12
-
13
11
  options.each do |key, descriptor|
14
12
  opts.on(*option_parser_on_args(key, **descriptor)) do |val|
15
13
  yield [key, val]
@@ -20,7 +18,7 @@ module Anyway # :nodoc:
20
18
 
21
19
  private
22
20
 
23
- def option_parser_on_args(key, flag: false, desc: nil, type: AutoCast)
21
+ def option_parser_on_args(key, flag: false, desc: nil, type: ::String)
24
22
  on_args = ["--#{key.to_s.tr("_", "-")}#{flag ? "" : " VALUE"}"]
25
23
  on_args << type unless flag
26
24
  on_args << desc unless desc.nil?
@@ -70,13 +70,11 @@ module Anyway
70
70
  end
71
71
 
72
72
  def option_parser
73
- @option_parser ||= begin
74
- OptionParserBuilder.call(self.class.option_parser_options) do |key, val|
75
- write_config_attr(key, val)
76
- end.tap do |parser|
77
- self.class.option_parser_extensions.map do |extension|
78
- extension.call(parser, self)
79
- end
73
+ @option_parser ||= OptionParserBuilder.call(self.class.option_parser_options) do |key, val|
74
+ write_config_attr(key, val)
75
+ end.tap do |parser|
76
+ self.class.option_parser_extensions.map do |extension|
77
+ extension.call(parser, self)
80
78
  end
81
79
  end
82
80
  end
@@ -22,15 +22,15 @@ module Anyway
22
22
  :credentials,
23
23
  store: credentials_path
24
24
  ) do
25
- ::Rails.application.credentials.public_send(name)
25
+ ::Rails.application.credentials.config[name.to_sym]
26
26
  end.then do |creds|
27
- config.deep_merge!(creds) if creds
27
+ Utils.deep_merge!(config, creds) if creds
28
28
  end
29
29
 
30
30
  if use_local?
31
31
  trace!(:credentials, store: LOCAL_CONTENT_PATH) do
32
32
  local_credentials(name)
33
- end.then { |creds| config.deep_merge!(creds) if creds }
33
+ end.then { |creds| Utils.deep_merge!(config, creds) if creds }
34
34
  end
35
35
 
36
36
  config
@@ -48,7 +48,7 @@ module Anyway
48
48
  key_path: ::Rails.root.join("config/credentials/local.key")
49
49
  )
50
50
 
51
- creds.public_send(name)
51
+ creds.config[name.to_sym]
52
52
  end
53
53
 
54
54
  def credentials_path
@@ -15,7 +15,7 @@ module Anyway
15
15
  trace!(:secrets) do
16
16
  secrets.public_send(name)
17
17
  end.then do |secrets|
18
- config.deep_merge!(secrets) if secrets
18
+ Utils.deep_merge!(config, secrets) if secrets
19
19
  end
20
20
 
21
21
  config
@@ -24,13 +24,11 @@ module Anyway
24
24
  private
25
25
 
26
26
  def secrets
27
- @secrets ||= begin
28
- ::Rails.application.secrets.tap do |_|
29
- # Reset secrets state if the app hasn't been initialized
30
- # See https://github.com/palkan/anyway_config/issues/14
31
- next if ::Rails.application.initialized?
32
- ::Rails.application.remove_instance_variable(:@secrets)
33
- end
27
+ @secrets ||= ::Rails.application.secrets.tap do |_|
28
+ # Reset secrets state if the app hasn't been initialized
29
+ # See https://github.com/palkan/anyway_config/issues/14
30
+ next if ::Rails.application.initialized?
31
+ ::Rails.application.remove_instance_variable(:@secrets)
34
32
  end
35
33
  end
36
34
  end
@@ -5,11 +5,22 @@ module Anyway
5
5
  module Loaders
6
6
  class YAML < Anyway::Loaders::YAML
7
7
  def load_base_yml(*)
8
+ parsed_yml = super
9
+ return parsed_yml unless environmental?(parsed_yml)
10
+
8
11
  super[::Rails.env] || {}
9
12
  end
10
13
 
11
14
  private
12
15
 
16
+ def environmental?(parsed_yml)
17
+ return true unless Settings.future.unwrap_known_environments
18
+ # likely
19
+ return true if parsed_yml.key?(::Rails.env)
20
+ # less likely
21
+ ::Rails.application.config.anyway_config.known_environments.any? { parsed_yml.key?(_1) }
22
+ end
23
+
13
24
  def relative_config_path(path)
14
25
  Pathname.new(path).relative_path_from(::Rails.root)
15
26
  end
@@ -9,8 +9,13 @@ end
9
9
 
10
10
  module Anyway
11
11
  class Settings
12
+ class Future
13
+ setting :unwrap_known_environments, true
14
+ end
15
+
12
16
  class << self
13
17
  attr_reader :autoload_static_config_path, :autoloader
18
+ attr_accessor :known_environments
14
19
 
15
20
  if defined?(::Zeitwerk)
16
21
  def autoload_static_config_path=(val)
@@ -22,10 +27,11 @@ module Anyway
22
27
 
23
28
  @autoload_static_config_path = val
24
29
 
25
- # See https://github.com/rails/rails/blob/8ab4fd12f18203b83d0f252db96d10731485ff6a/railties/lib/rails/autoloaders.rb#L10
30
+ # See Rails 6 https://github.com/rails/rails/blob/8ab4fd12f18203b83d0f252db96d10731485ff6a/railties/lib/rails/autoloaders.rb#L10
31
+ # and Rails 7 https://github.com/rails/rails/blob/5462fbd5de1900c1b1ce1c9dc11c1a2d8cdcd809/railties/lib/rails/autoloaders.rb#L15
26
32
  @autoloader = Zeitwerk::Loader.new.tap do |loader|
27
33
  loader.tag = "anyway.config"
28
- loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
34
+ loader.inflector = defined?(ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector) ? ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector : ::Rails::Autoloaders::Inflector
29
35
  loader.push_dir(::Rails.root.join(val))
30
36
  loader.setup
31
37
  end
@@ -56,5 +62,6 @@ module Anyway
56
62
  end
57
63
 
58
64
  self.default_config_path = ->(name) { ::Rails.root.join("config", "#{name}.yml") }
65
+ self.known_environments = %w[test development production]
59
66
  end
60
67
  end
data/lib/anyway/rbs.rb ADDED
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ module RBSGenerator
5
+ TYPE_TO_CLASS = {
6
+ string: "String",
7
+ integer: "Integer",
8
+ float: "Float",
9
+ date: "Date",
10
+ datetime: "DateTime",
11
+ uri: "URI",
12
+ boolean: "bool"
13
+ }.freeze
14
+
15
+ # Generate RBS signature from a config class
16
+ def to_rbs
17
+ *namespace, class_name = name.split("::")
18
+
19
+ buf = []
20
+ indent = 0
21
+ interface_name = "_Config"
22
+
23
+ if namespace.empty?
24
+ interface_name = "_#{class_name}"
25
+ else
26
+ buf << "module #{namespace.join("::")}"
27
+ indent += 1
28
+ end
29
+
30
+ # Using interface emulates a module we include to provide getters and setters
31
+ # (thus making `super` possible)
32
+ buf << "#{" " * indent}interface #{interface_name}"
33
+ indent += 1
34
+
35
+ # Generating setters and getters for config attributes
36
+ config_attributes.each do |param|
37
+ type = coercion_mapping[param] || defaults[param.to_s]
38
+
39
+ type =
40
+ case type
41
+ in NilClass
42
+ "untyped"
43
+ in Symbol
44
+ TYPE_TO_CLASS.fetch(type) { defaults[param] ? "Symbol" : "untyped" }
45
+ in Array
46
+ "Array[untyped]"
47
+ in array:, type:, **nil
48
+ "Array[#{TYPE_TO_CLASS.fetch(type, "untyped")}]"
49
+ in Hash
50
+ "Hash[string,untyped]"
51
+ in TrueClass | FalseClass
52
+ "bool"
53
+ else
54
+ type.class.to_s
55
+ end
56
+
57
+ getter_type = type
58
+ getter_type = "#{type}?" unless required_attributes.include?(param)
59
+
60
+ buf << "#{" " * indent}def #{param}: () -> #{getter_type}"
61
+ buf << "#{" " * indent}def #{param}=: (#{type}) -> void"
62
+
63
+ if type == "bool" || type == "bool?"
64
+ buf << "#{" " * indent}def #{param}?: () -> #{getter_type}"
65
+ end
66
+ end
67
+
68
+ indent -= 1
69
+ buf << "#{" " * indent}end"
70
+
71
+ buf << ""
72
+
73
+ buf << "#{" " * indent}class #{class_name} < #{superclass.name}"
74
+ indent += 1
75
+
76
+ buf << "#{" " * indent}include #{interface_name}"
77
+
78
+ indent -= 1
79
+ buf << "#{" " * indent}end"
80
+
81
+ unless namespace.empty?
82
+ buf << "end"
83
+ end
84
+
85
+ buf << ""
86
+
87
+ buf.join("\n")
88
+ end
89
+ end
90
+
91
+ Config.extend RBSGenerator
92
+ end
@@ -5,16 +5,66 @@ module Anyway
5
5
  #
6
6
  # Settings contain the library-wide configuration.
7
7
  class Settings
8
+ # Future encapsulates settings that will be introduced in the upcoming version
9
+ # with the default values, which could break compatibility
10
+ class Future
11
+ class << self
12
+ def setting(name, default_value)
13
+ settings[name] = default_value
14
+
15
+ define_method(name) do
16
+ store[name]
17
+ end
18
+
19
+ define_method(:"#{name}=") do |val|
20
+ store[name] = val
21
+ end
22
+ end
23
+
24
+ def settings
25
+ @settings ||= {}
26
+ end
27
+ end
28
+
29
+ def initialize
30
+ @store = {}
31
+ end
32
+
33
+ def use(*names)
34
+ store.clear
35
+ names.each { store[_1] = self.class.settings[_1] }
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :store
41
+ end
42
+
8
43
  class << self
9
44
  # Define whether to load data from
10
45
  # *.yml.local (or credentials/local.yml.enc)
11
46
  attr_accessor :use_local_files
12
47
 
13
- # Return a path to YML config file given the config name
14
- attr_accessor :default_config_path
48
+ # A proc returning a path to YML config file given the config name
49
+ attr_reader :default_config_path
50
+
51
+ def default_config_path=(val)
52
+ if val.is_a?(Proc)
53
+ @default_config_path = val
54
+ return
55
+ end
56
+
57
+ val = val.to_s
58
+
59
+ @default_config_path = ->(name) { File.join(val, "#{name}.yml") }
60
+ end
15
61
 
16
62
  # Enable source tracing
17
63
  attr_accessor :tracing_enabled
64
+
65
+ def future
66
+ @future ||= Future.new
67
+ end
18
68
  end
19
69
 
20
70
  # By default, use local files only in development (that's the purpose if the local files)
@@ -34,11 +34,11 @@ module Anyway
34
34
 
35
35
  def record_value(val, *path, **opts)
36
36
  key = path.pop
37
- if val.is_a?(Hash)
37
+ trace = if val.is_a?(Hash)
38
38
  Trace.new.tap { _1.merge_values(val, **opts) }
39
39
  else
40
40
  Trace.new(:value, val, **opts)
41
- end => trace
41
+ end
42
42
 
43
43
  target_trace = path.empty? ? self : value.dig(*path)
44
44
  target_trace.value[key.to_s] = trace
@@ -86,7 +86,7 @@ module Anyway
86
86
  if trace?
87
87
  value.transform_values(&:to_h).tap { _1.default_proc = nil }
88
88
  else
89
- {value: value, source: source}
89
+ {value, source}
90
90
  end
91
91
  end
92
92
 
@@ -141,7 +141,7 @@ module Anyway
141
141
 
142
142
  def current_trace() = trace_stack.last
143
143
 
144
- alias tracing? current_trace
144
+ alias_method :tracing?, :current_trace
145
145
 
146
146
  def source_stack
147
147
  (Thread.current[:__anyway__trace_source_stack__] ||= [])
@@ -171,9 +171,9 @@ module Anyway
171
171
  return yield unless Tracing.tracing?
172
172
  val = yield
173
173
  if val.is_a?(Hash)
174
- Tracing.current_trace.merge_values(val, type: type, **opts)
174
+ Tracing.current_trace.merge_values(val, type:, **opts)
175
175
  else
176
- Tracing.current_trace.record_value(val, *path, type: type, **opts)
176
+ Tracing.current_trace.record_value(val, *path, type:, **opts)
177
177
  end
178
178
  val
179
179
  end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ # Contains a mapping between type IDs/names and deserializers
5
+ class TypeRegistry
6
+ class << self
7
+ def default
8
+ @default ||= TypeRegistry.new
9
+ end
10
+ end
11
+
12
+ def initialize
13
+ @registry = {}
14
+ end
15
+
16
+ def accept(name_or_object, &block)
17
+ if !block && !name_or_object.respond_to?(:call)
18
+ raise ArgumentError, "Please, provide a type casting block or an object implementing #call(val) method"
19
+ end
20
+
21
+ registry[name_or_object] = block || name_or_object
22
+ end
23
+
24
+ def deserialize(raw, type_id, array: false)
25
+ return if raw.nil?
26
+
27
+ caster =
28
+ if type_id.is_a?(Symbol) || type_id.nil?
29
+ registry.fetch(type_id) { raise ArgumentError, "Unknown type: #{type_id}" }
30
+ else
31
+ raise ArgumentError, "Type must implement #call(val): #{type_id}" unless type_id.respond_to?(:call)
32
+ type_id
33
+ end
34
+
35
+ if array
36
+ raw_arr = raw.is_a?(String) ? raw.split(/\s*,\s*/) : Array(raw)
37
+ raw_arr.map { caster.call(_1) }
38
+ else
39
+ caster.call(raw)
40
+ end
41
+ end
42
+
43
+ def dup
44
+ new_obj = self.class.allocate
45
+ new_obj.instance_variable_set(:@registry, registry.dup)
46
+ new_obj
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :registry
52
+ end
53
+
54
+ TypeRegistry.default.tap do |obj|
55
+ obj.accept(nil, &:itself)
56
+ obj.accept(:string, &:to_s)
57
+ obj.accept(:integer, &:to_i)
58
+ obj.accept(:float, &:to_f)
59
+
60
+ obj.accept(:date) do
61
+ require "date" unless defined?(::Date)
62
+
63
+ next _1 if _1.is_a?(::Date)
64
+
65
+ next _1.to_date if _1.respond_to?(:to_date)
66
+
67
+ ::Date.parse(_1)
68
+ end
69
+
70
+ obj.accept(:datetime) do
71
+ require "date" unless defined?(::Date)
72
+
73
+ next _1 if _1.is_a?(::DateTime)
74
+
75
+ next _1.to_datetime if _1.respond_to?(:to_datetime)
76
+
77
+ ::DateTime.parse(_1)
78
+ end
79
+
80
+ obj.accept(:uri) do
81
+ require "uri" unless defined?(::URI)
82
+
83
+ next _1 if _1.is_a?(::URI)
84
+
85
+ ::URI.parse(_1)
86
+ end
87
+
88
+ obj.accept(:boolean) do
89
+ _1.to_s.match?(/\A(true|t|yes|y|1)\z/i)
90
+ end
91
+ end
92
+
93
+ # TypeCaster is an object responsible for type-casting.
94
+ # It uses a provided types registry and mapping, and also
95
+ # accepts a fallback typecaster.
96
+ class TypeCaster
97
+ using Ext::DeepDup
98
+ using Ext::Hash
99
+
100
+ def initialize(mapping, registry: TypeRegistry.default, fallback: ::Anyway::AutoCast)
101
+ @mapping = mapping.deep_dup
102
+ @registry = registry
103
+ @fallback = fallback
104
+ end
105
+
106
+ def coerce(key, val, config: mapping)
107
+ caster_config = config[key.to_sym]
108
+
109
+ return fallback.coerce(key, val) unless caster_config
110
+
111
+ case caster_config
112
+ in array:, type:, **nil
113
+ registry.deserialize(val, type, array: array)
114
+ in Hash
115
+ return val unless val.is_a?(Hash)
116
+
117
+ caster_config.each do |k, v|
118
+ ks = k.to_s
119
+ next unless val.key?(ks)
120
+
121
+ val[ks] = coerce(k, val[ks], config: caster_config)
122
+ end
123
+
124
+ val
125
+ else
126
+ registry.deserialize(val, caster_config)
127
+ end
128
+ end
129
+
130
+ private
131
+
132
+ attr_reader :mapping, :registry, :fallback
133
+ end
134
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ using Anyway::Ext::DeepDup
5
+
6
+ module Utils
7
+ def self.deep_merge!(source, other)
8
+ other.each do |key, other_value|
9
+ this_value = source[key]
10
+
11
+ if this_value.is_a?(::Hash) && other_value.is_a?(::Hash)
12
+ deep_merge!(this_value, other_value)
13
+ else
14
+ source[key] = other_value.deep_dup
15
+ end
16
+ end
17
+
18
+ source
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anyway # :nodoc:
4
- VERSION = "2.0.6"
4
+ VERSION = "2.2.2"
5
5
  end
data/lib/anyway_config.rb CHANGED
@@ -11,12 +11,16 @@ require "anyway/ext/deep_dup"
11
11
  require "anyway/ext/deep_freeze"
12
12
  require "anyway/ext/hash"
13
13
 
14
+ require "anyway/utils/deep_merge"
15
+
14
16
  require "anyway/settings"
15
17
  require "anyway/tracing"
16
18
  require "anyway/config"
17
19
  require "anyway/auto_cast"
20
+ require "anyway/type_casting"
18
21
  require "anyway/env"
19
22
  require "anyway/loaders"
23
+ require "anyway/rbs"
20
24
 
21
25
  module Anyway # :nodoc:
22
26
  class << self