anyway_config 2.1.0 → 2.2.3

Sign up to get free protection for your applications and to get access to all the features.
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