anyway_config 2.0.2 → 2.0.3

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: 7f9746b519b5adfe5e3a9b1f8753c8bac8a0c89cac457fa52c96c482bdf77897
4
- data.tar.gz: b105ab28f23df78e54d163487c75f43b4ec3ed69d761bd92d91d0d080ccb770b
3
+ metadata.gz: 2c71bc14b4c2822f12237ce9931c8ff7110c02694a8130477901d3e282c0d08c
4
+ data.tar.gz: de7ef174a92b14e9eb62b70c27ec9ce01f202acf5ed6f3af74666973ec8d81a7
5
5
  SHA512:
6
- metadata.gz: f9eda4f4738f72fc3db0d35584b696f310102e4a9caaeebfdf42c991fe54b983f7ec36da7e14dc0b8fac834fd627b387000254f9fc7f43966ef45e268bdc4950
7
- data.tar.gz: bc2abf29581fa77b0b467861a8bf4a8225690f8f258eb299d7c76b6337990ce0aa423fe7fda043dd1dcbbd6f2beaad5c227a36117f6aa018190bd42714b2cd0b
6
+ metadata.gz: 809b732f5d26976789919febd120191102bb8d40e31130d37bdd0832e748dbe371856f3b57fed97973ac978718562d2e5de65203ab273e380b15c31fd36c44a2
7
+ data.tar.gz: c5f6c3aa86c0c6843442c09273fd832f830c0930fe0285493fc0a0fdffbf5fb8918ecd8b45532f7e8676b06c200b6d709873ba83ce65bbabc223c4ed48fbc066
@@ -1,5 +1,9 @@
1
1
  # Change log
2
2
 
