anyway_config 2.1.0 → 2.2.3

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -0
  3. data/README.md +189 -4
  4. data/lib/.rbnext/2.7/anyway/auto_cast.rb +41 -21
  5. data/lib/.rbnext/2.7/anyway/config.rb +48 -1
  6. data/lib/.rbnext/2.7/anyway/rails/loaders/yaml.rb +8 -2
  7. data/lib/.rbnext/2.7/anyway/rbs.rb +92 -0
  8. data/lib/.rbnext/2.7/anyway/tracing.rb +5 -7
  9. data/lib/.rbnext/2.7/anyway/type_casting.rb +143 -0
  10. data/lib/.rbnext/3.0/anyway/auto_cast.rb +53 -0
  11. data/lib/.rbnext/3.0/anyway/config.rb +48 -1
  12. data/lib/.rbnext/3.0/anyway/tracing.rb +5 -7
  13. data/lib/.rbnext/{1995.next → 3.1}/anyway/config.rb +48 -1
  14. data/lib/.rbnext/{1995.next → 3.1}/anyway/dynamic_config.rb +6 -2
  15. data/lib/.rbnext/{1995.next → 3.1}/anyway/env.rb +0 -0
  16. data/lib/.rbnext/{1995.next → 3.1}/anyway/loaders/base.rb +0 -0
  17. data/lib/.rbnext/{1995.next → 3.1}/anyway/tracing.rb +2 -2
  18. data/lib/anyway/auto_cast.rb +41 -21
  19. data/lib/anyway/config.rb +48 -1
  20. data/lib/anyway/dynamic_config.rb +6 -2
  21. data/lib/anyway/ext/deep_dup.rb +6 -0
  22. data/lib/anyway/loaders/env.rb +3 -1
  23. data/lib/anyway/loaders/yaml.rb +16 -4
  24. data/lib/anyway/option_parser_builder.rb +1 -3
  25. data/lib/anyway/optparse_config.rb +5 -7
  26. data/lib/anyway/rails/loaders/credentials.rb +2 -2
  27. data/lib/anyway/rails/loaders/secrets.rb +5 -7
  28. data/lib/anyway/rails/loaders/yaml.rb +8 -2
  29. data/lib/anyway/rails/settings.rb +8 -2
  30. data/lib/anyway/rbs.rb +92 -0
  31. data/lib/anyway/tracing.rb +3 -3
  32. data/lib/anyway/type_casting.rb +134 -0
  33. data/lib/anyway/version.rb +1 -1
  34. data/lib/anyway_config.rb +2 -0
  35. data/lib/generators/anyway/install/templates/application_config.rb.tt +1 -1
  36. data/sig/anyway_config.rbs +129 -0
  37. metadata +21 -16
  38. data/lib/.rbnext/2.7/anyway/option_parser_builder.rb +0 -31
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+ using RubyNext;
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 { |_1| 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 |_1|
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 |_1|
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 |_1|
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 |_1|
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; when ((__m__ = caster_config)) && false
112
+ when ((__m__.respond_to?(:deconstruct_keys) && (((__m_hash__src__ = __m__.deconstruct_keys(nil)) || true) && (Hash === __m_hash__src__ || Kernel.raise(TypeError, "#deconstruct_keys must return Hash"))) && (__m_hash__ = __m_hash__src__.dup)) && ((__m_hash__.key?(:array) && __m_hash__.key?(:type)) && (((array = __m_hash__.delete(:array)) || true) && (((type = __m_hash__.delete(:type)) || true) && __m_hash__.empty?))))
113
+ registry.deserialize(val, type, array: array)
114
+ when (Hash === __m__)
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+ return val unless val.is_a?(Hash)
125
+
126
+ caster_config.each do |k, v|
127
+ ks = k.to_s
128
+ next unless val.key?(ks)
129
+
130
+ val[ks] = coerce(k, val[ks], config: caster_config)
131
+ end
132
+
133
+ val
134
+ else
135
+ registry.deserialize(val, caster_config)
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ attr_reader :mapping, :registry, :fallback
142
+ end
143
+ end
@@ -0,0 +1,53 @@
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 { call(_1) }
17
+ when ARRAY_RXP
18
+ val.split(/\s*,\s*/).map { call(_1) }
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
@@ -44,6 +44,7 @@ module Anyway # :nodoc:
44
44
  to_h
45
45
  to_source_trace
46
46
  write_config_attr
47
+ __type_caster__
47
48
  ].freeze
48
49
 
49
50
  class Error < StandardError; end
@@ -196,6 +197,47 @@ module Anyway # :nodoc:
196
197
 
197
198
  def new_empty_config() ; {}; end
198
199
 
