anyway_config 2.0.1 → 2.0.6

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: 9b9434114b530b5850b6f9210018f05b1d271072b5f29769700dd166b498c4b0
4
- data.tar.gz: a14411a20a2f549fd955c53dc5e57535207338d7a10c9b91af1dc612b06b30df
3
+ metadata.gz: b3fcc40e50bfd285e686ca346ff5ddb3f4039104f1b5a79a8a4dd305d5d53707
4
+ data.tar.gz: 1f43073daee8706d7c9be2161bd226cb07f04a92929e6cdd81d64c8303f029da
5
5
  SHA512:
6
- metadata.gz: 97362fd7819d70b1d52700146d12457fd62a0e2eaa34bc9511da87bb362a7722d066dc87c83c5a14381467a8fa934f5f25afe545036d8a6dae6a1492daf0ccfa
7
- data.tar.gz: 2653e0fc1dfb868e0b015048c7f46567b90fb30892fcf9cd6b52d69a16f2635b91e6e409b499b7d0364635d4d755f811e2a1a63bf19831dffa7916ec9b964780
6
+ metadata.gz: 8125fbf4c4c592f673eca184d74ec3e02e1217016e4fa5a5d6371a24d13b9b092c48220dcd0bc57e33cbf7b0b28f0a1f5d2b9ce4c2f33aba42506e3444855bc9
7
+ data.tar.gz: 5076ec06d2dd7acbcb8a002653eb5e8df80a666ef83e7820d9ba9dd19103ac70db9560c818774bc8d8a26ea5f4b67b929c9e192c487f3e1785a5ed3f0d85b00a
@@ -1,5 +1,27 @@
1
1
  # Change log
2
2
 
