anyway_config 2.0.5 → 2.2.1
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 +241 -181
- data/README.md +238 -13
- data/lib/.rbnext/1995.next/anyway/config.rb +438 -0
- data/lib/.rbnext/1995.next/anyway/dynamic_config.rb +31 -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/auto_cast.rb +39 -19
- data/lib/.rbnext/2.7/anyway/config.rb +61 -16
- data/lib/.rbnext/2.7/anyway/rails/loaders/yaml.rb +30 -0
- data/lib/.rbnext/2.7/anyway/rbs.rb +92 -0
- data/lib/.rbnext/2.7/anyway/settings.rb +79 -0
- data/lib/.rbnext/2.7/anyway/tracing.rb +6 -6
- data/lib/.rbnext/2.7/anyway/type_casting.rb +143 -0
- data/lib/.rbnext/3.0/anyway/auto_cast.rb +53 -0
- data/lib/.rbnext/{2.8 → 3.0}/anyway/config.rb +61 -16
- data/lib/.rbnext/{2.8 → 3.0}/anyway/loaders/base.rb +0 -0
- data/lib/.rbnext/{2.8 → 3.0}/anyway/loaders.rb +0 -0
- data/lib/.rbnext/{2.8 → 3.0}/anyway/tracing.rb +6 -6
- data/lib/anyway/auto_cast.rb +39 -19
- data/lib/anyway/config.rb +75 -30
- data/lib/anyway/dynamic_config.rb +6 -2
- data/lib/anyway/env.rb +1 -1
- data/lib/anyway/ext/deep_dup.rb +12 -0
- data/lib/anyway/ext/hash.rb +10 -12
- data/lib/anyway/loaders/base.rb +1 -1
- data/lib/anyway/loaders/env.rb +3 -1
- data/lib/anyway/loaders/yaml.rb +9 -5
- data/lib/anyway/option_parser_builder.rb +1 -3
- data/lib/anyway/optparse_config.rb +5 -7
- data/lib/anyway/rails/loaders/credentials.rb +4 -4
- data/lib/anyway/rails/loaders/secrets.rb +6 -8
- data/lib/anyway/rails/loaders/yaml.rb +11 -0
- data/lib/anyway/rails/settings.rb +9 -2
- data/lib/anyway/rbs.rb +92 -0
- data/lib/anyway/settings.rb +52 -2
- data/lib/anyway/tracing.rb +9 -9
- data/lib/anyway/type_casting.rb +134 -0
- data/lib/anyway/utils/deep_merge.rb +21 -0
- data/lib/anyway/version.rb +1 -1
- data/lib/anyway_config.rb +4 -0
- data/sig/anyway_config.rbs +129 -0
- metadata +42 -15
- data/lib/.rbnext/2.7/anyway/option_parser_builder.rb +0 -31
data/lib/anyway/config.rb
CHANGED
@@ -19,7 +19,7 @@ module Anyway # :nodoc:
|
|
19
19
|
# Provides `attr_config` method to describe
|
20
20
|
# configuration parameters and set defaults
|
21
21
|
class Config
|
22
|
-
PARAM_NAME = /^[a-z_](
|
22
|
+
PARAM_NAME = /^[a-z_](\w+)?$/
|
23
23
|
|
24
24
|
# List of names that couldn't be used as config names
|
25
25
|
# (the class instance methods we use)
|
@@ -40,12 +40,15 @@ module Anyway # :nodoc:
|
|
40
40
|
raise_validation_error
|
41
41
|
reload
|
42
42
|
resolve_config_path
|
43
|
+
tap
|
43
44
|
to_h
|
44
45
|
to_source_trace
|
45
46
|
write_config_attr
|
47
|
+
__type_caster__
|
46
48
|
].freeze
|
47
49
|
|
48
50
|
class Error < StandardError; end
|
51
|
+
|
49
52
|
class ValidationError < Error; end
|
50
53
|
|
51
54
|
include OptparseConfig
|
@@ -106,21 +109,21 @@ module Anyway # :nodoc:
|
|
106
109
|
def defaults
|
107
110
|
return @defaults if instance_variable_defined?(:@defaults)
|
108
111
|
|
109
|
-
if superclass < Anyway::Config
|
112
|
+
@defaults = if superclass < Anyway::Config
|
110
113
|
superclass.defaults.deep_dup
|
111
114
|
else
|
112
115
|
new_empty_config
|
113
|
-
end
|
116
|
+
end
|
114
117
|
end
|
115
118
|
|
116
119
|
def config_attributes
|
117
120
|
return @config_attributes if instance_variable_defined?(:@config_attributes)
|
118
121
|
|
119
|
-
if superclass < Anyway::Config
|
122
|
+
@config_attributes = if superclass < Anyway::Config
|
120
123
|
superclass.config_attributes.dup
|
121
124
|
else
|
122
125
|
[]
|
123
|
-
end
|
126
|
+
end
|
124
127
|
end
|
125
128
|
|
126
129
|
def required(*names)
|
@@ -134,17 +137,17 @@ module Anyway # :nodoc:
|
|
134
137
|
def required_attributes
|
135
138
|
return @required_attributes if instance_variable_defined?(:@required_attributes)
|
136
139
|
|
137
|
-
if superclass < Anyway::Config
|
140
|
+
@required_attributes = if superclass < Anyway::Config
|
138
141
|
superclass.required_attributes.dup
|
139
142
|
else
|
140
143
|
[]
|
141
|
-
end
|
144
|
+
end
|
142
145
|
end
|
143
146
|
|
144
147
|
def on_load(*names, &block)
|
145
|
-
raise ArgumentError, "Either methods or block should be specified, not both" if
|
148
|
+
raise ArgumentError, "Either methods or block should be specified, not both" if block && !names.empty?
|
146
149
|
|
147
|
-
if
|
150
|
+
if block
|
148
151
|
load_callbacks << BlockCallback.new(block)
|
149
152
|
else
|
150
153
|
load_callbacks.push(*names.map { NamedCallback.new(_1) })
|
@@ -154,11 +157,11 @@ module Anyway # :nodoc:
|
|
154
157
|
def load_callbacks
|
155
158
|
return @load_callbacks if instance_variable_defined?(:@load_callbacks)
|
156
159
|
|
157
|
-
if superclass <= Anyway::Config
|
160
|
+
@load_callbacks = if superclass <= Anyway::Config
|
158
161
|
superclass.load_callbacks.dup
|
159
162
|
else
|
160
163
|
[]
|
161
|
-
end
|
164
|
+
end
|
162
165
|
end
|
163
166
|
|
164
167
|
def config_name(val = nil)
|
@@ -185,24 +188,64 @@ module Anyway # :nodoc:
|
|
185
188
|
|
186
189
|
return @env_prefix if instance_variable_defined?(:@env_prefix)
|
187
190
|
|
188
|
-
if superclass < Anyway::Config && superclass.explicit_config_name?
|
191
|
+
@env_prefix = if superclass < Anyway::Config && superclass.explicit_config_name?
|
189
192
|
superclass.env_prefix
|
190
193
|
else
|
191
194
|
config_name.upcase
|
192
|
-
end
|
195
|
+
end
|
193
196
|
end
|
194
197
|
|
195
198
|
def new_empty_config() = {}
|
196
199
|
|
200
|
+
def coerce_types(mapping)
|
201
|
+
coercion_mapping.deep_merge!(mapping)
|
202
|
+
end
|
203
|
+
|
204
|
+
def coercion_mapping
|
205
|
+
return @coercion_mapping if instance_variable_defined?(:@coercion_mapping)
|
206
|
+
|
207
|
+
@coercion_mapping = if superclass < Anyway::Config
|
208
|
+
superclass.coercion_mapping.deep_dup
|
209
|
+
else
|
210
|
+
{}
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def type_caster(val = nil)
|
215
|
+
return @type_caster unless val.nil?
|
216
|
+
|
217
|
+
@type_caster ||=
|
218
|
+
if coercion_mapping.empty?
|
219
|
+
fallback_type_caster
|
220
|
+
else
|
221
|
+
::Anyway::TypeCaster.new(coercion_mapping, fallback: fallback_type_caster)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def fallback_type_caster(val = nil)
|
226
|
+
return (@fallback_type_caster = val) unless val.nil?
|
227
|
+
|
228
|
+
return @fallback_type_caster if instance_variable_defined?(:@fallback_type_caster)
|
229
|
+
|
230
|
+
@fallback_type_caster = if superclass < Anyway::Config
|
231
|
+
superclass.fallback_type_caster.deep_dup
|
232
|
+
else
|
233
|
+
::Anyway::AutoCast
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def disable_auto_cast!
|
238
|
+
@fallback_type_caster = ::Anyway::NoCast
|
239
|
+
end
|
240
|
+
|
197
241
|
private
|
198
242
|
|
199
243
|
def define_config_accessor(*names)
|
200
244
|
names.each do |name|
|
201
245
|
accessors_module.module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
202
246
|
def #{name}=(val)
|
203
|
-
__trace__&.record_value(val, \"#{name}\", Tracing.current_trace_source)
|
204
|
-
|
205
|
-
@#{name} = values[:#{name}] = val
|
247
|
+
__trace__&.record_value(val, \"#{name}\", **Tracing.current_trace_source)
|
248
|
+
values[:#{name}] = val
|
206
249
|
end
|
207
250
|
|
208
251
|
def #{name}
|
@@ -215,9 +258,9 @@ module Anyway # :nodoc:
|
|
215
258
|
def accessors_module
|
216
259
|
return @accessors_module if instance_variable_defined?(:@accessors_module)
|
217
260
|
|
218
|
-
Module.new.tap do |mod|
|
261
|
+
@accessors_module = Module.new.tap do |mod|
|
219
262
|
include mod
|
220
|
-
end
|
263
|
+
end
|
221
264
|
end
|
222
265
|
|
223
266
|
def build_config_name
|
@@ -229,7 +272,7 @@ module Anyway # :nodoc:
|
|
229
272
|
# handle two cases:
|
230
273
|
# - SomeModule::Config => "some_module"
|
231
274
|
# - SomeConfig => "some"
|
232
|
-
unless name =~ /^(\w+)(
|
275
|
+
unless name =~ /^(\w+)(::)?Config$/
|
233
276
|
raise "Couldn't infer config name, please, specify it explicitly" \
|
234
277
|
"via `config_name :my_config`"
|
235
278
|
end
|
@@ -285,22 +328,19 @@ module Anyway # :nodoc:
|
|
285
328
|
def load(overrides = nil)
|
286
329
|
base_config = self.class.defaults.deep_dup
|
287
330
|
|
288
|
-
Tracing.capture do
|
331
|
+
trace = Tracing.capture do
|
289
332
|
Tracing.trace!(:defaults) { base_config }
|
290
333
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
env_prefix: env_prefix,
|
295
|
-
config_path: resolve_config_path(config_name, env_prefix)
|
296
|
-
)
|
334
|
+
config_path = resolve_config_path(config_name, env_prefix)
|
335
|
+
|
336
|
+
load_from_sources(base_config, name: config_name, env_prefix:, config_path:)
|
297
337
|
|
298
338
|
if overrides
|
299
339
|
Tracing.trace!(:load) { overrides }
|
300
340
|
|
301
|
-
|
341
|
+
Utils.deep_merge!(base_config, overrides)
|
302
342
|
end
|
303
|
-
end
|
343
|
+
end
|
304
344
|
|
305
345
|
base_config.each do |key, val|
|
306
346
|
write_config_attr(key.to_sym, val)
|
@@ -321,7 +361,7 @@ module Anyway # :nodoc:
|
|
321
361
|
|
322
362
|
def load_from_sources(base_config, **options)
|
323
363
|
Anyway.loaders.each do |(_id, loader)|
|
324
|
-
|
364
|
+
Utils.deep_merge!(base_config, loader.call(**options))
|
325
365
|
end
|
326
366
|
base_config
|
327
367
|
end
|
@@ -375,7 +415,7 @@ module Anyway # :nodoc:
|
|
375
415
|
values[name].nil? || (values[name].is_a?(String) && values[name].empty?)
|
376
416
|
end.then do |missing|
|
377
417
|
next if missing.empty?
|
378
|
-
raise_validation_error "The following config parameters are missing or empty: #{missing.join(", ")}"
|
418
|
+
raise_validation_error "The following config parameters for `#{self.class.name}(config_name: #{self.class.config_name})` are missing or empty: #{missing.join(", ")}"
|
379
419
|
end
|
380
420
|
end
|
381
421
|
|
@@ -383,11 +423,16 @@ module Anyway # :nodoc:
|
|
383
423
|
key = key.to_sym
|
384
424
|
return unless self.class.config_attributes.include?(key)
|
385
425
|
|
426
|
+
val = __type_caster__.coerce(key, val)
|
386
427
|
public_send(:"#{key}=", val)
|
387
428
|
end
|
388
429
|
|
389
430
|
def raise_validation_error(msg)
|
390
431
|
raise ValidationError, msg
|
391
432
|
end
|
433
|
+
|
434
|
+
def __type_caster__
|
435
|
+
self.class.type_caster
|
436
|
+
end
|
392
437
|
end
|
393
438
|
end
|
@@ -12,11 +12,15 @@ module Anyway
|
|
12
12
|
# my_config = Anyway::Config.for(:my_app)
|
13
13
|
# # will load data from config/my_app.yml, secrets.my_app, ENV["MY_APP_*"]
|
14
14
|
#
|
15
|
-
def for(name, **options)
|
15
|
+
def for(name, auto_cast: true, **options)
|
16
16
|
config = allocate
|
17
17
|
options[:env_prefix] ||= name.to_s.upcase
|
18
18
|
options[:config_path] ||= config.resolve_config_path(name, options[:env_prefix])
|
19
|
-
|
19
|
+
|
20
|
+
raw_config = config.load_from_sources(new_empty_config, name:, **options)
|
21
|
+
return raw_config unless auto_cast
|
22
|
+
|
23
|
+
AutoCast.call(raw_config)
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
data/lib/anyway/env.rb
CHANGED
@@ -49,7 +49,7 @@ module Anyway
|
|
49
49
|
path = key.sub(/^#{prefix}_/, "").downcase
|
50
50
|
|
51
51
|
paths = path.split("__")
|
52
|
-
trace!(:env, *paths, key:
|
52
|
+
trace!(:env, *paths, key:) { data.bury(type_cast.call(val), *paths) }
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
data/lib/anyway/ext/deep_dup.rb
CHANGED
data/lib/anyway/ext/hash.rb
CHANGED
@@ -5,18 +5,6 @@ module Anyway
|
|
5
5
|
# Extend Hash through refinements
|
6
6
|
module Hash
|
7
7
|
refine ::Hash do
|
8
|
-
# From ActiveSupport http://api.rubyonrails.org/classes/Hash.html#method-i-deep_merge
|
9
|
-
def deep_merge!(other_hash)
|
10
|
-
merge!(other_hash) do |key, this_value, other_value|
|
11
|
-
if this_value.is_a?(::Hash) && other_value.is_a?(::Hash)
|
12
|
-
this_value.deep_merge!(other_value)
|
13
|
-
this_value
|
14
|
-
else
|
15
|
-
other_value
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
8
|
def stringify_keys!
|
21
9
|
keys.each do |key|
|
22
10
|
value = delete(key)
|
@@ -38,6 +26,16 @@ module Anyway
|
|
38
26
|
end
|
39
27
|
hash[last_key] = val
|
40
28
|
end
|
29
|
+
|
30
|
+
def deep_merge!(other)
|
31
|
+
other.each do |k, v|
|
32
|
+
if key?(k) && self[k].is_a?(::Hash) && v.is_a?(::Hash)
|
33
|
+
self[k].deep_merge!(v)
|
34
|
+
else
|
35
|
+
self[k] = v
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
41
39
|
end
|
42
40
|
|
43
41
|
using self
|
data/lib/anyway/loaders/base.rb
CHANGED
data/lib/anyway/loaders/env.rb
CHANGED
@@ -6,7 +6,9 @@ module Anyway
|
|
6
6
|
module Loaders
|
7
7
|
class Env < Base
|
8
8
|
def call(env_prefix:, **_options)
|
9
|
-
|
9
|
+
env = ::Anyway::Env.new(type_cast: ::Anyway::NoCast)
|
10
|
+
|
11
|
+
env.fetch_with_trace(env_prefix).then do |(conf, trace)|
|
10
12
|
Tracing.current_trace&.merge!(trace)
|
11
13
|
conf
|
12
14
|
end
|
data/lib/anyway/loaders/yaml.rb
CHANGED
@@ -17,7 +17,7 @@ module Anyway
|
|
17
17
|
local_path = local_config_path(config_path)
|
18
18
|
local_config = trace!(:yml, path: relative_config_path(local_path).to_s) { load_local_yml(local_path) }
|
19
19
|
|
20
|
-
|
20
|
+
Utils.deep_merge!(base_config, local_config)
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
@@ -25,15 +25,19 @@ module Anyway
|
|
25
25
|
def parse_yml(path)
|
26
26
|
return {} unless File.file?(path)
|
27
27
|
require "yaml" unless defined?(::YAML)
|
28
|
+
|
29
|
+
# By default, YAML load will return `false` when the yaml document is
|
30
|
+
# empty. When this occurs, we return an empty hash instead, to match
|
31
|
+
# the interface when no config file is present.
|
28
32
|
if defined?(ERB)
|
29
|
-
::YAML.load(ERB.new(File.read(path)).result) # rubocop:disable Security/YAMLLoad
|
33
|
+
::YAML.load(ERB.new(File.read(path)).result) || {} # rubocop:disable Security/YAMLLoad
|
30
34
|
else
|
31
|
-
::YAML.load_file(path)
|
35
|
+
::YAML.load_file(path) || {}
|
32
36
|
end
|
33
37
|
end
|
34
38
|
|
35
|
-
|
36
|
-
|
39
|
+
alias_method :load_base_yml, :parse_yml
|
40
|
+
alias_method :load_local_yml, :parse_yml
|
37
41
|
|
38
42
|
def local_config_path(path)
|
39
43
|
path.sub(/\.yml/, ".local.yml")
|
@@ -8,8 +8,6 @@ module Anyway # :nodoc:
|
|
8
8
|
class << self
|
9
9
|
def call(options)
|
10
10
|
OptionParser.new do |opts|
|
11
|
-
opts.accept(AutoCast) { AutoCast.call(_1) }
|
12
|
-
|
13
11
|
options.each do |key, descriptor|
|
14
12
|
opts.on(*option_parser_on_args(key, **descriptor)) do |val|
|
15
13
|
yield [key, val]
|
@@ -20,7 +18,7 @@ module Anyway # :nodoc:
|
|
20
18
|
|
21
19
|
private
|
22
20
|
|
23
|
-
def option_parser_on_args(key, flag: false, desc: nil, type:
|
21
|
+
def option_parser_on_args(key, flag: false, desc: nil, type: ::String)
|
24
22
|
on_args = ["--#{key.to_s.tr("_", "-")}#{flag ? "" : " VALUE"}"]
|
25
23
|
on_args << type unless flag
|
26
24
|
on_args << desc unless desc.nil?
|
@@ -70,13 +70,11 @@ module Anyway
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def option_parser
|
73
|
-
@option_parser ||=
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
extension.call(parser, self)
|
79
|
-
end
|
73
|
+
@option_parser ||= OptionParserBuilder.call(self.class.option_parser_options) do |key, val|
|
74
|
+
write_config_attr(key, val)
|
75
|
+
end.tap do |parser|
|
76
|
+
self.class.option_parser_extensions.map do |extension|
|
77
|
+
extension.call(parser, self)
|
80
78
|
end
|
81
79
|
end
|
82
80
|
end
|
@@ -22,15 +22,15 @@ module Anyway
|
|
22
22
|
:credentials,
|
23
23
|
store: credentials_path
|
24
24
|
) do
|
25
|
-
::Rails.application.credentials.
|
25
|
+
::Rails.application.credentials.config[name.to_sym]
|
26
26
|
end.then do |creds|
|
27
|
-
|
27
|
+
Utils.deep_merge!(config, creds) if creds
|
28
28
|
end
|
29
29
|
|
30
30
|
if use_local?
|
31
31
|
trace!(:credentials, store: LOCAL_CONTENT_PATH) do
|
32
32
|
local_credentials(name)
|
33
|
-
end.then { |creds|
|
33
|
+
end.then { |creds| Utils.deep_merge!(config, creds) if creds }
|
34
34
|
end
|
35
35
|
|
36
36
|
config
|
@@ -48,7 +48,7 @@ module Anyway
|
|
48
48
|
key_path: ::Rails.root.join("config/credentials/local.key")
|
49
49
|
)
|
50
50
|
|
51
|
-
creds.
|
51
|
+
creds.config[name.to_sym]
|
52
52
|
end
|
53
53
|
|
54
54
|
def credentials_path
|
@@ -15,7 +15,7 @@ module Anyway
|
|
15
15
|
trace!(:secrets) do
|
16
16
|
secrets.public_send(name)
|
17
17
|
end.then do |secrets|
|
18
|
-
|
18
|
+
Utils.deep_merge!(config, secrets) if secrets
|
19
19
|
end
|
20
20
|
|
21
21
|
config
|
@@ -24,13 +24,11 @@ module Anyway
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def secrets
|
27
|
-
@secrets ||=
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
::Rails.application.remove_instance_variable(:@secrets)
|
33
|
-
end
|
27
|
+
@secrets ||= ::Rails.application.secrets.tap do |_|
|
28
|
+
# Reset secrets state if the app hasn't been initialized
|
29
|
+
# See https://github.com/palkan/anyway_config/issues/14
|
30
|
+
next if ::Rails.application.initialized?
|
31
|
+
::Rails.application.remove_instance_variable(:@secrets)
|
34
32
|
end
|
35
33
|
end
|
36
34
|
end
|
@@ -5,11 +5,22 @@ module Anyway
|
|
5
5
|
module Loaders
|
6
6
|
class YAML < Anyway::Loaders::YAML
|
7
7
|
def load_base_yml(*)
|
8
|
+
parsed_yml = super
|
9
|
+
return parsed_yml unless environmental?(parsed_yml)
|
10
|
+
|
8
11
|
super[::Rails.env] || {}
|
9
12
|
end
|
10
13
|
|
11
14
|
private
|
12
15
|
|
16
|
+
def environmental?(parsed_yml)
|
17
|
+
return true unless Settings.future.unwrap_known_environments
|
18
|
+
# likely
|
19
|
+
return true if parsed_yml.key?(::Rails.env)
|
20
|
+
# less likely
|
21
|
+
::Rails.application.config.anyway_config.known_environments.any? { parsed_yml.key?(_1) }
|
22
|
+
end
|
23
|
+
|
13
24
|
def relative_config_path(path)
|
14
25
|
Pathname.new(path).relative_path_from(::Rails.root)
|
15
26
|
end
|
@@ -9,8 +9,13 @@ end
|
|
9
9
|
|
10
10
|
module Anyway
|
11
11
|
class Settings
|
12
|
+
class Future
|
13
|
+
setting :unwrap_known_environments, true
|
14
|
+
end
|
15
|
+
|
12
16
|
class << self
|
13
17
|
attr_reader :autoload_static_config_path, :autoloader
|
18
|
+
attr_accessor :known_environments
|
14
19
|
|
15
20
|
if defined?(::Zeitwerk)
|
16
21
|
def autoload_static_config_path=(val)
|
@@ -22,10 +27,11 @@ module Anyway
|
|
22
27
|
|
23
28
|
@autoload_static_config_path = val
|
24
29
|
|
25
|
-
# See https://github.com/rails/rails/blob/8ab4fd12f18203b83d0f252db96d10731485ff6a/railties/lib/rails/autoloaders.rb#L10
|
30
|
+
# See Rails 6 https://github.com/rails/rails/blob/8ab4fd12f18203b83d0f252db96d10731485ff6a/railties/lib/rails/autoloaders.rb#L10
|
31
|
+
# and Rails 7 https://github.com/rails/rails/blob/5462fbd5de1900c1b1ce1c9dc11c1a2d8cdcd809/railties/lib/rails/autoloaders.rb#L15
|
26
32
|
@autoloader = Zeitwerk::Loader.new.tap do |loader|
|
27
33
|
loader.tag = "anyway.config"
|
28
|
-
loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
|
34
|
+
loader.inflector = defined?(ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector) ? ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector : ::Rails::Autoloaders::Inflector
|
29
35
|
loader.push_dir(::Rails.root.join(val))
|
30
36
|
loader.setup
|
31
37
|
end
|
@@ -56,5 +62,6 @@ module Anyway
|
|
56
62
|
end
|
57
63
|
|
58
64
|
self.default_config_path = ->(name) { ::Rails.root.join("config", "#{name}.yml") }
|
65
|
+
self.known_environments = %w[test development production]
|
59
66
|
end
|
60
67
|
end
|
data/lib/anyway/rbs.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anyway
|
4
|
+
module RBSGenerator
|
5
|
+
TYPE_TO_CLASS = {
|
6
|
+
string: "String",
|
7
|
+
integer: "Integer",
|
8
|
+
float: "Float",
|
9
|
+
date: "Date",
|
10
|
+
datetime: "DateTime",
|
11
|
+
uri: "URI",
|
12
|
+
boolean: "bool"
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
# Generate RBS signature from a config class
|
16
|
+
def to_rbs
|
17
|
+
*namespace, class_name = name.split("::")
|
18
|
+
|
19
|
+
buf = []
|
20
|
+
indent = 0
|
21
|
+
interface_name = "_Config"
|
22
|
+
|
23
|
+
if namespace.empty?
|
24
|
+
interface_name = "_#{class_name}"
|
25
|
+
else
|
26
|
+
buf << "module #{namespace.join("::")}"
|
27
|
+
indent += 1
|
28
|
+
end
|
29
|
+
|
30
|
+
# Using interface emulates a module we include to provide getters and setters
|
31
|
+
# (thus making `super` possible)
|
32
|
+
buf << "#{" " * indent}interface #{interface_name}"
|
33
|
+
indent += 1
|
34
|
+
|
35
|
+
# Generating setters and getters for config attributes
|
36
|
+
config_attributes.each do |param|
|
37
|
+
type = coercion_mapping[param] || defaults[param.to_s]
|
38
|
+
|
39
|
+
type =
|
40
|
+
case type
|
41
|
+
in NilClass
|
42
|
+
"untyped"
|
43
|
+
in Symbol
|
44
|
+
TYPE_TO_CLASS.fetch(type) { defaults[param] ? "Symbol" : "untyped" }
|
45
|
+
in Array
|
46
|
+
"Array[untyped]"
|
47
|
+
in array:, type:, **nil
|
48
|
+
"Array[#{TYPE_TO_CLASS.fetch(type, "untyped")}]"
|
49
|
+
in Hash
|
50
|
+
"Hash[string,untyped]"
|
51
|
+
in TrueClass | FalseClass
|
52
|
+
"bool"
|
53
|
+
else
|
54
|
+
type.class.to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
getter_type = type
|
58
|
+
getter_type = "#{type}?" unless required_attributes.include?(param)
|
59
|
+
|
60
|
+
buf << "#{" " * indent}def #{param}: () -> #{getter_type}"
|
61
|
+
buf << "#{" " * indent}def #{param}=: (#{type}) -> void"
|
62
|
+
|
63
|
+
if type == "bool" || type == "bool?"
|
64
|
+
buf << "#{" " * indent}def #{param}?: () -> #{getter_type}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
indent -= 1
|
69
|
+
buf << "#{" " * indent}end"
|
70
|
+
|
71
|
+
buf << ""
|
72
|
+
|
73
|
+
buf << "#{" " * indent}class #{class_name} < #{superclass.name}"
|
74
|
+
indent += 1
|
75
|
+
|
76
|
+
buf << "#{" " * indent}include #{interface_name}"
|
77
|
+
|
78
|
+
indent -= 1
|
79
|
+
buf << "#{" " * indent}end"
|
80
|
+
|
81
|
+
unless namespace.empty?
|
82
|
+
buf << "end"
|
83
|
+
end
|
84
|
+
|
85
|
+
buf << ""
|
86
|
+
|
87
|
+
buf.join("\n")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
Config.extend RBSGenerator
|
92
|
+
end
|