anyway_config 2.0.2 → 2.1.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 +215 -181
- data/README.md +70 -10
- data/lib/.rbnext/1995.next/anyway/config.rb +391 -0
- data/lib/.rbnext/1995.next/anyway/dynamic_config.rb +27 -0
- data/lib/.rbnext/1995.next/anyway/env.rb +56 -0
- data/lib/.rbnext/1995.next/anyway/loaders/base.rb +21 -0
- data/lib/.rbnext/1995.next/anyway/tracing.rb +181 -0
- data/lib/.rbnext/2.7/anyway/config.rb +46 -69
- data/lib/.rbnext/2.7/anyway/rails/loaders/yaml.rb +30 -0
- data/lib/.rbnext/2.7/anyway/settings.rb +79 -0
- data/lib/.rbnext/2.7/anyway/tracing.rb +18 -26
- data/lib/.rbnext/3.0/anyway/config.rb +391 -0
- data/lib/.rbnext/3.0/anyway/loaders.rb +77 -0
- data/lib/.rbnext/3.0/anyway/loaders/base.rb +21 -0
- data/lib/.rbnext/3.0/anyway/tracing.rb +183 -0
- data/lib/anyway/config.rb +46 -69
- data/lib/anyway/dynamic_config.rb +1 -1
- data/lib/anyway/env.rb +1 -1
- data/lib/anyway/ext/deep_dup.rb +6 -0
- data/lib/anyway/ext/hash.rb +0 -12
- data/lib/anyway/loaders.rb +1 -3
- data/lib/anyway/loaders/base.rb +2 -4
- data/lib/anyway/loaders/yaml.rb +4 -4
- data/lib/anyway/rails/loaders/credentials.rb +2 -2
- data/lib/anyway/rails/loaders/secrets.rb +1 -1
- data/lib/anyway/rails/loaders/yaml.rb +11 -0
- data/lib/anyway/rails/settings.rb +6 -0
- data/lib/anyway/settings.rb +52 -2
- data/lib/anyway/testing.rb +1 -1
- data/lib/anyway/tracing.rb +17 -27
- data/lib/anyway/utils/deep_merge.rb +21 -0
- data/lib/anyway/version.rb +1 -1
- data/lib/anyway_config.rb +3 -1
- metadata +20 -8
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[](
|
1
|
+
[](https://cultofmartians.com/tasks/anyway-config-options-parse.html#task)
|
2
2
|
[](https://rubygems.org/gems/anyway_config) [](https://github.com/palkan/anyway_config/actions)
|
3
3
|
[](https://github.com/palkan/anyway_config/actions)
|
4
4
|
|
@@ -329,7 +329,9 @@ and then use [Rails generators](#generators) to make your application Anyway Con
|
|
329
329
|
|
330
330
|
Your config is filled up with values from the following sources (ordered by priority from low to high):
|
331
331
|
|
332
|
-
|
332
|
+
1) **YAML configuration files**: `RAILS_ROOT/config/my_cool_gem.yml`.
|
333
|
+
|
334
|
+
Recognizes Rails environment, supports `ERB`:
|
333
335
|
|
334
336
|
```yml
|
335
337
|
test:
|
@@ -341,9 +343,51 @@ development:
|
|
341
343
|
port: 3000
|
342
344
|
```
|
343
345
|
|
344
|
-
|
346
|
+
### Multi-env configuration
|
347
|
+
|
348
|
+
_⚡️ This feature will be turned on by default in the future releases. You can turn it on now via `config.anyway_config.future.use :unwrap_known_environments`._
|
349
|
+
|
350
|
+
If the YML does not have keys that are one of the "known" Rails environments (development, production, test)—the same configuration will be available in all environments, similar to non-Rails behavior:
|
351
|
+
|
352
|
+
```yml
|
353
|
+
host: localhost
|
354
|
+
port: 3002
|
355
|
+
# These values will be active in all environments
|
356
|
+
```
|
357
|
+
|
358
|
+
To extend the list of known environments, use the setting in the relevant part of your Rails code:
|
359
|
+
|
360
|
+
```ruby
|
361
|
+
Rails.application.config.anyway_config.known_environments << "staging"
|
362
|
+
```
|
363
|
+
|
364
|
+
If your YML defines at least a single "environmental" top-level, you _have_ to separate all your settings per-environment. You can't mix and match:
|
365
|
+
|
366
|
+
```yml
|
367
|
+
staging:
|
368
|
+
host: localhost # This value will be loaded when Rails.env.staging? is true
|
369
|
+
|
370
|
+
port: 3002 # This value will not be loaded at all
|
371
|
+
```
|
372
|
+
|
373
|
+
You can specify the lookup path for YAML files in one of the following ways:
|
374
|
+
|
375
|
+
- By setting `config.anyway_config.default_config_path` to a target directory path:
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
config.anyway_config.default_config_path = "/etc/configs"
|
379
|
+
config.anyway_config.default_config_path = Rails.root.join("etc", "configs")
|
380
|
+
```
|
381
|
+
|
382
|
+
- By setting `config.anyway_config.default_config_path` to a Proc, which accepts a config name and returns the path:
|
345
383
|
|
346
|
-
|
384
|
+
```ruby
|
385
|
+
config.anyway_config.default_config_path = ->(name) { Rails.root.join("data", "configs", "#{name}.yml") }
|
386
|
+
```
|
387
|
+
|
388
|
+
- By overriding a specific config YML file path via the `<NAME>_CONF` env variable, e.g., `MYCOOLGEM_CONF=path/to/cool.yml`
|
389
|
+
|
390
|
+
2) **Rails secrets**: `Rails.application.secrets.my_cool_gem` (if `secrets.yml` present).
|
347
391
|
|
348
392
|
```yml
|
349
393
|
# config/secrets.yml
|
@@ -352,7 +396,7 @@ development:
|
|
352
396
|
port: 4444
|
353
397
|
```
|
354
398
|
|
355
|
-
|
399
|
+
3) **Rails credentials**: `Rails.application.credentials.my_cool_gem` (if supported):
|
356
400
|
|
357
401
|
```yml
|
358
402
|
my_cool_gem:
|
@@ -361,7 +405,7 @@ my_cool_gem:
|
|
361
405
|
|
362
406
|
**NOTE:** You can backport Rails 6 per-environment credentials to Rails 5.2 app using [this patch](https://gist.github.com/palkan/e27e4885535ff25753aefce45378e0cb).
|
363
407
|
|
364
|
-
|
408
|
+
4) **Environment variables**: `ENV['MYCOOLGEM_*']`.
|
365
409
|
|
366
410
|
See [environment variables](#environment-variables).
|
367
411
|
|
@@ -444,7 +488,7 @@ Alternatively, you can call `rails g anyway:app_config name param1 param2 ...`.
|
|
444
488
|
|
445
489
|
The default data loading mechanism for non-Rails applications is the following (ordered by priority from low to high):
|
446
490
|
|
447
|
-
|
491
|
+
1) **YAML configuration files**: `./config/<config-name>.yml`.
|
448
492
|
|
449
493
|
In pure Ruby apps, we do not know about _environments_ (`test`, `development`, `production`, etc.); thus, we assume that the YAML contains values for a single environment:
|
450
494
|
|
@@ -453,9 +497,25 @@ host: localhost
|
|
453
497
|
port: 3000
|
454
498
|
```
|
455
499
|
|
456
|
-
|
500
|
+
`ERB` is supported if `erb` is loaded (thus, you need to call `require "erb"` somewhere before loading configuration).
|
501
|
+
|
502
|
+
You can specify the lookup path for YAML files in one of the following ways:
|
503
|
+
|
504
|
+
- By setting `Anyway::Settings.default_config_path` to a target directory path:
|
505
|
+
|
506
|
+
```ruby
|
507
|
+
Anyway::Settings.default_config_path = "/etc/configs"
|
508
|
+
```
|
509
|
+
|
510
|
+
- By setting `Anyway::Settings.default_config_path` to a Proc, which accepts a config name and returns the path:
|
511
|
+
|
512
|
+
```ruby
|
513
|
+
Anyway::Settings.default_config_path = ->(name) { Rails.root.join("data", "configs", "#{name}.yml") }
|
514
|
+
```
|
515
|
+
|
516
|
+
- By overriding a specific config YML file path via the `<NAME>_CONF` env variable, e.g., `MYCOOLGEM_CONF=path/to/cool.yml`
|
457
517
|
|
458
|
-
|
518
|
+
2) **Environment variables**: `ENV['MYCOOLGEM_*']`.
|
459
519
|
|
460
520
|
See [environment variables](#environment-variables).
|
461
521
|
|
@@ -666,7 +726,7 @@ If you want to delete the env var, pass `nil` as the value.
|
|
666
726
|
|
667
727
|
This helper is automatically included to RSpec if `RAILS_ENV` or `RACK_ENV` env variable is equal to "test". It's only available for the example with the tag `type: :config` or with the path `spec/configs/...`.
|
668
728
|
|
669
|
-
You can add it manually by requiring `"anyway/testing/helpers"` and including the `Anyway::
|
729
|
+
You can add it manually by requiring `"anyway/testing/helpers"` and including the `Anyway::Testing::Helpers` module (into RSpec configuration or Minitest test class).
|
670
730
|
|
671
731
|
## OptionParser integration
|
672
732
|
|
@@ -0,0 +1,391 @@
|
|
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)
|
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
|
+
tap
|
44
|
+
to_h
|
45
|
+
to_source_trace
|
46
|
+
write_config_attr
|
47
|
+
].freeze
|
48
|
+
|
49
|
+
class Error < StandardError; end
|
50
|
+
|
51
|
+
class ValidationError < Error; end
|
52
|
+
|
53
|
+
include OptparseConfig
|
54
|
+
include DynamicConfig
|
55
|
+
|
56
|
+
class BlockCallback
|
57
|
+
attr_reader :block
|
58
|
+
|
59
|
+
def initialize(block)
|
60
|
+
@block = block
|
61
|
+
end
|
62
|
+
|
63
|
+
def apply_to(config)
|
64
|
+
config.instance_exec(&block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class NamedCallback
|
69
|
+
attr_reader :name
|
70
|
+
|
71
|
+
def initialize(name)
|
72
|
+
@name = name
|
73
|
+
end
|
74
|
+
|
75
|
+
def apply_to(config) = config.send(name)
|
76
|
+
end
|
77
|
+
|
78
|
+
class << self
|
79
|
+
def attr_config(*args, **hargs)
|
80
|
+
new_defaults = hargs.deep_dup
|
81
|
+
new_defaults.stringify_keys!
|
82
|
+
|
83
|
+
defaults.merge! new_defaults
|
84
|
+
|
85
|
+
new_keys = ((args + new_defaults.keys) - config_attributes)
|
86
|
+
|
87
|
+
validate_param_names! new_keys.map(&:to_s)
|
88
|
+
|
89
|
+
new_keys.map!(&:to_sym)
|
90
|
+
|
91
|
+
unless (reserved_names = (new_keys & RESERVED_NAMES)).empty?
|
92
|
+
raise ArgumentError, "Can not use the following reserved names as config attrubutes: " \
|
93
|
+
"#{reserved_names.sort.map(&:to_s).join(", ")}"
|
94
|
+
end
|
95
|
+
|
96
|
+
config_attributes.push(*new_keys)
|
97
|
+
|
98
|
+
define_config_accessor(*new_keys)
|
99
|
+
|
100
|
+
# Define predicate methods ("param?") for attributes
|
101
|
+
# having `true` or `false` as default values
|
102
|
+
new_defaults.each do |key, val|
|
103
|
+
next unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
104
|
+
alias_method :"#{key}?", :"#{key}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def defaults
|
109
|
+
return @defaults if instance_variable_defined?(:@defaults)
|
110
|
+
|
111
|
+
@defaults = if superclass < Anyway::Config
|
112
|
+
superclass.defaults.deep_dup
|
113
|
+
else
|
114
|
+
new_empty_config
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def config_attributes
|
119
|
+
return @config_attributes if instance_variable_defined?(:@config_attributes)
|
120
|
+
|
121
|
+
@config_attributes = if superclass < Anyway::Config
|
122
|
+
superclass.config_attributes.dup
|
123
|
+
else
|
124
|
+
[]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def required(*names)
|
129
|
+
unless (unknown_names = (names - config_attributes)).empty?
|
130
|
+
raise ArgumentError, "Unknown config param: #{unknown_names.join(",")}"
|
131
|
+
end
|
132
|
+
|
133
|
+
required_attributes.push(*names)
|
134
|
+
end
|
135
|
+
|
136
|
+
def required_attributes
|
137
|
+
return @required_attributes if instance_variable_defined?(:@required_attributes)
|
138
|
+
|
139
|
+
@required_attributes = if superclass < Anyway::Config
|
140
|
+
superclass.required_attributes.dup
|
141
|
+
else
|
142
|
+
[]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def on_load(*names, &block)
|
147
|
+
raise ArgumentError, "Either methods or block should be specified, not both" if block && !names.empty?
|
148
|
+
|
149
|
+
if block
|
150
|
+
load_callbacks << BlockCallback.new(block)
|
151
|
+
else
|
152
|
+
load_callbacks.push(*names.map { NamedCallback.new(_1) })
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def load_callbacks
|
157
|
+
return @load_callbacks if instance_variable_defined?(:@load_callbacks)
|
158
|
+
|
159
|
+
@load_callbacks = if superclass <= Anyway::Config
|
160
|
+
superclass.load_callbacks.dup
|
161
|
+
else
|
162
|
+
[]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def config_name(val = nil)
|
167
|
+
return (@explicit_config_name = val.to_s) unless val.nil?
|
168
|
+
|
169
|
+
return @config_name if instance_variable_defined?(:@config_name)
|
170
|
+
|
171
|
+
@config_name = explicit_config_name || build_config_name
|
172
|
+
end
|
173
|
+
|
174
|
+
def explicit_config_name
|
175
|
+
return @explicit_config_name if instance_variable_defined?(:@explicit_config_name)
|
176
|
+
|
177
|
+
@explicit_config_name =
|
178
|
+
if superclass.respond_to?(:explicit_config_name)
|
179
|
+
superclass.explicit_config_name
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def explicit_config_name?() = !explicit_config_name.nil?
|
184
|
+
|
185
|
+
def env_prefix(val = nil)
|
186
|
+
return (@env_prefix = val.to_s.upcase) unless val.nil?
|
187
|
+
|
188
|
+
return @env_prefix if instance_variable_defined?(:@env_prefix)
|
189
|
+
|
190
|
+
@env_prefix = if superclass < Anyway::Config && superclass.explicit_config_name?
|
191
|
+
superclass.env_prefix
|
192
|
+
else
|
193
|
+
config_name.upcase
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def new_empty_config() = {}
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def define_config_accessor(*names)
|
202
|
+
names.each do |name|
|
203
|
+
accessors_module.module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
204
|
+
def #{name}=(val)
|
205
|
+
__trace__&.record_value(val, \"#{name}\", **Tracing.current_trace_source)
|
206
|
+
values[:#{name}] = val
|
207
|
+
end
|
208
|
+
|
209
|
+
def #{name}
|
210
|
+
values[:#{name}]
|
211
|
+
end
|
212
|
+
RUBY
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def accessors_module
|
217
|
+
return @accessors_module if instance_variable_defined?(:@accessors_module)
|
218
|
+
|
219
|
+
@accessors_module = Module.new.tap do |mod|
|
220
|
+
include mod
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def build_config_name
|
225
|
+
unless name
|
226
|
+
raise "Please, specify config name explicitly for anonymous class " \
|
227
|
+
"via `config_name :my_config`"
|
228
|
+
end
|
229
|
+
|
230
|
+
# handle two cases:
|
231
|
+
# - SomeModule::Config => "some_module"
|
232
|
+
# - SomeConfig => "some"
|
233
|
+
unless name =~ /^(\w+)(::)?Config$/
|
234
|
+
raise "Couldn't infer config name, please, specify it explicitly" \
|
235
|
+
"via `config_name :my_config`"
|
236
|
+
end
|
237
|
+
|
238
|
+
Regexp.last_match[1].tap(&:downcase!)
|
239
|
+
end
|
240
|
+
|
241
|
+
def validate_param_names!(names)
|
242
|
+
invalid_names = names.reject { |name| name =~ PARAM_NAME }
|
243
|
+
return if invalid_names.empty?
|
244
|
+
|
245
|
+
raise ArgumentError, "Invalid attr_config name: #{invalid_names.join(", ")}.\n" \
|
246
|
+
"Valid names must satisfy /#{PARAM_NAME.source}/."
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
on_load :validate_required_attributes!
|
251
|
+
|
252
|
+
attr_reader :config_name, :env_prefix
|
253
|
+
|
254
|
+
# Instantiate config instance.
|
255
|
+
#
|
256
|
+
# Example:
|
257
|
+
#
|
258
|
+
# my_config = Anyway::Config.new()
|
259
|
+
#
|
260
|
+
# # provide some values explicitly
|
261
|
+
# my_config = Anyway::Config.new({some: :value})
|
262
|
+
#
|
263
|
+
def initialize(overrides = nil)
|
264
|
+
@config_name = self.class.config_name
|
265
|
+
|
266
|
+
raise ArgumentError, "Config name is missing" unless @config_name
|
267
|
+
|
268
|
+
@env_prefix = self.class.env_prefix
|
269
|
+
@values = {}
|
270
|
+
|
271
|
+
load(overrides)
|
272
|
+
end
|
273
|
+
|
274
|
+
def reload(overrides = nil)
|
275
|
+
clear
|
276
|
+
load(overrides)
|
277
|
+
self
|
278
|
+
end
|
279
|
+
|
280
|
+
def clear
|
281
|
+
values.clear
|
282
|
+
@__trace__ = nil
|
283
|
+
self
|
284
|
+
end
|
285
|
+
|
286
|
+
def load(overrides = nil)
|
287
|
+
base_config = self.class.defaults.deep_dup
|
288
|
+
|
289
|
+
trace = Tracing.capture do
|
290
|
+
Tracing.trace!(:defaults) { base_config }
|
291
|
+
|
292
|
+
config_path = resolve_config_path(config_name, env_prefix)
|
293
|
+
|
294
|
+
load_from_sources(base_config, name: config_name, env_prefix: env_prefix, config_path: config_path)
|
295
|
+
|
296
|
+
if overrides
|
297
|
+
Tracing.trace!(:load) { overrides }
|
298
|
+
|
299
|
+
Utils.deep_merge!(base_config, overrides)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
base_config.each do |key, val|
|
304
|
+
write_config_attr(key.to_sym, val)
|
305
|
+
end
|
306
|
+
|
307
|
+
# Trace may contain unknown attributes
|
308
|
+
trace&.keep_if { |key| self.class.config_attributes.include?(key.to_sym) }
|
309
|
+
|
310
|
+
# Run on_load callbacks
|
311
|
+
self.class.load_callbacks.each { _1.apply_to(self) }
|
312
|
+
|
313
|
+
# Set trace after we write all the values to
|
314
|
+
# avoid changing the source to accessor
|
315
|
+
@__trace__ = trace
|
316
|
+
|
317
|
+
self
|
318
|
+
end
|
319
|
+
|
320
|
+
def load_from_sources(base_config, **options)
|
321
|
+
Anyway.loaders.each do |(_id, loader)|
|
322
|
+
Utils.deep_merge!(base_config, loader.call(**options))
|
323
|
+
end
|
324
|
+
base_config
|
325
|
+
end
|
326
|
+
|
327
|
+
def dig(*keys) = values.dig(*keys)
|
328
|
+
|
329
|
+
def to_h() = values.deep_dup.deep_freeze
|
330
|
+
|
331
|
+
def dup
|
332
|
+
self.class.allocate.tap do |new_config|
|
333
|
+
%i[config_name env_prefix __trace__].each do |ivar|
|
334
|
+
new_config.instance_variable_set(:"@#{ivar}", send(ivar).dup)
|
335
|
+
end
|
336
|
+
new_config.instance_variable_set(:@values, values.deep_dup)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def resolve_config_path(name, env_prefix)
|
341
|
+
Anyway.env.fetch(env_prefix).delete("conf") || Settings.default_config_path.call(name)
|
342
|
+
end
|
343
|
+
|
344
|
+
def deconstruct_keys(keys) = values.deconstruct_keys(keys)
|
345
|
+
|
346
|
+
def to_source_trace() = __trace__&.to_h
|
347
|
+
|
348
|
+
def inspect
|
349
|
+
"#<#{self.class}:0x#{vm_object_id.rjust(16, "0")} config_name=\"#{config_name}\" env_prefix=\"#{env_prefix}\" " \
|
350
|
+
"values=#{values.inspect}>"
|
351
|
+
end
|
352
|
+
|
353
|
+
def pretty_print(q)
|
354
|
+
q.object_group self do
|
355
|
+
q.nest(1) do
|
356
|
+
q.breakable
|
357
|
+
q.text "config_name=#{config_name.inspect}"
|
358
|
+
q.breakable
|
359
|
+
q.text "env_prefix=#{env_prefix.inspect}"
|
360
|
+
q.breakable
|
361
|
+
q.text "values:"
|
362
|
+
q.pp __trace__
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
private
|
368
|
+
|
369
|
+
attr_reader :values, :__trace__
|
370
|
+
|
371
|
+
def validate_required_attributes!
|
372
|
+
self.class.required_attributes.select do |name|
|
373
|
+
values[name].nil? || (values[name].is_a?(String) && values[name].empty?)
|
374
|
+
end.then do |missing|
|
375
|
+
next if missing.empty?
|
376
|
+
raise_validation_error "The following config parameters are missing or empty: #{missing.join(", ")}"
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def write_config_attr(key, val)
|
381
|
+
key = key.to_sym
|
382
|
+
return unless self.class.config_attributes.include?(key)
|
383
|
+
|
384
|
+
public_send(:"#{key}=", val)
|
385
|
+
end
|
386
|
+
|
387
|
+
def raise_validation_error(msg)
|
388
|
+
raise ValidationError, msg
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|