200
+ def coerce_types(mapping)
201
+ Utils.deep_merge!(coercion_mapping, mapping)
202
+ end
203
+
204
+ def coercion_mapping
205
+ return @coercion_mapping if instance_variable_defined?(:@coercion_mapping)
206
+
207
+ @coercion_mapping = if superclass < Anyway::Config
208
+ superclass.coercion_mapping.deep_dup
209
+ else
210
+ {}
211
+ end
212
+ end
213
+
214
+ def type_caster(val = nil)
215
+ return @type_caster unless val.nil?
216
+
217
+ @type_caster ||=
218
+ if coercion_mapping.empty?
219
+ fallback_type_caster
220
+ else
221
+ ::Anyway::TypeCaster.new(coercion_mapping, fallback: fallback_type_caster)
222
+ end
223
+ end
224
+
225
+ def fallback_type_caster(val = nil)
226
+ return (@fallback_type_caster = val) unless val.nil?
227
+
228
+ return @fallback_type_caster if instance_variable_defined?(:@fallback_type_caster)
229
+
230
+ @fallback_type_caster = if superclass < Anyway::Config
231
+ superclass.fallback_type_caster.deep_dup
232
+ else
233
+ ::Anyway::AutoCast
234
+ end
235
+ end
236
+
237
+ def disable_auto_cast!
238
+ @fallback_type_caster = ::Anyway::NoCast
239
+ end
240
+
199
241
  private
200
242
 
201
243
  def define_config_accessor(*names)
@@ -373,7 +415,7 @@ module Anyway # :nodoc:
373
415
  values[name].nil? || (values[name].is_a?(String) && values[name].empty?)
374
416
  end.then do |missing|
375
417
  next if missing.empty?
376
- raise_validation_error "The following config parameters are missing or empty: #{missing.join(", ")}"
418
+ raise_validation_error "The following config parameters for `#{self.class.name}(config_name: #{self.class.config_name})` are missing or empty: #{missing.join(", ")}"
377
419
  end
378
420
  end
379
421
 
@@ -381,11 +423,16 @@ module Anyway # :nodoc:
381
423
  key = key.to_sym
382
424
  return unless self.class.config_attributes.include?(key)
383
425
 
426
+ val = __type_caster__.coerce(key, val)
384
427
  public_send(:"#{key}=", val)
385
428
  end
386
429
 
387
430
  def raise_validation_error(msg)
388
431
  raise ValidationError, msg
389
432
  end
433
+
434
+ def __type_caster__
435
+ self.class.type_caster
436
+ end
390
437
  end
391
438
  end
@@ -34,13 +34,11 @@ module Anyway
34
34
 
35
35
  def record_value(val, *path, **opts)
36
36
  key = path.pop
37
- (__m__ = if val.is_a?(Hash)
38
- Trace.new.tap {
39
- _1.merge_values(val, **opts)
40
- }
41
- else
42
- Trace.new(:value, val, **opts)
43
- end) && (((trace = __m__) || true) || Kernel.raise(NoMatchingPatternError, __m__.inspect))
37
+ trace = if val.is_a?(Hash)
38
+ Trace.new.tap { _1.merge_values(val, **opts) }
39
+ else
40
+ Trace.new(:value, val, **opts)
41
+ end
44
42
 
45
43
  target_trace = path.empty? ? self : value.dig(*path)
46
44
  target_trace.value[key.to_s] = trace
@@ -44,6 +44,7 @@ module Anyway # :nodoc:
44
44
  to_h
45
45
  to_source_trace
46
46
  write_config_attr
47
+ __type_caster__
47
48
  ].freeze
48
49
 
49
50
  class Error < StandardError; end
@@ -196,6 +197,47 @@ module Anyway # :nodoc:
196
197
 
197
198
  def new_empty_config() = {}
198
199
 
200
+ def coerce_types(mapping)
201
+ Utils.deep_merge!(coercion_mapping, mapping)
202
+ end
203
+
204
+ def coercion_mapping
205
+ return @coercion_mapping if instance_variable_defined?(:@coercion_mapping)
206
+
207
+ @coercion_mapping = if superclass < Anyway::Config
208
+ superclass.coercion_mapping.deep_dup
209
+ else
210
+ {}
211
+ end
212
+ end
213
+
214
+ def type_caster(val = nil)
215
+ return @type_caster unless val.nil?
216
+
217
+ @type_caster ||=
218
+ if coercion_mapping.empty?
219
+ fallback_type_caster
220
+ else
221
+ ::Anyway::TypeCaster.new(coercion_mapping, fallback: fallback_type_caster)
222
+ end
223
+ end
224
+
225
+ def fallback_type_caster(val = nil)
226
+ return (@fallback_type_caster = val) unless val.nil?
227
+
228
+ return @fallback_type_caster if instance_variable_defined?(:@fallback_type_caster)
229
+
230
+ @fallback_type_caster = if superclass < Anyway::Config
231
+ superclass.fallback_type_caster.deep_dup
232
+ else
233
+ ::Anyway::AutoCast
234
+ end
235
+ end
236
+
237
+ def disable_auto_cast!
238
+ @fallback_type_caster = ::Anyway::NoCast
239
+ end
240
+
199
241
  private