3
+ ## 2.0.3 (2020-05-12)
4
+
5
+ - Enable [auto-transpiling](https://github.com/ruby-next/ruby-next#transpiled-files-vs-vcs-vs-installing-from-source) to allow installing from source. ([@palkan][])
6
+
3
7
  ## 2.0.2 (2020-04-24)
4
8
 
5
9
  - Make sure configs are eager loaded in Rails when `config.eager_load = true`. ([@palkan][])
@@ -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
 
@@ -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
 
@@ -35,12 +33,12 @@ module Anyway
35
33
  end
36
34
 
37
35
  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
36
+ trace = if val.is_a?(Hash)
37
+ Trace.new.tap { |_1| _1.merge_values(val, **opts) }
38
+ else
39
+ Trace.new(:value, val, **opts)
40
+ end
41
+
44
42
  target_trace = path.empty? ? self : value.dig(*path)
45
43
  target_trace.value[key.to_s] = trace
46
44
 
@@ -79,13 +77,9 @@ module Anyway
79
77
  value.keep_if(*__rest__, &__block__)
80
78
  end
81
79
 
82
- def clear
83
- value.clear
84
- end
80
+ def clear() ; value.clear; end
85
81
 
86
- def trace?
87
- type == :trace
88
- end
82
+ def trace?() ; type == :trace; end
89
83
 
90
84
  def to_h
91
85
  if trace?
@@ -95,9 +89,7 @@ module Anyway
95
89
  end
96
90
  end
97
91
 
98
- def dup
99
- self.class.new(type, value.dup, source)
100
- end
92
+ def dup() ; self.class.new(type, value.dup, source); end
101
93
 
102
94
  def pretty_print(q)
103
95
  if trace?
@@ -146,9 +138,7 @@ module Anyway
146
138
  (Thread.current[:__anyway__trace_stack__] ||= [])
147
139
  end
148
140
 
149
- def current_trace
150
- trace_stack.last
151
- end
141
+ def current_trace() ; trace_stack.last; end
152
142
 
153
143
  alias tracing? current_trace
154
144
 
@@ -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
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ using RubyNext
5
+
6
+ module Loaders
7
+ class Registry
8
+ attr_reader :registry
9
+
10
+ def initialize
11
+ @registry = []
12
+ end
13
+
14
+ def prepend(id, handler = nil, &block)
15
+ handler ||= block
16
+ insert_at(0, id, handler)
17
+ end
18
+
19
+ def append(id, handler = nil, &block)
20
+ handler ||= block
21
+ insert_at(registry.size, id, handler)
22
+ end
23
+
24
+ def insert_before(another_id, id, handler = nil, &block)
25
+ ind = registry.find_index { |(hid, _)| hid == another_id }
26
+ raise ArgumentError, "Loader with ID #{another_id} hasn't been registered" if ind.nil?
27
+
28
+ handler ||= block
29
+ insert_at(ind, id, handler)
30
+ end
31
+
32
+ def insert_after(another_id, id, handler = nil, &block)
33
+ ind = registry.find_index { |(hid, _)| hid == another_id }
34
+ raise ArgumentError, "Loader with ID #{another_id} hasn't been registered" if ind.nil?
35
+
36
+ handler ||= block
37
+ insert_at(ind + 1, id, handler)
38
+ end
39
+
40
+ def override(id, handler)
41
+ find(id).then do |id_to_handler|
42
+ raise ArgumentError, "Loader with ID #{id} hasn't been registered" if id_to_handler.nil?
43
+ id_to_handler[1] = handler
44
+ end
45
+ end
46
+
47
+ def delete(id)
48
+ find(id).then do |id_to_handler|
49
+ raise ArgumentError, "Loader with ID #{id} hasn't been registered" if id_to_handler.nil?
50
+ registry.delete id_to_handler
51
+ end
52
+ end
53
+
54
+ def each(&block)
55
+ registry.each(&block)
56
+ end
57
+
58
+ def freeze() ; registry.freeze; end
59
+
60
+ private
61
+
62
+ def insert_at(index, id, handler)
63
+ raise ArgumentError, "Loader with ID #{id} has been already registered" unless find(id).nil?
64
+
65
+ registry.insert(index, [id, handler])
66
+ end
67
+
68
+ def find(id)
69
+ registry.find { |(hid, _)| hid == id }
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ require "anyway/loaders/base"
76
+ require "anyway/loaders/yaml"
77
+ require "anyway/loaders/env"
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ module Loaders
5
+ class Base
6
+ include Tracing
7
+
8
+ class << self
9
+ def call(local: Anyway::Settings.use_local_files, **opts)
10
+ new(local: local).call(**opts)
11
+ end
12
+ end
13
+
14
+ def initialize(local:)
15
+ @local = local
16
+ end
17
+
18
+ def use_local?() ; @local == true; end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ # Provides method to trace values association
5
+ module Tracing
6
+ using Anyway::Ext::DeepDup
7
+
8
+ using(Module.new do
9
+ refine Hash do
10
+ def inspect
11
+ "{#{map { |k, v| "#{k}: #{v.inspect}" }.join(", ")}}"
12
+ end
13
+ end
14
+
15
+ refine Thread::Backtrace::Location do
16
+ def path_lineno() ; "#{path}:#{lineno}"; end
17
+ end
18
+ end)
19
+
20
+ class Trace
21
+ UNDEF = Object.new
22
+
23
+ attr_reader :type, :value, :source
24
+
25
+ def initialize(type = :trace, value = UNDEF, **source)
26
+ @type = type
27
+ @source = source
28
+ @value = value == UNDEF ? Hash.new { |h, k| h[k] = Trace.new(:trace) } : value
29
+ end
30
+
31
+ def dig(...)
32
+ value.dig(...)
33
+ end
34
+
35
+ def record_value(val, *path, key, **opts)
36
+ trace = if val.is_a?(Hash)
37
+ Trace.new.tap { _1.merge_values(val, **opts) }
38
+ else
39
+ Trace.new(:value, val, **opts)
40
+ end
41
+
42
+ target_trace = path.empty? ? self : value.dig(*path)
43
+ target_trace.value[key.to_s] = trace
44
+
45
+ val
46
+ end
47
+
48
+ def merge_values(hash, **opts)
49
+ return hash unless hash
50
+
51
+ hash.each do |key, val|
52
+ if val.is_a?(Hash)
53
+ value[key.to_s].merge_values(val, **opts)
54
+ else
55
+ value[key.to_s] = Trace.new(:value, val, **opts)
56
+ end
57
+ end
58
+
59
+ hash
60
+ end
61
+
62
+ def merge!(another_trace)
63
+ raise ArgumentError, "You can only merge into a :trace type, and this is :#{type}" unless trace?
64
+ raise ArgumentError, "You can only merge a :trace type, but trying :#{type}" unless another_trace.trace?
65
+
66
+ another_trace.value.each do |key, sub_trace|
67
+ if sub_trace.trace?
68
+ value[key].merge! sub_trace
69
+ else
70
+ value[key] = sub_trace
71
+ end
72
+ end
73
+ end
74
+
75
+ def keep_if(...)
76
+ raise ArgumentError, "You can only filter :trace type, and this is :#{type}" unless trace?
77
+ value.keep_if(...)
78
+ end
79
+
80
+ def clear() ; value.clear; end
81
+
82
+ def trace?() ; type == :trace; end
83
+
84
+ def to_h
85
+ if trace?
86
+ value.transform_values(&:to_h).tap { _1.default_proc = nil }
87
+ else
88
+ {value: value, source: source}
89
+ end
90
+ end
91
+
92
+ def dup() ; self.class.new(type, value.dup, source); end
93
+
94
+ def pretty_print(q)
95
+ if trace?
96
+ q.nest(2) do
97
+ q.breakable ""
98
+ q.seplist(value, nil, :each) do |k, v|
99
+ q.group do
100
+ q.text k
101
+ q.text " =>"
102
+ q.breakable " " unless v.trace?
103
+ q.pp v
104
+ end
105
+ end
106
+ end
107
+ else
108
+ q.pp value
109
+ q.group(0, " (", ")") do
110
+ q.seplist(source, lambda { q.breakable " " }, :each) do |k, v|
111
+ q.group do
112
+ q.text k.to_s
113
+ q.text "="
114
+ q.text v.to_s
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ class << self
123
+ def capture
124
+ unless Settings.tracing_enabled
125
+ yield
126
+ return
127
+ end
128
+
129
+ trace = Trace.new
130
+ trace_stack.push trace
131
+ yield
132
+ trace_stack.last
133
+ ensure
134
+ trace_stack.pop
135
+ end
136
+
137
+ def trace_stack
138
+ (Thread.current[:__anyway__trace_stack__] ||= [])
139
+ end
140
+
141
+ def current_trace() ; trace_stack.last; end
142
+
143
+ alias tracing? current_trace
144
+
145
+ def source_stack
146
+ (Thread.current[:__anyway__trace_source_stack__] ||= [])
147
+ end
148
+
149
+ def current_trace_source
150
+ source_stack.last || accessor_source(caller_locations(2, 1).first)
151
+ end
152
+
153
+ def with_trace_source(src)
154
+ source_stack << src
155
+ yield
156
+ ensure
157
+ source_stack.pop
158
+ end
159
+
160
+ private
161
+
162
+ def accessor_source(location)
163
+ {type: :accessor, called_from: location.path_lineno}
164
+ end
165
+ end
166
+
167
+ module_function
168
+
169
+ def trace!(type, *path, **opts)
170
+ return yield unless Tracing.tracing?
171
+ source = {type: type}.merge(opts)
172
+ val = yield
173
+ if val.is_a?(Hash)
174
+ Tracing.current_trace.merge_values(val, **source)
175
+ else
176
+ Tracing.current_trace.record_value(val, *path, **source)
177
+ end
178
+ val
179
+ end
180
+ end
181
+ end
@@ -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)
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)
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
+ if superclass < Anyway::Config
110
+ superclass.defaults.deep_dup
111
+ else
112
+ new_empty_config
113
+ end => @defaults
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
+ if superclass < Anyway::Config
120
+ superclass.config_attributes.dup
121
+ else
122
+ []
123
+ end => @config_attributes
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
+ if superclass < Anyway::Config
138
+ superclass.required_attributes.dup
139
+ else
140
+ []
141
+ end => @required_attributes
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
+ if superclass <= Anyway::Config
158
+ superclass.load_callbacks.dup
159
+ else
160
+ []
161
+ end => @load_callbacks
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?
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
+ if superclass < Anyway::Config && superclass.explicit_config_name?
189
+ superclass.env_prefix
190
+ else
191
+ config_name.upcase
192
+ end => @env_prefix
204
193
  end
