anyway_config 2.0.1 → 2.0.6

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