anyway_config 2.0.0.pre2 → 2.0.0.rc1
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 +350 -0
- data/LICENSE.txt +1 -1
- data/README.md +444 -119
- data/lib/.rbnext/2.7/anyway/auto_cast.rb +33 -0
- data/lib/.rbnext/2.7/anyway/config.rb +404 -0
- data/lib/.rbnext/2.7/anyway/option_parser_builder.rb +31 -0
- data/lib/.rbnext/2.7/anyway/tracing.rb +187 -0
- data/lib/anyway/auto_cast.rb +33 -0
- data/lib/anyway/config.rb +235 -58
- data/lib/anyway/dynamic_config.rb +1 -1
- data/lib/anyway/env.rb +29 -19
- data/lib/anyway/ext/hash.rb +14 -6
- data/lib/anyway/loaders.rb +79 -0
- data/lib/anyway/loaders/base.rb +23 -0
- data/lib/anyway/loaders/env.rb +16 -0
- data/lib/anyway/loaders/yaml.rb +46 -0
- data/lib/anyway/option_parser_builder.rb +6 -3
- data/lib/anyway/optparse_config.rb +10 -6
- data/lib/anyway/rails.rb +16 -0
- data/lib/anyway/rails/config.rb +2 -69
- data/lib/anyway/rails/loaders.rb +5 -0
- data/lib/anyway/rails/loaders/credentials.rb +64 -0
- data/lib/anyway/rails/loaders/secrets.rb +39 -0
- data/lib/anyway/rails/loaders/yaml.rb +19 -0
- data/lib/anyway/rails/settings.rb +58 -0
- data/lib/anyway/railtie.rb +14 -2
- data/lib/anyway/settings.rb +29 -0
- data/lib/anyway/tracing.rb +187 -0
- data/lib/anyway/version.rb +1 -1
- data/lib/anyway_config.rb +27 -21
- data/lib/generators/anyway/app_config/USAGE +9 -0
- data/lib/generators/anyway/app_config/app_config_generator.rb +17 -0
- data/lib/generators/anyway/config/USAGE +13 -0
- data/lib/generators/anyway/config/config_generator.rb +46 -0
- data/lib/generators/anyway/config/templates/config.rb.tt +9 -0
- data/lib/generators/anyway/config/templates/config.yml.tt +13 -0
- data/lib/generators/anyway/install/USAGE +4 -0
- data/lib/generators/anyway/install/install_generator.rb +43 -0
- data/lib/generators/anyway/install/templates/application_config.rb.tt +17 -0
- metadata +75 -10
- data/lib/anyway/ext/string_serialize.rb +0 -38
- data/lib/anyway/loaders/env_loader.rb +0 -0
- data/lib/anyway/loaders/secrets_loader.rb +0 -0
- data/lib/anyway/loaders/yaml_loader.rb +0 -0
@@ -0,0 +1,187 @@
|
|
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
|
17
|
+
"#{path}:#{lineno}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end)
|
21
|
+
|
22
|
+
class Trace
|
23
|
+
UNDEF = Object.new
|
24
|
+
|
25
|
+
attr_reader :type, :value, :source
|
26
|
+
|
27
|
+
def initialize(type = :trace, value = UNDEF, **source)
|
28
|
+
@type = type
|
29
|
+
@source = source
|
30
|
+
@value = value == UNDEF ? Hash.new { |h, k| h[k] = Trace.new(:trace) } : value
|
31
|
+
end
|
32
|
+
|
33
|
+
def dig(*__rest__, &__block__)
|
34
|
+
value.dig(*__rest__, &__block__)
|
35
|
+
end
|
36
|
+
|
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
|
44
|
+
target_trace = path.empty? ? self : value.dig(*path)
|
45
|
+
target_trace.value[key.to_s] = trace
|
46
|
+
|
47
|
+
val
|
48
|
+
end
|
49
|
+
|
50
|
+
def merge_values(hash, **opts)
|
51
|
+
return hash unless hash
|
52
|
+
|
53
|
+
hash.each do |key, val|
|
54
|
+
if val.is_a?(Hash)
|
55
|
+
value[key.to_s].merge_values(val, **opts)
|
56
|
+
else
|
57
|
+
value[key.to_s] = Trace.new(:value, val, **opts)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
hash
|
62
|
+
end
|
63
|
+
|
64
|
+
def merge!(another_trace)
|
65
|
+
raise ArgumentError, "You can only merge into a :trace type, and this is :#{type}" unless trace?
|
66
|
+
raise ArgumentError, "You can only merge a :trace type, but trying :#{type}" unless another_trace.trace?
|
67
|
+
|
68
|
+
another_trace.value.each do |key, sub_trace|
|
69
|
+
if sub_trace.trace?
|
70
|
+
value[key].merge! sub_trace
|
71
|
+
else
|
72
|
+
value[key] = sub_trace
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def keep_if(*__rest__, &__block__)
|
78
|
+
raise ArgumentError, "You can only filter :trace type, and this is :#{type}" unless trace?
|
79
|
+
value.keep_if(*__rest__, &__block__)
|
80
|
+
end
|
81
|
+
|
82
|
+
def clear
|
83
|
+
value.clear
|
84
|
+
end
|
85
|
+
|
86
|
+
def trace?
|
87
|
+
type == :trace
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_h
|
91
|
+
if trace?
|
92
|
+
value.transform_values(&:to_h).tap { |_1| _1.default_proc = nil }
|
93
|
+
else
|
94
|
+
{value: value, source: source}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def pretty_print(q)
|
99
|
+
if trace?
|
100
|
+
q.nest(2) do
|
101
|
+
q.breakable ""
|
102
|
+
q.seplist(value, nil, :each) do |k, v|
|
103
|
+
q.group do
|
104
|
+
q.text k
|
105
|
+
q.text " =>"
|
106
|
+
q.breakable " " unless v.trace?
|
107
|
+
q.pp v
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
else
|
112
|
+
q.pp value
|
113
|
+
q.group(0, " (", ")") do
|
114
|
+
q.seplist(source, lambda { q.breakable " " }, :each) do |k, v|
|
115
|
+
q.group do
|
116
|
+
q.text k.to_s
|
117
|
+
q.text "="
|
118
|
+
q.text v.to_s
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class << self
|
127
|
+
def capture
|
128
|
+
unless Settings.tracing_enabled
|
129
|
+
yield
|
130
|
+
return
|
131
|
+
end
|
132
|
+
|
133
|
+
trace = Trace.new
|
134
|
+
trace_stack.push trace
|
135
|
+
yield
|
136
|
+
trace_stack.last
|
137
|
+
ensure
|
138
|
+
trace_stack.pop
|
139
|
+
end
|
140
|
+
|
141
|
+
def trace_stack
|
142
|
+
(Thread.current[:__anyway__trace_stack__] ||= [])
|
143
|
+
end
|
144
|
+
|
145
|
+
def current_trace
|
146
|
+
trace_stack.last
|
147
|
+
end
|
148
|
+
|
149
|
+
alias tracing? current_trace
|
150
|
+
|
151
|
+
def source_stack
|
152
|
+
(Thread.current[:__anyway__trace_source_stack__] ||= [])
|
153
|
+
end
|
154
|
+
|
155
|
+
def current_trace_source
|
156
|
+
source_stack.last || accessor_source(caller_locations(2, 1).first)
|
157
|
+
end
|
158
|
+
|
159
|
+
def with_trace_source(src)
|
160
|
+
source_stack << src
|
161
|
+
yield
|
162
|
+
ensure
|
163
|
+
source_stack.pop
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def accessor_source(location)
|
169
|
+
{type: :accessor, called_from: location.path_lineno}
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
module_function
|
174
|
+
|
175
|
+
def trace!(type, *path, **opts)
|
176
|
+
return yield unless Tracing.tracing?
|
177
|
+
source = {type: type}.merge(opts)
|
178
|
+
val = yield
|
179
|
+
if val.is_a?(Hash)
|
180
|
+
Tracing.current_trace.merge_values(val, **source)
|
181
|
+
else
|
182
|
+
Tracing.current_trace.record_value(yield, *path, **source)
|
183
|
+
end
|
184
|
+
val
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anyway
|
4
|
+
module AutoCast
|
5
|
+
# Regexp to detect array values
|
6
|
+
# Array value is a values that contains at least one comma
|
7
|
+
# and doesn't start/end with quote
|
8
|
+
ARRAY_RXP = /\A[^'"].*\s*,\s*.*[^'"]\z/
|
9
|
+
|
10
|
+
def self.call(val)
|
11
|
+
return val unless String === val
|
12
|
+
|
13
|
+
case val
|
14
|
+
when ARRAY_RXP
|
15
|
+
val.split(/\s*,\s*/).map { call(_1) }
|
16
|
+
when /\A(true|t|yes|y)\z/i
|
17
|
+
true
|
18
|
+
when /\A(false|f|no|n)\z/i
|
19
|
+
false
|
20
|
+
when /\A(nil|null)\z/i
|
21
|
+
nil
|
22
|
+
when /\A\d+\z/
|
23
|
+
val.to_i
|
24
|
+
when /\A\d*\.\d+\z/
|
25
|
+
val.to_f
|
26
|
+
when /\A['"].*['"]\z/
|
27
|
+
val.gsub(/(\A['"]|['"]\z)/, "")
|
28
|
+
else
|
29
|
+
val
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/anyway/config.rb
CHANGED
@@ -3,24 +3,79 @@
|
|
3
3
|
require "anyway/optparse_config"
|
4
4
|
require "anyway/dynamic_config"
|
5
5
|
|
6
|
-
require "anyway/ext/deep_dup"
|
7
|
-
require "anyway/ext/deep_freeze"
|
8
|
-
require "anyway/ext/hash"
|
9
|
-
require "anyway/ext/string_serialize"
|
10
|
-
|
11
6
|
module Anyway # :nodoc:
|
7
|
+
using RubyNext
|
12
8
|
using Anyway::Ext::DeepDup
|
13
9
|
using Anyway::Ext::DeepFreeze
|
14
10
|
using Anyway::Ext::Hash
|
15
|
-
|
11
|
+
|
12
|
+
using(Module.new do
|
13
|
+
refine Object do
|
14
|
+
def vm_object_id
|
15
|
+
(object_id << 1).to_s(16)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end)
|
16
19
|
|
17
20
|
# Base config class
|
18
21
|
# Provides `attr_config` method to describe
|
19
22
|
# configuration parameters and set defaults
|
20
23
|
class Config
|
24
|
+
PARAM_NAME = /^[a-z_]([\w]+)?$/
|
25
|
+
|
26
|
+
# List of names that couldn't be used as config names
|
27
|
+
# (the class instance methods we use)
|
28
|
+
RESERVED_NAMES = %i[
|
29
|
+
config_name
|
30
|
+
env_prefix
|
31
|
+
values
|
32
|
+
class
|
33
|
+
clear
|
34
|
+
deconstruct_keys
|
35
|
+
dig
|
36
|
+
initialize
|
37
|
+
load
|
38
|
+
load_from_sources
|
39
|
+
option_parser
|
40
|
+
pretty_print
|
41
|
+
raise_validation_error
|
42
|
+
reload
|
43
|
+
resolve_config_path
|
44
|
+
to_h
|
45
|
+
to_source_trace
|
46
|
+
write_config_attr
|
47
|
+
].freeze
|
48
|
+
|
49
|
+
class Error < StandardError; end
|
50
|
+
class ValidationError < Error; end
|
51
|
+
|
21
52
|
include OptparseConfig
|
22
53
|
include DynamicConfig
|
23
54
|
|
55
|
+
class BlockCallback
|
56
|
+
attr_reader :block
|
57
|
+
|
58
|
+
def initialize(block)
|
59
|
+
@block = block
|
60
|
+
end
|
61
|
+
|
62
|
+
def apply_to(config)
|
63
|
+
config.instance_exec(&block)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class NamedCallback
|
68
|
+
attr_reader :name
|
69
|
+
|
70
|
+
def initialize(name)
|
71
|
+
@name = name
|
72
|
+
end
|
73
|
+
|
74
|
+
def apply_to(config)
|
75
|
+
config.send(name)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
24
79
|
class << self
|
25
80
|
def attr_config(*args, **hargs)
|
26
81
|
new_defaults = hargs.deep_dup
|
@@ -28,9 +83,27 @@ module Anyway # :nodoc:
|
|
28
83
|
|
29
84
|
defaults.merge! new_defaults
|
30
85
|
|
31
|
-
new_keys = (args + new_defaults.keys) - config_attributes
|
86
|
+
new_keys = ((args + new_defaults.keys) - config_attributes)
|
87
|
+
|
88
|
+
validate_param_names! new_keys.map(&:to_s)
|
89
|
+
|
90
|
+
new_keys.map!(&:to_sym)
|
91
|
+
|
92
|
+
unless (reserved_names = (new_keys & RESERVED_NAMES)).empty?
|
93
|
+
raise ArgumentError, "Can not use the following reserved names as config attrubutes: " \
|
94
|
+
"#{reserved_names.sort.map(&:to_s).join(", ")}"
|
95
|
+
end
|
96
|
+
|
32
97
|
config_attributes.push(*new_keys)
|
33
|
-
|
98
|
+
|
99
|
+
define_config_accessor(*new_keys)
|
100
|
+
|
101
|
+
# Define predicate methods ("param?") for attributes
|
102
|
+
# having `true` or `false` as default values
|
103
|
+
new_defaults.each do |key, val|
|
104
|
+
next unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
105
|
+
alias_method :"#{key}?", :"#{key}"
|
106
|
+
end
|
34
107
|
end
|
35
108
|
|
36
109
|
def defaults
|
@@ -40,7 +113,7 @@ module Anyway # :nodoc:
|
|
40
113
|
if superclass < Anyway::Config
|
41
114
|
superclass.defaults.deep_dup
|
42
115
|
else
|
43
|
-
|
116
|
+
new_empty_config
|
44
117
|
end
|
45
118
|
end
|
46
119
|
|
@@ -55,6 +128,46 @@ module Anyway # :nodoc:
|
|
55
128
|
end
|
56
129
|
end
|
57
130
|
|
131
|
+
def required(*names)
|
132
|
+
unless (unknown_names = (names - config_attributes)).empty?
|
133
|
+
raise ArgumentError, "Unknown config param: #{unknown_names.join(",")}"
|
134
|
+
end
|
135
|
+
|
136
|
+
required_attributes.push(*names)
|
137
|
+
end
|
138
|
+
|
139
|
+
def required_attributes
|
140
|
+
return @required_attributes if instance_variable_defined?(:@required_attributes)
|
141
|
+
|
142
|
+
@required_attributes =
|
143
|
+
if superclass < Anyway::Config
|
144
|
+
superclass.required_attributes.dup
|
145
|
+
else
|
146
|
+
[]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def on_load(*names, &block)
|
151
|
+
raise ArgumentError, "Either methods or block should be specified, not both" if block_given? && !names.empty?
|
152
|
+
|
153
|
+
if block_given?
|
154
|
+
load_callbacks << BlockCallback.new(block)
|
155
|
+
else
|
156
|
+
load_callbacks.push(*names.map { NamedCallback.new(_1) })
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def load_callbacks
|
161
|
+
return @load_callbacks if instance_variable_defined?(:@load_callbacks)
|
162
|
+
|
163
|
+
@load_callbacks =
|
164
|
+
if superclass <= Anyway::Config
|
165
|
+
superclass.load_callbacks.dup
|
166
|
+
else
|
167
|
+
[]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
58
171
|
def config_name(val = nil)
|
59
172
|
return (@explicit_config_name = val.to_s) unless val.nil?
|
60
173
|
|
@@ -89,8 +202,36 @@ module Anyway # :nodoc:
|
|
89
202
|
end
|
90
203
|
end
|
91
204
|
|
205
|
+
def new_empty_config
|
206
|
+
{}
|
207
|
+
end
|
208
|
+
|
92
209
|
private
|
93
210
|
|
211
|
+
def define_config_accessor(*names)
|
212
|
+
names.each do |name|
|
213
|
+
accessors_module.module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
214
|
+
def #{name}=(val)
|
215
|
+
__trace__&.record_value(val, \"#{name}\", Tracing.current_trace_source)
|
216
|
+
# DEPRECATED: instance variable set will be removed in 2.1
|
217
|
+
@#{name} = values[:#{name}] = val
|
218
|
+
end
|
219
|
+
|
220
|
+
def #{name}
|
221
|
+
values[:#{name}]
|
222
|
+
end
|
223
|
+
RUBY
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def accessors_module
|
228
|
+
return @accessors_module if instance_variable_defined?(:@accessors_module)
|
229
|
+
|
230
|
+
@accessors_module = Module.new.tap do |mod|
|
231
|
+
include mod
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
94
235
|
def build_config_name
|
95
236
|
unless name
|
96
237
|
raise "Please, specify config name explicitly for anonymous class " \
|
@@ -107,8 +248,18 @@ module Anyway # :nodoc:
|
|
107
248
|
|
108
249
|
Regexp.last_match[1].tap(&:downcase!)
|
109
250
|
end
|
251
|
+
|
252
|
+
def validate_param_names!(names)
|
253
|
+
invalid_names = names.reject { |name| name =~ PARAM_NAME }
|
254
|
+
return if invalid_names.empty?
|
255
|
+
|
256
|
+
raise ArgumentError, "Invalid attr_config name: #{invalid_names.join(", ")}.\n" \
|
257
|
+
"Valid names must satisfy /#{PARAM_NAME.source}/."
|
258
|
+
end
|
110
259
|
end
|
111
260
|
|
261
|
+
on_load :validate_required_attributes!
|
262
|
+
|
112
263
|
attr_reader :config_name, :env_prefix
|
113
264
|
|
114
265
|
# Instantiate config instance.
|
@@ -120,108 +271,134 @@ module Anyway # :nodoc:
|
|
120
271
|
# # provide some values explicitly
|
121
272
|
# my_config = Anyway::Config.new({some: :value})
|
122
273
|
#
|
123
|
-
def initialize(overrides =
|
274
|
+
def initialize(overrides = nil)
|
124
275
|
@config_name = self.class.config_name
|
125
276
|
|
126
277
|
raise ArgumentError, "Config name is missing" unless @config_name
|
127
278
|
|
128
279
|
@env_prefix = self.class.env_prefix
|
280
|
+
@values = {}
|
129
281
|
|
130
282
|
load(overrides)
|
131
283
|
end
|
132
284
|
|
133
|
-
def reload(overrides =
|
285
|
+
def reload(overrides = nil)
|
134
286
|
clear
|
135
287
|
load(overrides)
|
136
288
|
self
|
137
289
|
end
|
138
290
|
|
139
291
|
def clear
|
140
|
-
|
141
|
-
|
142
|
-
end
|
292
|
+
values.clear
|
293
|
+
@__trace__ = nil
|
143
294
|
self
|
144
295
|
end
|
145
296
|
|
146
|
-
def load(overrides =
|
147
|
-
base_config =
|
297
|
+
def load(overrides = nil)
|
298
|
+
base_config = self.class.defaults.deep_dup
|
299
|
+
|
300
|
+
trace = Tracing.capture do
|
301
|
+
Tracing.trace!(:defaults) { base_config }
|
148
302
|
|
149
|
-
base_config.deep_merge!(
|
150
303
|
load_from_sources(
|
304
|
+
base_config,
|
151
305
|
name: config_name,
|
152
306
|
env_prefix: env_prefix,
|
153
307
|
config_path: resolve_config_path(config_name, env_prefix)
|
154
308
|
)
|
155
|
-
)
|
156
309
|
|
157
|
-
|
310
|
+
if overrides
|
311
|
+
Tracing.trace!(:load) { overrides }
|
312
|
+
|
313
|
+
base_config.deep_merge!(overrides)
|
314
|
+
end
|
315
|
+
end
|
158
316
|
|
159
317
|
base_config.each do |key, val|
|
160
|
-
|
318
|
+
write_config_attr(key.to_sym, val)
|
161
319
|
end
|
320
|
+
|
321
|
+
# Trace may contain unknown attributes
|
322
|
+
trace&.keep_if { |key| self.class.config_attributes.include?(key.to_sym) }
|
323
|
+
|
324
|
+
# Run on_load callbacks
|
325
|
+
self.class.load_callbacks.each { _1.apply_to(self) }
|
326
|
+
|
327
|
+
# Set trace after we write all the values to
|
328
|
+
# avoid changing the source to accessor
|
329
|
+
@__trace__ = trace
|
330
|
+
|
331
|
+
self
|
162
332
|
end
|
163
333
|
|
164
|
-
def load_from_sources(**options)
|
165
|
-
|
166
|
-
|
167
|
-
base_config.deep_merge!(config) if config
|
334
|
+
def load_from_sources(base_config, **options)
|
335
|
+
Anyway.loaders.each do |(_id, loader)|
|
336
|
+
base_config.deep_merge!(loader.call(**options))
|
168
337
|
end
|
169
338
|
base_config
|
170
339
|
end
|
171
340
|
|
172
|
-
def
|
173
|
-
|
174
|
-
yield load_from_env(options)
|
341
|
+
def dig(*keys)
|
342
|
+
values.dig(*keys)
|
175
343
|
end
|
176
344
|
|
177
|
-
def
|
178
|
-
|
345
|
+
def to_h
|
346
|
+
values.deep_dup.deep_freeze
|
347
|
+
end
|
179
348
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
end
|
349
|
+
def resolve_config_path(name, env_prefix)
|
350
|
+
Anyway.env.fetch(env_prefix).delete("conf") || Settings.default_config_path.call(name)
|
351
|
+
end
|
184
352
|
|
185
|
-
|
353
|
+
def deconstruct_keys(keys)
|
354
|
+
values.deconstruct_keys(keys)
|
186
355
|
end
|
187
356
|
|
188
|
-
def
|
189
|
-
|
357
|
+
def to_source_trace
|
358
|
+
__trace__&.to_h
|
190
359
|
end
|
191
360
|
|
192
|
-
def
|
193
|
-
self.class.
|
194
|
-
|
195
|
-
end.deep_dup.deep_freeze
|
361
|
+
def inspect
|
362
|
+
"#<#{self.class}:0x#{vm_object_id.rjust(16, "0")} config_name=\"#{config_name}\" env_prefix=\"#{env_prefix}\" " \
|
363
|
+
"values=#{values.inspect}>"
|
196
364
|
end
|
197
365
|
|
198
|
-
def
|
199
|
-
|
366
|
+
def pretty_print(q)
|
367
|
+
q.object_group self do
|
368
|
+
q.nest(1) do
|
369
|
+
q.breakable
|
370
|
+
q.text "config_name=#{config_name.inspect}"
|
371
|
+
q.breakable
|
372
|
+
q.text "env_prefix=#{env_prefix.inspect}"
|
373
|
+
q.breakable
|
374
|
+
q.text "values:"
|
375
|
+
q.pp __trace__
|
376
|
+
end
|
377
|
+
end
|
200
378
|
end
|
201
379
|
|
202
380
|
private
|
203
381
|
|
204
|
-
|
205
|
-
send("#{key}=", val) if respond_to?(key)
|
206
|
-
end
|
207
|
-
|
208
|
-
def load_from_yml(path)
|
209
|
-
return {} unless File.file?(path)
|
382
|
+
attr_reader :values, :__trace__
|
210
383
|
|
211
|
-
|
384
|
+
def validate_required_attributes!
|
385
|
+
self.class.required_attributes.select do |name|
|
386
|
+
values[name].nil? || (values[name].is_a?(String) && values[name].empty?)
|
387
|
+
end.then do |missing|
|
388
|
+
next if missing.empty?
|
389
|
+
raise_validation_error "The following config parameters are missing or empty: #{missing.join(", ")}"
|
390
|
+
end
|
212
391
|
end
|
213
392
|
|
214
|
-
def
|
215
|
-
|
393
|
+
def write_config_attr(key, val)
|
394
|
+
key = key.to_sym
|
395
|
+
return unless self.class.config_attributes.include?(key)
|
396
|
+
|
397
|
+
public_send(:"#{key}=", val)
|
216
398
|
end
|
217
399
|
|
218
|
-
def
|
219
|
-
|
220
|
-
if defined?(ERB)
|
221
|
-
YAML.safe_load(ERB.new(File.read(path)).result, [], [], true)
|
222
|
-
else
|
223
|
-
YAML.load_file(path)
|
224
|
-
end
|
400
|
+
def raise_validation_error(msg)
|
401
|
+
raise ValidationError, msg
|
225
402
|
end
|
226
403
|
end
|
227
404
|
end
|