205
194
 
206
- def new_empty_config
207
- {}
208
- end
195
+ def new_empty_config() = {}
209
196
 
210
197
  private
211
198
 
@@ -228,9 +215,9 @@ module Anyway # :nodoc:
228
215
  def accessors_module
229
216
  return @accessors_module if instance_variable_defined?(:@accessors_module)
230
217
 
231
- @accessors_module = Module.new.tap do |mod|
218
+ Module.new.tap do |mod|
232
219
  include mod
233
- end
220
+ end => @accessors_module
234
221
  end
235
222
 
236
223
  def build_config_name
@@ -298,7 +285,7 @@ module Anyway # :nodoc:
298
285
  def load(overrides = nil)
299
286
  base_config = self.class.defaults.deep_dup
300
287
 
301
- trace = Tracing.capture do
288
+ Tracing.capture do
302
289
  Tracing.trace!(:defaults) { base_config }
303
290
 
304
291
  load_from_sources(
@@ -313,7 +300,7 @@ module Anyway # :nodoc:
313
300
 
314
301
  base_config.deep_merge!(overrides)
315
302
  end
316
- end
303
+ end => trace
317
304
 
318
305
  base_config.each do |key, val|
319
306
  write_config_attr(key.to_sym, val)
@@ -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)
345
330
 
