runger_config 3.0.1 → 5.0.0
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 +4 -4
- data/CHANGELOG.md +44 -35
- data/README.md +83 -83
- data/lib/generators/runger/app_config/app_config_generator.rb +13 -0
- data/lib/generators/runger/config/config_generator.rb +49 -0
- data/lib/generators/runger/install/install_generator.rb +45 -0
- data/lib/generators/{anyway → runger}/install/templates/application_config.rb.tt +1 -1
- data/lib/{anyway → runger}/auto_cast.rb +4 -4
- data/lib/{anyway → runger}/config.rb +124 -104
- data/lib/runger/dynamic_config.rb +29 -0
- data/lib/runger/ejson_parser.rb +40 -0
- data/lib/runger/env.rb +70 -0
- data/lib/runger/ext/deep_dup.rb +45 -0
- data/lib/runger/ext/deep_freeze.rb +40 -0
- data/lib/runger/ext/flatten_names.rb +33 -0
- data/lib/runger/ext/hash.rb +37 -0
- data/lib/runger/ext/string_constantize.rb +21 -0
- data/lib/runger/loaders/base.rb +17 -0
- data/lib/runger/loaders/doppler.rb +57 -0
- data/lib/runger/loaders/ejson.rb +91 -0
- data/lib/runger/loaders/env.rb +12 -0
- data/lib/runger/loaders/yaml.rb +86 -0
- data/lib/runger/loaders.rb +75 -0
- data/lib/runger/option_parser_builder.rb +27 -0
- data/lib/{anyway → runger}/optparse_config.rb +15 -14
- data/lib/runger/rails/autoload.rb +38 -0
- data/lib/runger/rails/config.rb +19 -0
- data/lib/runger/rails/loaders/credentials.rb +58 -0
- data/lib/runger/rails/loaders/secrets.rb +31 -0
- data/lib/runger/rails/loaders/yaml.rb +4 -0
- data/lib/runger/rails/loaders.rb +5 -0
- data/lib/runger/rails/settings.rb +83 -0
- data/lib/runger/rails.rb +22 -0
- data/lib/{anyway → runger}/railtie.rb +9 -8
- data/lib/{anyway → runger}/rbs.rb +30 -30
- data/lib/runger/settings.rb +109 -0
- data/lib/runger/testing/helpers.rb +34 -0
- data/lib/{anyway → runger}/testing.rb +3 -3
- data/lib/runger/tracing.rb +195 -0
- data/lib/{anyway → runger}/type_casting.rb +19 -14
- data/lib/{anyway → runger}/utils/deep_merge.rb +2 -2
- data/lib/runger/utils/which.rb +16 -0
- data/lib/runger/version.rb +5 -0
- data/lib/{anyway.rb → runger.rb} +1 -1
- data/lib/runger_config.rb +56 -0
- data/sig/{anyway_config.rbs → runger_config.rbs} +1 -1
- metadata +68 -68
- data/lib/anyway/dynamic_config.rb +0 -31
- data/lib/anyway/ejson_parser.rb +0 -40
- data/lib/anyway/env.rb +0 -72
- data/lib/anyway/ext/deep_dup.rb +0 -48
- data/lib/anyway/ext/deep_freeze.rb +0 -44
- data/lib/anyway/ext/flatten_names.rb +0 -37
- data/lib/anyway/ext/hash.rb +0 -40
- data/lib/anyway/ext/string_constantize.rb +0 -24
- data/lib/anyway/loaders/base.rb +0 -21
- data/lib/anyway/loaders/doppler.rb +0 -61
- data/lib/anyway/loaders/ejson.rb +0 -89
- data/lib/anyway/loaders/env.rb +0 -16
- data/lib/anyway/loaders/yaml.rb +0 -83
- data/lib/anyway/loaders.rb +0 -77
- data/lib/anyway/option_parser_builder.rb +0 -29
- data/lib/anyway/rails/autoload.rb +0 -40
- data/lib/anyway/rails/config.rb +0 -23
- data/lib/anyway/rails/loaders/credentials.rb +0 -62
- data/lib/anyway/rails/loaders/secrets.rb +0 -35
- data/lib/anyway/rails/loaders/yaml.rb +0 -9
- data/lib/anyway/rails/loaders.rb +0 -5
- data/lib/anyway/rails/settings.rb +0 -83
- data/lib/anyway/rails.rb +0 -24
- data/lib/anyway/settings.rb +0 -111
- data/lib/anyway/testing/helpers.rb +0 -36
- data/lib/anyway/tracing.rb +0 -188
- data/lib/anyway/utils/which.rb +0 -18
- data/lib/anyway/version.rb +0 -5
- data/lib/anyway_config.rb +0 -49
- data/lib/generators/anyway/app_config/app_config_generator.rb +0 -17
- data/lib/generators/anyway/config/config_generator.rb +0 -46
- data/lib/generators/anyway/install/install_generator.rb +0 -47
- /data/lib/generators/{anyway → runger}/app_config/USAGE +0 -0
- /data/lib/generators/{anyway → runger}/config/USAGE +0 -0
- /data/lib/generators/{anyway → runger}/config/templates/config.rb.tt +0 -0
- /data/lib/generators/{anyway → runger}/config/templates/config.yml.tt +0 -0
- /data/lib/generators/{anyway → runger}/install/USAGE +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Base class for application config classes
|
4
|
-
class ApplicationConfig <
|
4
|
+
class ApplicationConfig < Runger::Config
|
5
5
|
class << self
|
6
6
|
# Make it possible to access a singleton config instance
|
7
7
|
# via class methods (i.e., without explicitly calling `instance`)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
3
|
+
module Runger
|
4
4
|
module AutoCast
|
5
5
|
# Regexp to detect array values
|
6
6
|
# Array value is a values that contains at least one comma
|
@@ -23,11 +23,11 @@ module Anyway
|
|
23
23
|
when /\A(nil|null)\z/i
|
24
24
|
nil
|
25
25
|
when /\A\d+\z/
|
26
|
-
val
|
26
|
+
Integer(val, 10)
|
27
27
|
when /\A\d*\.\d+\z/
|
28
|
-
val
|
28
|
+
Float(val)
|
29
29
|
when /\A['"].*['"]\z/
|
30
|
-
val.gsub(/(\A['"]|['"]\z)/,
|
30
|
+
val.gsub(/(\A['"]|['"]\z)/, '')
|
31
31
|
else
|
32
32
|
val
|
33
33
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'active_support/all'
|
4
|
+
require 'runger/dynamic_config'
|
5
|
+
require 'runger/optparse_config'
|
6
6
|
|
7
|
-
module
|
8
|
-
using
|
9
|
-
using
|
10
|
-
using
|
11
|
-
using
|
7
|
+
module Runger # :nodoc:
|
8
|
+
using Runger::Ext::DeepDup
|
9
|
+
using Runger::Ext::DeepFreeze
|
10
|
+
using Runger::Ext::Hash
|
11
|
+
using Runger::Ext::FlattenNames
|
12
12
|
|
13
13
|
using(Module.new do
|
14
14
|
refine Object do
|
@@ -20,6 +20,8 @@ module Anyway # :nodoc:
|
|
20
20
|
# Provides `attr_config` method to describe
|
21
21
|
# configuration parameters and set defaults
|
22
22
|
class Config
|
23
|
+
include OptparseConfig
|
24
|
+
include DynamicConfig
|
23
25
|
PARAM_NAME = /^[a-z_](\w+)?$/
|
24
26
|
|
25
27
|
# List of names that couldn't be used as config names
|
@@ -53,9 +55,6 @@ module Anyway # :nodoc:
|
|
53
55
|
|
54
56
|
class ValidationError < Error; end
|
55
57
|
|
56
|
-
include OptparseConfig
|
57
|
-
include DynamicConfig
|
58
|
-
|
59
58
|
class BlockCallback
|
60
59
|
attr_reader :block
|
61
60
|
|
@@ -83,17 +82,18 @@ module Anyway # :nodoc:
|
|
83
82
|
new_defaults = hargs.deep_dup
|
84
83
|
new_defaults.stringify_keys!
|
85
84
|
|
86
|
-
defaults.merge!
|
85
|
+
defaults.merge!(new_defaults)
|
87
86
|
|
88
87
|
new_keys = ((args + new_defaults.keys) - config_attributes)
|
89
88
|
|
90
|
-
validate_param_names!
|
89
|
+
validate_param_names!(new_keys.map(&:to_s))
|
91
90
|
|
92
91
|
new_keys.map!(&:to_sym)
|
93
92
|
|
94
93
|
unless (reserved_names = (new_keys & RESERVED_NAMES)).empty?
|
95
|
-
raise
|
96
|
-
|
94
|
+
raise(ArgumentError,
|
95
|
+
'Can not use the following reserved names as config attrubutes: ' \
|
96
|
+
"#{reserved_names.sort.map(&:to_s).join(', ')}")
|
97
97
|
end
|
98
98
|
|
99
99
|
config_attributes.push(*new_keys)
|
@@ -104,33 +104,39 @@ module Anyway # :nodoc:
|
|
104
104
|
# having `true` or `false` as default values
|
105
105
|
new_defaults.each do |key, val|
|
106
106
|
next unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
107
|
-
|
107
|
+
|
108
|
+
alias_method(:"#{key}?", :"#{key}")
|
108
109
|
end
|
109
110
|
end
|
110
111
|
|
111
112
|
def defaults
|
112
113
|
return @defaults if instance_variable_defined?(:@defaults)
|
113
114
|
|
114
|
-
@defaults =
|
115
|
-
superclass
|
116
|
-
|
117
|
-
|
118
|
-
|
115
|
+
@defaults =
|
116
|
+
if superclass < Runger::Config
|
117
|
+
superclass.defaults.deep_dup
|
118
|
+
else
|
119
|
+
new_empty_config
|
120
|
+
end
|
119
121
|
end
|
120
122
|
|
121
123
|
def config_attributes
|
122
124
|
return @config_attributes if instance_variable_defined?(:@config_attributes)
|
123
125
|
|
124
|
-
@config_attributes =
|
125
|
-
superclass
|
126
|
-
|
127
|
-
|
128
|
-
|
126
|
+
@config_attributes =
|
127
|
+
if superclass < Runger::Config
|
128
|
+
superclass.config_attributes.dup
|
129
|
+
else
|
130
|
+
[]
|
131
|
+
end
|
129
132
|
end
|
130
133
|
|
131
134
|
def required(*names, env: nil, **nested)
|
132
135
|
unknown_names = names + nested.keys - config_attributes
|
133
|
-
|
136
|
+
if unknown_names.any?
|
137
|
+
raise(ArgumentError,
|
138
|
+
"Unknown config param: #{unknown_names.join(',')}")
|
139
|
+
end
|
134
140
|
|
135
141
|
return unless Settings.matching_env?(env)
|
136
142
|
|
@@ -141,15 +147,19 @@ module Anyway # :nodoc:
|
|
141
147
|
def required_attributes
|
142
148
|
return @required_attributes if instance_variable_defined?(:@required_attributes)
|
143
149
|
|
144
|
-
@required_attributes =
|
145
|
-
superclass
|
146
|
-
|
147
|
-
|
148
|
-
|
150
|
+
@required_attributes =
|
151
|
+
if superclass < Runger::Config
|
152
|
+
superclass.required_attributes.dup
|
153
|
+
else
|
154
|
+
[]
|
155
|
+
end
|
149
156
|
end
|
150
157
|
|
151
158
|
def on_load(*names, &block)
|
152
|
-
|
159
|
+
if block && !names.empty?
|
160
|
+
raise(ArgumentError,
|
161
|
+
'Either methods or block should be specified, not both')
|
162
|
+
end
|
153
163
|
|
154
164
|
if block
|
155
165
|
load_callbacks << BlockCallback.new(block)
|
@@ -161,11 +171,12 @@ module Anyway # :nodoc:
|
|
161
171
|
def load_callbacks
|
162
172
|
return @load_callbacks if instance_variable_defined?(:@load_callbacks)
|
163
173
|
|
164
|
-
@load_callbacks =
|
165
|
-
superclass
|
166
|
-
|
167
|
-
|
168
|
-
|
174
|
+
@load_callbacks =
|
175
|
+
if superclass <= Runger::Config
|
176
|
+
superclass.load_callbacks.dup
|
177
|
+
else
|
178
|
+
[]
|
179
|
+
end
|
169
180
|
end
|
170
181
|
|
171
182
|
def config_name(val = nil)
|
@@ -192,11 +203,12 @@ module Anyway # :nodoc:
|
|
192
203
|
|
193
204
|
return @env_prefix if instance_variable_defined?(:@env_prefix)
|
194
205
|
|
195
|
-
@env_prefix =
|
196
|
-
superclass.
|
197
|
-
|
198
|
-
|
199
|
-
|
206
|
+
@env_prefix =
|
207
|
+
if superclass < Runger::Config && superclass.explicit_config_name?
|
208
|
+
superclass.env_prefix
|
209
|
+
else
|
210
|
+
config_name.upcase
|
211
|
+
end
|
200
212
|
end
|
201
213
|
|
202
214
|
def loader_options(val = nil)
|
@@ -204,34 +216,36 @@ module Anyway # :nodoc:
|
|
204
216
|
|
205
217
|
return @loader_options if instance_variable_defined?(:@loader_options)
|
206
218
|
|
207
|
-
@loader_options =
|
208
|
-
superclass
|
209
|
-
|
210
|
-
|
211
|
-
|
219
|
+
@loader_options =
|
220
|
+
if superclass < Runger::Config
|
221
|
+
superclass.loader_options
|
222
|
+
else
|
223
|
+
{}
|
224
|
+
end
|
212
225
|
end
|
213
226
|
|
214
227
|
def new_empty_config = {}
|
215
228
|
|
216
229
|
def coerce_types(mapping)
|
217
|
-
Utils.deep_merge!(coercion_mapping, mapping)
|
230
|
+
::Runger::Utils.deep_merge!(coercion_mapping, mapping)
|
218
231
|
|
219
232
|
mapping.each do |key, val|
|
220
233
|
type = val.is_a?(::Hash) ? val[:type] : val
|
221
234
|
next if type != :boolean
|
222
235
|
|
223
|
-
alias_method
|
236
|
+
alias_method(:"#{key}?", :"#{key}")
|
224
237
|
end
|
225
238
|
end
|
226
239
|
|
227
240
|
def coercion_mapping
|
228
241
|
return @coercion_mapping if instance_variable_defined?(:@coercion_mapping)
|
229
242
|
|
230
|
-
@coercion_mapping =
|
231
|
-
superclass
|
232
|
-
|
233
|
-
|
234
|
-
|
243
|
+
@coercion_mapping =
|
244
|
+
if superclass < Runger::Config
|
245
|
+
superclass.coercion_mapping.deep_dup
|
246
|
+
else
|
247
|
+
{}
|
248
|
+
end
|
235
249
|
end
|
236
250
|
|
237
251
|
def type_caster(val = nil)
|
@@ -241,7 +255,7 @@ module Anyway # :nodoc:
|
|
241
255
|
if coercion_mapping.empty?
|
242
256
|
fallback_type_caster
|
243
257
|
else
|
244
|
-
::
|
258
|
+
::Runger::TypeCaster.new(coercion_mapping, fallback: fallback_type_caster)
|
245
259
|
end
|
246
260
|
end
|
247
261
|
|
@@ -250,22 +264,23 @@ module Anyway # :nodoc:
|
|
250
264
|
|
251
265
|
return @fallback_type_caster if instance_variable_defined?(:@fallback_type_caster)
|
252
266
|
|
253
|
-
@fallback_type_caster =
|
254
|
-
superclass
|
255
|
-
|
256
|
-
|
257
|
-
|
267
|
+
@fallback_type_caster =
|
268
|
+
if superclass < Runger::Config
|
269
|
+
superclass.fallback_type_caster.deep_dup
|
270
|
+
else
|
271
|
+
::Runger::AutoCast
|
272
|
+
end
|
258
273
|
end
|
259
274
|
|
260
275
|
def disable_auto_cast!
|
261
|
-
@fallback_type_caster = ::
|
276
|
+
@fallback_type_caster = ::Runger::NoCast
|
262
277
|
end
|
263
278
|
|
264
279
|
private
|
265
280
|
|
266
281
|
def define_config_accessor(*names)
|
267
282
|
names.each do |name|
|
268
|
-
accessors_module.module_eval
|
283
|
+
accessors_module.module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
269
284
|
def #{name}=(val)
|
270
285
|
__trace__&.record_value(val, "#{name}", **Tracing.current_trace_source)
|
271
286
|
values[:#{name}] = val
|
@@ -281,31 +296,33 @@ module Anyway # :nodoc:
|
|
281
296
|
def accessors_module
|
282
297
|
return @accessors_module if instance_variable_defined?(:@accessors_module)
|
283
298
|
|
284
|
-
@accessors_module =
|
285
|
-
|
286
|
-
|
299
|
+
@accessors_module =
|
300
|
+
Module.new.tap do |mod|
|
301
|
+
include mod
|
302
|
+
end
|
287
303
|
end
|
288
304
|
|
289
305
|
def build_config_name
|
290
306
|
unless name
|
291
|
-
raise
|
292
|
-
|
307
|
+
raise('Please, specify config name explicitly for anonymous class ' \
|
308
|
+
'via `config_name :my_config`')
|
293
309
|
end
|
294
310
|
|
295
|
-
unless name.underscore.
|
296
|
-
raise
|
297
|
-
|
311
|
+
unless name.underscore.tr('/', '_') =~ /(\w+)_config\z/
|
312
|
+
raise("Couldn't infer config name, please, specify it explicitly " \
|
313
|
+
'via `config_name :my_config`')
|
298
314
|
end
|
299
315
|
|
300
|
-
Regexp.last_match[1].delete_suffix(
|
316
|
+
Regexp.last_match[1].delete_suffix('_config').tap(&:downcase!)
|
301
317
|
end
|
302
318
|
|
303
319
|
def validate_param_names!(names)
|
304
|
-
invalid_names = names.
|
320
|
+
invalid_names = names.grep_v(PARAM_NAME)
|
305
321
|
return if invalid_names.empty?
|
306
322
|
|
307
|
-
raise
|
308
|
-
"
|
323
|
+
raise(ArgumentError,
|
324
|
+
"Invalid attr_config name: #{invalid_names.join(', ')}.\n" \
|
325
|
+
"Valid names must satisfy /#{PARAM_NAME.source}/.")
|
309
326
|
end
|
310
327
|
end
|
311
328
|
|
@@ -317,15 +334,15 @@ module Anyway # :nodoc:
|
|
317
334
|
#
|
318
335
|
# Example:
|
319
336
|
#
|
320
|
-
# my_config =
|
337
|
+
# my_config = Runger::Config.new()
|
321
338
|
#
|
322
339
|
# # provide some values explicitly
|
323
|
-
# my_config =
|
340
|
+
# my_config = Runger::Config.new({some: :value})
|
324
341
|
#
|
325
342
|
def initialize(overrides = nil)
|
326
343
|
@config_name = self.class.config_name
|
327
344
|
|
328
|
-
raise
|
345
|
+
raise(ArgumentError, 'Config name is missing') unless @config_name
|
329
346
|
|
330
347
|
@env_prefix = self.class.env_prefix
|
331
348
|
@values = {}
|
@@ -348,25 +365,26 @@ module Anyway # :nodoc:
|
|
348
365
|
def load(overrides = nil)
|
349
366
|
base_config = self.class.defaults.deep_dup
|
350
367
|
|
351
|
-
trace =
|
352
|
-
Tracing.
|
368
|
+
trace =
|
369
|
+
Tracing.capture do
|
370
|
+
Tracing.trace!(:defaults) { base_config }
|
353
371
|
|
354
|
-
|
372
|
+
config_path = resolve_config_path(config_name, env_prefix)
|
355
373
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
374
|
+
load_from_sources(
|
375
|
+
base_config,
|
376
|
+
name: config_name,
|
377
|
+
env_prefix:,
|
378
|
+
config_path:,
|
379
|
+
**self.class.loader_options,
|
380
|
+
)
|
363
381
|
|
364
|
-
|
365
|
-
|
382
|
+
if overrides
|
383
|
+
Tracing.trace!(:load) { overrides }
|
366
384
|
|
367
|
-
|
385
|
+
::Runger::Utils.deep_merge!(base_config, overrides)
|
386
|
+
end
|
368
387
|
end
|
369
|
-
end
|
370
388
|
|
371
389
|
base_config.each do |key, val|
|
372
390
|
write_config_attr(key.to_sym, val)
|
@@ -385,9 +403,9 @@ module Anyway # :nodoc:
|
|
385
403
|
self
|
386
404
|
end
|
387
405
|
|
388
|
-
def load_from_sources(base_config, **)
|
389
|
-
|
390
|
-
Utils.deep_merge!(base_config, loader.call(**))
|
406
|
+
def load_from_sources(base_config, **options)
|
407
|
+
Runger.loaders.each do |(_id, loader)|
|
408
|
+
::Runger::Utils.deep_merge!(base_config, loader.call(**options))
|
391
409
|
end
|
392
410
|
base_config
|
393
411
|
end
|
@@ -406,7 +424,7 @@ module Anyway # :nodoc:
|
|
406
424
|
end
|
407
425
|
|
408
426
|
def resolve_config_path(name, env_prefix)
|
409
|
-
|
427
|
+
Runger.env.fetch(env_prefix).delete('conf') || Settings.default_config_path.call(name)
|
410
428
|
end
|
411
429
|
|
412
430
|
def deconstruct_keys(keys) = values.deconstruct_keys(keys)
|
@@ -414,20 +432,21 @@ module Anyway # :nodoc:
|
|
414
432
|
def to_source_trace = __trace__&.to_h
|
415
433
|
|
416
434
|
def inspect
|
417
|
-
"#<#{self.class}:0x#{vm_object_id.rjust(16,
|
418
|
-
|
435
|
+
"#<#{self.class}:0x#{vm_object_id.rjust(16,
|
436
|
+
'0')} config_name=\"#{config_name}\" env_prefix=\"#{env_prefix}\" " \
|
437
|
+
"values=#{values.inspect}>"
|
419
438
|
end
|
420
439
|
|
421
440
|
def pretty_print(q)
|
422
|
-
q.object_group
|
441
|
+
q.object_group(self) do
|
423
442
|
q.nest(1) do
|
424
443
|
q.breakable
|
425
444
|
q.text "config_name=#{config_name.inspect}"
|
426
445
|
q.breakable
|
427
446
|
q.text "env_prefix=#{env_prefix.inspect}"
|
428
447
|
q.breakable
|
429
|
-
q.text
|
430
|
-
q.pp
|
448
|
+
q.text 'values:'
|
449
|
+
q.pp(__trace__)
|
431
450
|
end
|
432
451
|
end
|
433
452
|
end
|
@@ -442,11 +461,12 @@ module Anyway # :nodoc:
|
|
442
461
|
|
443
462
|
def validate_required_attributes!
|
444
463
|
self.class.required_attributes.select do |name|
|
445
|
-
val = values.dig(*name.to_s.split(
|
464
|
+
val = values.dig(*name.to_s.split('.').map(&:to_sym))
|
446
465
|
val.nil? || (val.is_a?(String) && val.empty?)
|
447
466
|
end.then do |missing|
|
448
467
|
next if missing.empty?
|
449
|
-
|
468
|
+
|
469
|
+
raise_validation_error("The following config parameters for `#{self.class.name}(config_name: #{self.class.config_name})` are missing or empty: #{missing.join(', ')}")
|
450
470
|
end
|
451
471
|
end
|
452
472
|
|
@@ -459,7 +479,7 @@ module Anyway # :nodoc:
|
|
459
479
|
end
|
460
480
|
|
461
481
|
def raise_validation_error(msg)
|
462
|
-
raise
|
482
|
+
raise(ValidationError, msg)
|
463
483
|
end
|
464
484
|
|
465
485
|
def __type_caster__
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Adds ability to generate anonymous (class-less) config dynamicly
|
4
|
+
# (like Rails.application.config_for but using more data sources).
|
5
|
+
module Runger::DynamicConfig
|
6
|
+
module ClassMethods
|
7
|
+
# Load config as Hash by any name
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# my_config = Runger::Config.for(:my_app)
|
12
|
+
# # will load data from config/my_app.yml, secrets.my_app, ENV["MY_APP_*"]
|
13
|
+
#
|
14
|
+
def for(name, auto_cast: true, **options)
|
15
|
+
config = allocate
|
16
|
+
options[:env_prefix] ||= name.to_s.upcase
|
17
|
+
options[:config_path] ||= config.resolve_config_path(name, options[:env_prefix])
|
18
|
+
|
19
|
+
raw_config = config.load_from_sources(new_empty_config, name:, **options)
|
20
|
+
return raw_config unless auto_cast
|
21
|
+
|
22
|
+
::Runger::AutoCast.call(raw_config)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.included(base)
|
27
|
+
base.extend(ClassMethods)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'runger/ext/hash'
|
5
|
+
|
6
|
+
using Runger::Ext::Hash
|
7
|
+
|
8
|
+
class Runger::EJSONParser
|
9
|
+
attr_reader :bin_path
|
10
|
+
|
11
|
+
def initialize(bin_path = 'ejson')
|
12
|
+
@bin_path = bin_path
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(file_path)
|
16
|
+
return unless File.exist?(file_path)
|
17
|
+
|
18
|
+
raw_content = nil
|
19
|
+
|
20
|
+
stdout, stderr, status = Open3.capture3("#{bin_path} decrypt #{file_path}")
|
21
|
+
|
22
|
+
if status.success?
|
23
|
+
raw_content = JSON.parse(stdout.chomp)
|
24
|
+
else
|
25
|
+
Kernel.warn("Failed to decrypt #{file_path}: #{stderr}")
|
26
|
+
end
|
27
|
+
|
28
|
+
return unless raw_content
|
29
|
+
|
30
|
+
raw_content.deep_transform_keys do |key|
|
31
|
+
if key[0] == '_'
|
32
|
+
# rubocop:disable Performance/ArraySemiInfiniteRangeSlice
|
33
|
+
key[1..]
|
34
|
+
# rubocop:enable Performance/ArraySemiInfiniteRangeSlice
|
35
|
+
else
|
36
|
+
key
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/runger/env.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Parses environment variables and provides
|
4
|
+
# method-like access
|
5
|
+
class Runger::Env
|
6
|
+
using Runger::Ext::DeepDup
|
7
|
+
using Runger::Ext::Hash
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def from_hash(hash, prefix: nil, memo: {})
|
11
|
+
hash.each do |key, value|
|
12
|
+
prefix_with_key = (prefix && !prefix.empty?) ? "#{prefix}_#{key.to_s.upcase}" : key.to_s.upcase
|
13
|
+
|
14
|
+
if value.is_a?(Hash)
|
15
|
+
from_hash(value, prefix: "#{prefix_with_key}_", memo:)
|
16
|
+
else
|
17
|
+
memo[prefix_with_key] = value.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
memo
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
include ::Runger::Tracing
|
26
|
+
|
27
|
+
attr_reader :data, :traces, :type_cast, :env_container
|
28
|
+
|
29
|
+
def initialize(type_cast: ::Runger::AutoCast, env_container: ENV)
|
30
|
+
@type_cast = type_cast
|
31
|
+
@data = {}
|
32
|
+
@traces = {}
|
33
|
+
@env_container = env_container
|
34
|
+
end
|
35
|
+
|
36
|
+
def clear
|
37
|
+
data.clear
|
38
|
+
traces.clear
|
39
|
+
end
|
40
|
+
|
41
|
+
def fetch(prefix)
|
42
|
+
return data[prefix].deep_dup if data.key?(prefix)
|
43
|
+
|
44
|
+
::Runger::Tracing.capture do
|
45
|
+
data[prefix] = parse_env(prefix)
|
46
|
+
end.then do |trace|
|
47
|
+
traces[prefix] = trace
|
48
|
+
end
|
49
|
+
|
50
|
+
data[prefix].deep_dup
|
51
|
+
end
|
52
|
+
|
53
|
+
def fetch_with_trace(prefix)
|
54
|
+
[fetch(prefix), traces[prefix]]
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def parse_env(prefix)
|
60
|
+
match_prefix = prefix.empty? ? prefix : "#{prefix}_"
|
61
|
+
env_container.each_pair.with_object({}) do |(key, val), data|
|
62
|
+
next unless key.start_with?(match_prefix)
|
63
|
+
|
64
|
+
path = key.sub(/^#{match_prefix}/, '').downcase
|
65
|
+
|
66
|
+
paths = path.split('__')
|
67
|
+
trace!(:env, *paths, key:) { data.bury(type_cast.call(val), *paths) }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Extend Object through refinements
|
4
|
+
module Runger::Ext::DeepDup
|
5
|
+
refine ::Hash do
|
6
|
+
# Based on ActiveSupport http://api.rubyonrails.org/classes/Hash.html#method-i-deep_dup
|
7
|
+
def deep_dup
|
8
|
+
each_with_object(dup) do |(key, value), hash|
|
9
|
+
hash[key] =
|
10
|
+
if value.is_a?(::Hash) || value.is_a?(::Array)
|
11
|
+
value.deep_dup
|
12
|
+
else
|
13
|
+
value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
refine ::Array do
|
20
|
+
# From ActiveSupport http://api.rubyonrails.org/classes/Array.html#method-i-deep_dup
|
21
|
+
def deep_dup
|
22
|
+
map do |value|
|
23
|
+
if value.is_a?(::Hash) || value.is_a?(::Array)
|
24
|
+
value.deep_dup
|
25
|
+
else
|
26
|
+
value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
refine ::Object do
|
33
|
+
def deep_dup
|
34
|
+
dup
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
refine ::Module do
|
39
|
+
def deep_dup
|
40
|
+
self
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
using self
|
45
|
+
end
|