3
+ ## 2.0.6 (2020-07-7)
4
+
5
+ - Fix Ruby 2.7 warnings. ([@palkan][])
6
+
7
+ ## 2.0.5 (2020-05-15)
8
+
9
+ - Use `YAML.load` instead of `YAML.safe_laad`. ([@palkan][])
10
+
11
+ ## 2.0.4 (2020-05-15)
12
+
13
+ - Fix regression with adding `ruby-next` as a runtime dependency even for RubyGems release. ([@palkan][])
14
+
15
+ ## 2.0.3 (2020-05-12)
16
+
17
+ - Enable [auto-transpiling](https://github.com/ruby-next/ruby-next#transpiled-files-vs-vcs-vs-installing-from-source) to allow installing from source. ([@palkan][])
18
+
19
+ ## 2.0.2 (2020-04-24)
20
+
21
+ - Make sure configs are eager loaded in Rails when `config.eager_load = true`. ([@palkan][])
22
+
23
+ Fixes [#58](https://github.com/palkan/anyway_config/issues/58).
24
+
3
25
  ## 2.0.1 (2020-04-15)
4
26
 
5
27
  - Fix loading Railtie when application has been already initialized. ([@palkan][])
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](http://cultofmartians.com)
1
+ [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/anyway-config-options-parse.html#task)
2
2
  [![Gem Version](https://badge.fury.io/rb/anyway_config.svg)](https://rubygems.org/gems/anyway_config) [![Build](https://github.com/palkan/anyway_config/workflows/Build/badge.svg)](https://github.com/palkan/anyway_config/actions)
3
3
  [![JRuby Build](https://github.com/palkan/anyway_config/workflows/JRuby%20Build/badge.svg)](https://github.com/palkan/anyway_config/actions)
4
4
 
@@ -11,9 +11,7 @@ module Anyway # :nodoc:
11
11
 
12
12
  using(Module.new do
13
13
  refine Object do
14
- def vm_object_id
15
- (object_id << 1).to_s(16)
16
- end
14
+ def vm_object_id() ; (object_id << 1).to_s(16); end
17
15
  end
18
16
  end)
19
17
 
@@ -72,9 +70,7 @@ module Anyway # :nodoc:
72
70
  @name = name
73
71
  end
74
72
 
75
- def apply_to(config)
76
- config.send(name)
77
- end
73
+ def apply_to(config) ; config.send(name); end
78
74
  end
79
75
 
80
76
  class << self
@@ -110,23 +106,21 @@ module Anyway # :nodoc:
110
106
  def defaults
111
107
  return @defaults if instance_variable_defined?(:@defaults)
112
108
 
113
- @defaults =
114
- if superclass < Anyway::Config
115
- superclass.defaults.deep_dup
116
- else
117
- new_empty_config
118
- end
109
+ @defaults = if superclass < Anyway::Config
110
+ superclass.defaults.deep_dup
111
+ else
112
+ new_empty_config
113
+ end
119
114
  end
120
115
 
121
116
  def config_attributes
122
117
  return @config_attributes if instance_variable_defined?(:@config_attributes)
123
118
 
124
- @config_attributes =
125
- if superclass < Anyway::Config
126
- superclass.config_attributes.dup
127
- else
128
- []
129
- end
119
+ @config_attributes = if superclass < Anyway::Config
120
+ superclass.config_attributes.dup
121
+ else
122
+ []
123
+ end
130
124
  end
131
125
 
132
126
  def required(*names)
@@ -140,12 +134,11 @@ module Anyway # :nodoc:
140
134
  def required_attributes
141
135
  return @required_attributes if instance_variable_defined?(:@required_attributes)
142
136
 
143
- @required_attributes =
144
- if superclass < Anyway::Config
145
- superclass.required_attributes.dup
146
- else
147
- []
148
- end
137
+ @required_attributes = if superclass < Anyway::Config
138
+ superclass.required_attributes.dup
139
+ else
140
+ []
141
+ end
149
142
  end
150
143
 
151
144
  def on_load(*names, &block)
@@ -161,12 +154,11 @@ module Anyway # :nodoc:
161
154
  def load_callbacks
162
155
  return @load_callbacks if instance_variable_defined?(:@load_callbacks)
163
156
 
164
- @load_callbacks =
165
- if superclass <= Anyway::Config
166
- superclass.load_callbacks.dup
167
- else
168
- []
169
- end
157
+ @load_callbacks = if superclass <= Anyway::Config
158
+ superclass.load_callbacks.dup
159
+ else
160
+ []
161
+ end
170
162
  end
171
163
 
172
164
  def config_name(val = nil)
@@ -186,26 +178,21 @@ module Anyway # :nodoc:
186
178
  end
187
179
  end
188
180
 
189
- def explicit_config_name?
190
- !explicit_config_name.nil?
191
- end
181
+ def explicit_config_name?() ; !explicit_config_name.nil?; end
192
182
 
193
183
  def env_prefix(val = nil)
194
184
  return (@env_prefix = val.to_s.upcase) unless val.nil?
195
185
 
196
186
  return @env_prefix if instance_variable_defined?(:@env_prefix)
197
187
 
198
- @env_prefix =
199
- if superclass < Anyway::Config && superclass.explicit_config_name?
200
- superclass.env_prefix
201
- else
202
- config_name.upcase
203
- end
188
+ @env_prefix = if superclass < Anyway::Config && superclass.explicit_config_name?
189
+ superclass.env_prefix
190
+ else
191
+ config_name.upcase
192
+ end
204
193
  end
205
194
 
206
- def new_empty_config
207
- {}
208
- end
195
+ def new_empty_config() ; {}; end
209
196
 
210
197
  private
211
198
 
@@ -213,7 +200,7 @@ module Anyway # :nodoc:
213
200
  names.each do |name|
214
201
  accessors_module.module_eval <<~RUBY, __FILE__, __LINE__ + 1
215
202
  def #{name}=(val)
216
- __trace__&.record_value(val, \"#{name}\", Tracing.current_trace_source)
203
+ __trace__&.record_value(val, \"#{name}\", **Tracing.current_trace_source)
217
204
  # DEPRECATED: instance variable set will be removed in 2.1
218
205
  @#{name} = values[:#{name}] = val
219
206
  end
@@ -339,13 +326,9 @@ module Anyway # :nodoc:
339
326
  base_config
340
327
  end
341
328
 
342
- def dig(*keys)
343
- values.dig(*keys)
344
- end
329
+ def dig(*keys) ; values.dig(*keys); end
345
330
 
346
- def to_h
347
- values.deep_dup.deep_freeze
348
- end
331
+ def to_h() ; values.deep_dup.deep_freeze; end
349
332
 
350
333
  def dup
351
334
  self.class.allocate.tap do |new_config|
@@ -360,13 +343,9 @@ module Anyway # :nodoc:
360
343
  Anyway.env.fetch(env_prefix).delete("conf") || Settings.default_config_path.call(name)
361
344
  end
362
345
 
363
- def deconstruct_keys(keys)
364
- values.deconstruct_keys(keys)
365
- end
346
+ def deconstruct_keys(keys) ; values.deconstruct_keys(keys); end
366
347
 
367
- def to_source_trace
368
- __trace__&.to_h
369
- end
348
+ def to_source_trace() ; __trace__&.to_h; end
370
349
 
371
350
  def inspect
372
351
  "#<#{self.class}:0x#{vm_object_id.rjust(16, "0")} config_name=\"#{config_name}\" env_prefix=\"#{env_prefix}\" " \
@@ -13,9 +13,7 @@ module Anyway
13
13
  end
14
14
 
15
15
  refine Thread::Backtrace::Location do
16
- def path_lineno
17
- "#{path}:#{lineno}"
18
- end
16
+ def path_lineno() ; "#{path}:#{lineno}"; end
19
17
  end
20
18
  end)
21
19
 
@@ -34,13 +32,14 @@ module Anyway
34
32
  value.dig(*__rest__, &__block__)
35
33
  end
36
34
 
37
- def record_value(val, *path, key, **opts)
38
- trace =
39
- if val.is_a?(Hash)
40
- Trace.new.tap { |_1| _1.merge_values(val, **opts) }
41
- else
42
- Trace.new(:value, val, **opts)
43
- end
35
+ def record_value(val, *path, **opts)
36
+ key = path.pop
37
+ trace = if val.is_a?(Hash)
38
+ Trace.new.tap { |_1| _1.merge_values(val, **opts) }
39
+ else
40
+ Trace.new(:value, val, **opts)
41
+ end
42
+
44
43
  target_trace = path.empty? ? self : value.dig(*path)
45
44
  target_trace.value[key.to_s] = trace
46
45
 
@@ -79,13 +78,9 @@ module Anyway
79
78
  value.keep_if(*__rest__, &__block__)
80
79
  end
81
80
 
82
- def clear
83
- value.clear
84
- end
81
+ def clear() ; value.clear; end
85
82
 
86
- def trace?
87
- type == :trace
88
- end
83
+ def trace?() ; type == :trace; end
89
84
 
90
85
  def to_h
91
86
  if trace?
@@ -95,9 +90,7 @@ module Anyway
95
90
  end
96
91
  end
97
92
 
98
- def dup
99
- self.class.new(type, value.dup, source)
100
- end
93
+ def dup() ; self.class.new(type, value.dup, **source); end
101
94
 
102
95
  def pretty_print(q)
103
96
  if trace?
@@ -146,9 +139,7 @@ module Anyway
146
139
  (Thread.current[:__anyway__trace_stack__] ||= [])
147
140
  end
148
141
 
149
- def current_trace
150
- trace_stack.last
151
- end
142
+ def current_trace() ; trace_stack.last; end
152
143
 
153
144
  alias tracing? current_trace
154
145
 
@@ -178,12 +169,11 @@ module Anyway
178
169
 
179
170
  def trace!(type, *path, **opts)
180
171
  return yield unless Tracing.tracing?
181
- source = {type: type}.merge(opts)
182
172
  val = yield
183
173
  if val.is_a?(Hash)
184
- Tracing.current_trace.merge_values(val, **source)
174
+ Tracing.current_trace.merge_values(val, type: type, **opts)
185
175
  else
186
- Tracing.current_trace.record_value(val, *path, **source)
176
+ Tracing.current_trace.record_value(val, *path, type: type, **opts)
187
177
  end
188
178
  val
189
179
  end
@@ -0,0 +1,393 @@
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
+
12
+ using(Module.new do
13
+ refine Object do
14
+ def vm_object_id() ; (object_id << 1).to_s(16); end
15
+ end
16
+ end)
17
+
18
+ # Base config class
19
+ # Provides `attr_config` method to describe
20
+ # configuration parameters and set defaults
21
+ class Config
22
+ PARAM_NAME = /^[a-z_]([\w]+)?$/
23
+
24
+ # List of names that couldn't be used as config names
25
+ # (the class instance methods we use)
26
+ RESERVED_NAMES = %i[
27
+ config_name
28
+ env_prefix
29
+ values
30
+ class
31
+ clear
32
+ deconstruct_keys
33
+ dig
34
+ dup
35
+ initialize
36
+ load
37
+ load_from_sources
38
+ option_parser
39
+ pretty_print
40
+ raise_validation_error
41
+ reload
42
+ resolve_config_path
43
+ to_h
44
+ to_source_trace
45
+ write_config_attr
46
+ ].freeze
47
+
48
+ class Error < StandardError; end
49
+ class ValidationError < Error; end
50
+
51
+ include OptparseConfig
52
+ include DynamicConfig
53
+
54
+ class BlockCallback
55
+ attr_reader :block
56
+
57
+ def initialize(block)
58
+ @block = block
59
+ end
60
+
61
+ def apply_to(config)
62
+ config.instance_exec(&block)
63
+ end
64
+ end
65
+
66
+ class NamedCallback
67
+ attr_reader :name
68
+
69
+ def initialize(name)
70
+ @name = name
71
+ end
72
+
73
+ def apply_to(config) ; config.send(name); end
74
+ end
75
+
76
+ class << self
77
+ def attr_config(*args, **hargs)
78
+ new_defaults = hargs.deep_dup
79
+ new_defaults.stringify_keys!
80
+
81
+ defaults.merge! new_defaults
82
+
83
+ new_keys = ((args + new_defaults.keys) - config_attributes)
84
+
85
+ validate_param_names! new_keys.map(&:to_s)
86
+
87
+ new_keys.map!(&:to_sym)
88
+
89
+ unless (reserved_names = (new_keys & RESERVED_NAMES)).empty?
90
+ raise ArgumentError, "Can not use the following reserved names as config attrubutes: " \
91
+ "#{reserved_names.sort.map(&:to_s).join(", ")}"
92
+ end
93
+
94
+ config_attributes.push(*new_keys)
95
+
96
+ define_config_accessor(*new_keys)
97
+
98
+ # Define predicate methods ("param?") for attributes
99
+ # having `true` or `false` as default values
100
+ new_defaults.each do |key, val|
101
+ next unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
102
+ alias_method :"#{key}?", :"#{key}"
103
+ end
104
+ end
105
+
106
+ def defaults
107
+ return @defaults if instance_variable_defined?(:@defaults)
108
+
109
+ @defaults = if superclass < Anyway::Config
110
+ superclass.defaults.deep_dup
111
+ else
112
+ new_empty_config
113
+ end
114
+ end
115
+
116
+ def config_attributes
117
+ return @config_attributes if instance_variable_defined?(:@config_attributes)
118
+
119
+ @config_attributes = if superclass < Anyway::Config
120
+ superclass.config_attributes.dup
121
+ else
122
+ []
123
+ end
124
+ end
125
+
126
+ def required(*names)
127
+ unless (unknown_names = (names - config_attributes)).empty?
128
+ raise ArgumentError, "Unknown config param: #{unknown_names.join(",")}"
129
+ end
130
+
131
+ required_attributes.push(*names)
132
+ end
133
+
134
+ def required_attributes
135
+ return @required_attributes if instance_variable_defined?(:@required_attributes)
136
+
137
+ @required_attributes = if superclass < Anyway::Config
138
+ superclass.required_attributes.dup
139
+ else
140
+ []
141
+ end
142
+ end
143
+
144
+ def on_load(*names, &block)
145
+ raise ArgumentError, "Either methods or block should be specified, not both" if block_given? && !names.empty?
146
+
147
+ if block_given?
148
+ load_callbacks << BlockCallback.new(block)
149
+ else
150
+ load_callbacks.push(*names.map { NamedCallback.new(_1) })
151
+ end
152
+ end
153
+
154
+ def load_callbacks
155
+ return @load_callbacks if instance_variable_defined?(:@load_callbacks)
156
+
157
+ @load_callbacks = if superclass <= Anyway::Config
158
+ superclass.load_callbacks.dup
159
+ else
160
+ []
161
+ end
162
+ end
163
+
164
+ def config_name(val = nil)
165
+ return (@explicit_config_name = val.to_s) unless val.nil?
166
+
167
+ return @config_name if instance_variable_defined?(:@config_name)
168
+
169
+ @config_name = explicit_config_name || build_config_name
170
+ end
171
+
172
+ def explicit_config_name
173
+ return @explicit_config_name if instance_variable_defined?(:@explicit_config_name)
174
+
175
+ @explicit_config_name =
176
+ if superclass.respond_to?(:explicit_config_name)
177
+ superclass.explicit_config_name
178
+ end
179
+ end
180
+
181
+ def explicit_config_name?() ; !explicit_config_name.nil?; end
182
+
183
+ def env_prefix(val = nil)
184
+ return (@env_prefix = val.to_s.upcase) unless val.nil?
185
+
186
+ return @env_prefix if instance_variable_defined?(:@env_prefix)
187
+
188
+ @env_prefix = if superclass < Anyway::Config && superclass.explicit_config_name?
189
+ superclass.env_prefix
190
+ else
191
+ config_name.upcase
192
+ end
193
+ end
194
+
195
+ def new_empty_config() ; {}; end
196
+
197
+ private
198
+
199
+ def define_config_accessor(*names)
200
+ names.each do |name|
201
+ accessors_module.module_eval <<~RUBY, __FILE__, __LINE__ + 1
202
+ def #{name}=(val)
203
+ __trace__&.record_value(val, \"#{name}\", **Tracing.current_trace_source)
204
+ # DEPRECATED: instance variable set will be removed in 2.1
205
+ @#{name} = values[:#{name}] = val
206
+ end
207
+
208
+ def #{name}
209
+ values[:#{name}]
210
+ end
211
+ RUBY
212
+ end
213
+ end
214
+
215
+ def accessors_module
216
+ return @accessors_module if instance_variable_defined?(:@accessors_module)
217
+
218
+ @accessors_module = Module.new.tap do |mod|
219
+ include mod
220
+ end
221
+ end
222
+
223
+ def build_config_name
224
+ unless name
225
+ raise "Please, specify config name explicitly for anonymous class " \
226
+ "via `config_name :my_config`"
227
+ end
228
+
229
+ # handle two cases:
230
+ # - SomeModule::Config => "some_module"
231
+ # - SomeConfig => "some"
232
+ unless name =~ /^(\w+)(\:\:)?Config$/
233
+ raise "Couldn't infer config name, please, specify it explicitly" \
234
+ "via `config_name :my_config`"
235
+ end
236
+
237
+ Regexp.last_match[1].tap(&:downcase!)
238
+ end
239
+
240
+ def validate_param_names!(names)
241
+ invalid_names = names.reject { |name| name =~ PARAM_NAME }
242
+ return if invalid_names.empty?
243
+
244
+ raise ArgumentError, "Invalid attr_config name: #{invalid_names.join(", ")}.\n" \
245
+ "Valid names must satisfy /#{PARAM_NAME.source}/."
246
+ end
247
+ end
248
+
249
+ on_load :validate_required_attributes!
250
+
251
+ attr_reader :config_name, :env_prefix
252
+
253
+ # Instantiate config instance.
254
+ #
255
+ # Example:
256
+ #
257
+ # my_config = Anyway::Config.new()
258
+ #
259
+ # # provide some values explicitly
260
+ # my_config = Anyway::Config.new({some: :value})
261
+ #
262
+ def initialize(overrides = nil)
263
+ @config_name = self.class.config_name
264
+
265
+ raise ArgumentError, "Config name is missing" unless @config_name
266
+
267
+ @env_prefix = self.class.env_prefix
268
+ @values = {}
269
+
270
+ load(overrides)
271
+ end
272
+
273
+ def reload(overrides = nil)
274
+ clear
275
+ load(overrides)
276
+ self
277
+ end
278
+
279
+ def clear
280
+ values.clear
281
+ @__trace__ = nil
282
+ self
283
+ end
284
+
285
+ def load(overrides = nil)
286
+ base_config = self.class.defaults.deep_dup
287
+
288
+ trace = Tracing.capture do
289
+ Tracing.trace!(:defaults) { base_config }
290
+
291
+ load_from_sources(
292
+ base_config,
293
+ name: config_name,
294
+ env_prefix: env_prefix,
295
+ config_path: resolve_config_path(config_name, env_prefix)
296
+ )
297
+
298
+ if overrides
299
+ Tracing.trace!(:load) { overrides }
300
+
301
+ base_config.deep_merge!(overrides)
302
+ end
303
+ end
304
+
305
+ base_config.each do |key, val|
306
+ write_config_attr(key.to_sym, val)
307
+ end
308
+
309
+ # Trace may contain unknown attributes
310
+ trace&.keep_if { |key| self.class.config_attributes.include?(key.to_sym) }
311
+
312
+ # Run on_load callbacks
313
+ self.class.load_callbacks.each { _1.apply_to(self) }
314
+
315
+ # Set trace after we write all the values to
316
+ # avoid changing the source to accessor
317
+ @__trace__ = trace
318
+
319
+ self
320
+ end
321
+
322
+ def load_from_sources(base_config, **options)
323
+ Anyway.loaders.each do |(_id, loader)|
324
+ base_config.deep_merge!(loader.call(**options))
325
+ end
326
+ base_config
327
+ end
328
+
329
+ def dig(*keys) ; values.dig(*keys); end
330
+
331
+ def to_h() ; values.deep_dup.deep_freeze; end
332
+
333
+ def dup
334
+ self.class.allocate.tap do |new_config|
335
+ %i[config_name env_prefix __trace__].each do |ivar|
336
+ new_config.instance_variable_set(:"@#{ivar}", send(ivar).dup)
337
+ end
338
+ new_config.instance_variable_set(:@values, values.deep_dup)
339
+ end
340
+ end
341
+
342
+ def resolve_config_path(name, env_prefix)
343
+ Anyway.env.fetch(env_prefix).delete("conf") || Settings.default_config_path.call(name)
344
+ end
345
+
346
+ def deconstruct_keys(keys) ; values.deconstruct_keys(keys); end
347
+
348
+ def to_source_trace() ; __trace__&.to_h; end
349
+
350
+ def inspect
351
+ "#<#{self.class}:0x#{vm_object_id.rjust(16, "0")} config_name=\"#{config_name}\" env_prefix=\"#{env_prefix}\" " \
352
+ "values=#{values.inspect}>"
353
+ end
354
+
355
+ def pretty_print(q)
356
+ q.object_group self do
357
+ q.nest(1) do
358
+ q.breakable
359
+ q.text "config_name=#{config_name.inspect}"
360
+ q.breakable
361
+ q.text "env_prefix=#{env_prefix.inspect}"
362
+ q.breakable
363
+ q.text "values:"
364
+ q.pp __trace__
365
+ end
366
+ end
367
+ end
368
+
369
+ private
370
+
371
+ attr_reader :values, :__trace__
372
+
373
+ def validate_required_attributes!
374
+ self.class.required_attributes.select do |name|
375
+ values[name].nil? || (values[name].is_a?(String) && values[name].empty?)
376
+ end.then do |missing|
377
+ next if missing.empty?
378
+ raise_validation_error "The following config parameters are missing or empty: #{missing.join(", ")}"
379
+ end
380
+ end
381
+
382
+ def write_config_attr(key, val)
383
+ key = key.to_sym
384
+ return unless self.class.config_attributes.include?(key)
385
+
386
+ public_send(:"#{key}=", val)
387
+ end
388
+
389
+ def raise_validation_error(msg)
390
+ raise ValidationError, msg
391
+ end
392
+ end
393
+ end