346
- def to_h
347
- values.deep_dup.deep_freeze
348
- end
331
+ def to_h() = values.deep_dup.deep_freeze
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)
366
347
 
367
- def to_source_trace
368
- __trace__&.to_h
369
- end
348
+ def to_source_trace() = __trace__&.to_h
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}\" " \
@@ -55,9 +55,7 @@ module Anyway
55
55
  registry.each(&block)
56
56
  end
57
57
 
58
- def freeze
59
- registry.freeze
60
- end
58
+ def freeze() = registry.freeze
61
59
 
62
60
  private
63
61
 
@@ -15,9 +15,7 @@ module Anyway
15
15
  @local = local
16
16
  end
17
17
 
18
- def use_local?
19
- @local == true
20
- end
18
+ def use_local?() = @local == true
21
19
  end
22
20
  end
23
21
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "anyway/testing/helpers"
4
4
 
5
- if defined?(RSpec)
5
+ if defined?(RSpec::Core)
6
6
  RSpec.configure do |config|
7
7
  config.include(
8
8
  Anyway::Testing::Helpers,
@@ -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}"
19
17
  end
20
18
  end)
21
19
 
@@ -35,12 +33,12 @@ module Anyway
35
33
  end
36
34
 
37
35
  def record_value(val, *path, key, **opts)
38
- trace =
39
- if val.is_a?(Hash)
40
- Trace.new.tap { _1.merge_values(val, **opts) }
41
- else
42
- Trace.new(:value, val, **opts)
43
- end
36
+ if val.is_a?(Hash)
37
+ Trace.new.tap { _1.merge_values(val, **opts) }
38
+ else
39
+ Trace.new(:value, val, **opts)
40
+ end => trace
41
+
44
42
  target_trace = path.empty? ? self : value.dig(*path)
45
43
  target_trace.value[key.to_s] = trace
46
44
 
@@ -79,13 +77,9 @@ module Anyway
79
77
  value.keep_if(...)
80
78
  end
81
79
 
82
- def clear
83
- value.clear
84
- end
80
+ def clear() = value.clear
85
81
 
86
- def trace?
87
- type == :trace
88
- end
82
+ def trace?() = type == :trace
89
83
 
90
84
  def to_h
91
85
  if trace?
@@ -95,9 +89,7 @@ module Anyway
95
89
  end
96
90
  end
97
91
 
98
- def dup
99
- self.class.new(type, value.dup, source)
100
- end
92
+ def dup() = self.class.new(type, value.dup, source)
101
93
 
102
94
  def pretty_print(q)
103
95
  if trace?
@@ -146,9 +138,7 @@ module Anyway
146
138
  (Thread.current[:__anyway__trace_stack__] ||= [])
147
139
  end
148
140
 
149
- def current_trace
150
- trace_stack.last
151
- end
141
+ def current_trace() = trace_stack.last
152
142
 
153
143
  alias tracing? current_trace
154
144
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anyway # :nodoc:
4
- VERSION = "2.0.2"
4
+ VERSION = "2.0.3"
5
5
  end
@@ -3,7 +3,7 @@
3
3
  require "ruby-next"
4
4
 
5
5
  require "ruby-next/language/setup"
6
- RubyNext::Language.setup_gem_load_path
6
+ RubyNext::Language.setup_gem_load_path(transpile: true)
7
7
 
8
8
  require "anyway/version"
9
9
 
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anyway_config
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-24 00:00:00.000000000 Z
11
+ date: 2020-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: ruby-next-core
14
+ name: ruby-next
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.5.1
19
+ version: 0.7.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.5.1
26
+ version: 0.7.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: ammeter
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '0.5'
89
+ version: '0.8'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '0.5'
96
+ version: '0.8'
97
97
  description: "\n Configuration DSL for Ruby libraries and applications.\n Allows
98
98
  you to easily follow the twelve-factor application principles (https://12factor.net/config).\n
99
99
  \ "
@@ -110,6 +110,10 @@ files:
110
110
  - lib/.rbnext/2.7/anyway/config.rb
111
111
  - lib/.rbnext/2.7/anyway/option_parser_builder.rb
112
112
  - lib/.rbnext/2.7/anyway/tracing.rb
113
+ - lib/.rbnext/2.8/anyway/config.rb
114
+ - lib/.rbnext/2.8/anyway/loaders.rb
115
+ - lib/.rbnext/2.8/anyway/loaders/base.rb
116
+ - lib/.rbnext/2.8/anyway/tracing.rb
113
117
  - lib/anyway.rb
114
118
  - lib/anyway/auto_cast.rb
115
119
  - lib/anyway/config.rb