200
242
 
201
243
  def define_config_accessor(*names)
@@ -373,7 +415,7 @@ module Anyway # :nodoc:
373
415
  values[name].nil? || (values[name].is_a?(String) && values[name].empty?)
374
416
  end.then do |missing|
375
417
  next if missing.empty?
376
- raise_validation_error "The following config parameters are missing or empty: #{missing.join(", ")}"
418
+ raise_validation_error "The following config parameters for `#{self.class.name}(config_name: #{self.class.config_name})` are missing or empty: #{missing.join(", ")}"
377
419
  end
378
420
  end
379
421
 
@@ -381,11 +423,16 @@ module Anyway # :nodoc:
381
423
  key = key.to_sym
382
424
  return unless self.class.config_attributes.include?(key)
383
425
 
426
+ val = __type_caster__.coerce(key, val)
384
427
  public_send(:"#{key}=", val)
385
428
  end
386
429
 
387
430
  def raise_validation_error(msg)
388
431
  raise ValidationError, msg
389
432
  end
433
+
434
+ def __type_caster__
435
+ self.class.type_caster
436
+ end
390
437
  end
391
438
  end
@@ -12,11 +12,15 @@ module Anyway
12
12
  # my_config = Anyway::Config.for(:my_app)
13
13
  # # will load data from config/my_app.yml, secrets.my_app, ENV["MY_APP_*"]
14
14
  #
15
- def for(name, **options)
15
+ def for(name, auto_cast: true, **options)
16
16
  config = allocate
17
17
  options[:env_prefix] ||= name.to_s.upcase
18
18
  options[:config_path] ||= config.resolve_config_path(name, options[:env_prefix])
19
- config.load_from_sources(new_empty_config, name: name, **options)
19
+
20
+ raw_config = config.load_from_sources(new_empty_config, name: name, **options)
21
+ return raw_config unless auto_cast
22
+
23
+ AutoCast.call(raw_config)
20
24
  end
21
25
  end
22
26
 
File without changes
@@ -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
@@ -4,30 +4,50 @@ module Anyway
4
4
  module AutoCast
5
5
  # Regexp to detect array values
6
6
  # Array value is a values that contains at least one comma
7
- # and doesn't start/end with quote
8
- ARRAY_RXP = /\A[^'"].*\s*,\s*.*[^'"]\z/
7
+ # and doesn't start/end with quote or curly braces
8
+ ARRAY_RXP = /\A[^'"{].*\s*,\s*.*[^'"}]\z/
9
9
 
10
- def self.call(val)
11
- return val unless String === val
10
+ class << self
11
+ def call(val)
12
+ return val unless val.is_a?(::Hash) || val.is_a?(::String)
12
13
 
13
- case val
14
- when ARRAY_RXP
15
- val.split(/\s*,\s*/).map { call(_1) }
16
- when /\A(true|t|yes|y)\z/i
17
- true
18
- when /\A(false|f|no|n)\z/i
19
- false
20
- when /\A(nil|null)\z/i
21
- nil
22
- when /\A\d+\z/
23
- val.to_i
24
- when /\A\d*\.\d+\z/
25
- val.to_f
26
- when /\A['"].*['"]\z/
27
- val.gsub(/(\A['"]|['"]\z)/, "")
28
- else
29
- val
14
+ case val
15
+ when Hash
16
+ val.transform_values { call(_1) }
17
+ when ARRAY_RXP
18
+ val.split(/\s*,\s*/).map { call(_1) }
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)
30
44
  end
31
45
  end
32
46
  end
47
+
48
+ module NoCast
49
+ def self.call(val) = val
50
+
51
+ def self.coerce(_key, val) = val
52
+ end
33
53
  end
data/lib/anyway/config.rb CHANGED
@@ -44,6 +44,7 @@ module Anyway # :nodoc:
44
44
  to_h
45
45
  to_source_trace
46
46
  write_config_attr
47
+ __type_caster__
47
48
  ].freeze
48
49
 
49
50
  class Error < StandardError; end
@@ -196,6 +197,47 @@ module Anyway # :nodoc:
196
197
 
197
198
  def new_empty_config() = {}
198
199
 
200
+ def coerce_types(mapping)
201
+ Utils.deep_merge!(coercion_mapping, mapping)
202
+ end
203
+
204
+ def coercion_mapping
205
+ return @coercion_mapping if instance_variable_defined?(:@coercion_mapping)
206
+
207
+ @coercion_mapping = if superclass < Anyway::Config
208
+ superclass.coercion_mapping.deep_dup
209
+ else
210
+ {}
211
+ end
212
+ end
213
+
214
+ def type_caster(val = nil)
215
+ return @type_caster unless val.nil?
216
+
217
+ @type_caster ||=
218
+ if coercion_mapping.empty?
219
+ fallback_type_caster
220
+ else
221
+ ::Anyway::TypeCaster.new(coercion_mapping, fallback: fallback_type_caster)
222
+ end
223
+ end
224
+
225
+ def fallback_type_caster(val = nil)
226
+ return (@fallback_type_caster = val) unless val.nil?
227
+
228
+ return @fallback_type_caster if instance_variable_defined?(:@fallback_type_caster)
229
+
230
+ @fallback_type_caster = if superclass < Anyway::Config
231
+ superclass.fallback_type_caster.deep_dup
232
+ else
233
+ ::Anyway::AutoCast
234
+ end
235
+ end
236
+
237
+ def disable_auto_cast!
238
+ @fallback_type_caster = ::Anyway::NoCast
239
+ end
240
+
199
241
  private
200
242
 
201
243
  def define_config_accessor(*names)
@@ -373,7 +415,7 @@ module Anyway # :nodoc:
373
415
  values[name].nil? || (values[name].is_a?(String) && values[name].empty?)
374
416
  end.then do |missing|
375
417
  next if missing.empty?
376
- raise_validation_error "The following config parameters are missing or empty: #{missing.join(", ")}"
418
+ raise_validation_error "The following config parameters for `#{self.class.name}(config_name: #{self.class.config_name})` are missing or empty: #{missing.join(", ")}"
377
419
  end
378
420
  end
379
421
 
@@ -381,11 +423,16 @@ module Anyway # :nodoc:
381
423
  key = key.to_sym
382
424
  return unless self.class.config_attributes.include?(key)
383
425
 
426
+ val = __type_caster__.coerce(key, val)
384
427
  public_send(:"#{key}=", val)
385
428
  end
386
429
 
387
430
  def raise_validation_error(msg)
388
431
  raise ValidationError, msg
389
432
  end
433
+
434
+ def __type_caster__
435
+ self.class.type_caster
436
+ end
390
437
  end
391
438
  end
@@ -12,11 +12,15 @@ module Anyway
12
12
  # my_config = Anyway::Config.for(:my_app)
13
13
  # # will load data from config/my_app.yml, secrets.my_app, ENV["MY_APP_*"]
14
14
  #
15
- def for(name, **options)
15
+ def for(name, auto_cast: true, **options)
16
16
  config = allocate
17
17
  options[:env_prefix] ||= name.to_s.upcase
18
18
  options[:config_path] ||= config.resolve_config_path(name, options[:env_prefix])
19
- config.load_from_sources(new_empty_config, name:, **options)
19
+
20
+ raw_config = config.load_from_sources(new_empty_config, name:, **options)
21
+ return raw_config unless auto_cast
22
+
23
+ AutoCast.call(raw_config)
20
24
  end
21
25
  end
22
26
 
@@ -36,6 +36,12 @@ module Anyway
36
36
  end
37
37
  end
38
38
 
39
+ refine ::Module do
40
+ def deep_dup
41
+ self
42
+ end
43
+ end
44
+
39
45
  using self
40
46
  end
41
47
  end
@@ -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
@@ -25,10 +25,22 @@ module Anyway
25
25
  def parse_yml(path)
26
26
  return {} unless File.file?(path)
27
27
  require "yaml" unless defined?(::YAML)
28
- if defined?(ERB)
29
- ::YAML.load(ERB.new(File.read(path)).result) # rubocop:disable Security/YAMLLoad
30
- else
31
- ::YAML.load_file(path)
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.
32
+ begin
33
+ if defined?(ERB)
34
+ ::YAML.load(ERB.new(File.read(path)).result, aliases: true) || {} # rubocop:disable Security/YAMLLoad
35
+ else
36
+ ::YAML.load_file(path, aliases: true) || {}
37
+ end
38
+ rescue ArgumentError
39
+ if defined?(ERB)
40
+ ::YAML.load(ERB.new(File.read(path)).result) || {} # rubocop:disable Security/YAMLLoad
41
+ else
42
+ ::YAML.load_file(path) || {}
43
+ end
32
44
  end
33
45
  end
34
46
 
@@ -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