ultra_settings 0.0.1.rc1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +320 -2
- data/VERSION +1 -1
- data/app/application.css +85 -0
- data/app/application.js +19 -0
- data/app/index.html.erb +93 -0
- data/app/layout.css +27 -0
- data/app/layout.html.erb +21 -0
- data/lib/ultra_settings/coerce.rb +98 -0
- data/lib/ultra_settings/configuration.rb +378 -79
- data/lib/ultra_settings/field.rb +57 -88
- data/lib/ultra_settings/rack_app.rb +21 -0
- data/lib/ultra_settings/railtie.rb +33 -0
- data/lib/ultra_settings/version.rb +5 -0
- data/lib/ultra_settings/web_view.rb +38 -0
- data/lib/ultra_settings/yaml_config.rb +78 -0
- data/lib/ultra_settings.rb +167 -74
- data/ultra_settings.gemspec +3 -4
- metadata +18 -35
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
module UltraSettings
|
6
|
+
# Utility functions for coercing values to other data types.
|
7
|
+
class Coerce
|
8
|
+
# rubocop:disable Lint/BooleanSymbol
|
9
|
+
FALSE_VALUES = Set.new([
|
10
|
+
false, 0,
|
11
|
+
"0", :"0",
|
12
|
+
"f", :f,
|
13
|
+
"F", :F,
|
14
|
+
"false", :false,
|
15
|
+
"FALSE", :FALSE,
|
16
|
+
"off", :off,
|
17
|
+
"OFF", :OFF
|
18
|
+
]).freeze
|
19
|
+
# rubocop:enable Lint/BooleanSymbol
|
20
|
+
|
21
|
+
class << self
|
22
|
+
# Cast a value to a specific type.
|
23
|
+
#
|
24
|
+
# @param value [Object]
|
25
|
+
# @param type [Symbol]
|
26
|
+
# @return [Object]
|
27
|
+
def coerce_value(value, type)
|
28
|
+
return nil if value.nil? || value == ""
|
29
|
+
|
30
|
+
case type
|
31
|
+
when :integer
|
32
|
+
value.is_a?(Integer) ? value : value.to_s&.to_i
|
33
|
+
when :float
|
34
|
+
value.is_a?(Float) ? value : value.to_s&.to_f
|
35
|
+
when :boolean
|
36
|
+
Coerce.boolean(value)
|
37
|
+
when :datetime
|
38
|
+
Coerce.time(value)
|
39
|
+
when :array
|
40
|
+
Array(value).map(&:to_s)
|
41
|
+
when :symbol
|
42
|
+
value.to_s.to_sym
|
43
|
+
else
|
44
|
+
value.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Cast variations of booleans (i.e. "true", "false", 1, 0, etc.) to actual boolean objects.
|
49
|
+
#
|
50
|
+
# @param value [Object]
|
51
|
+
# @return [Boolean]
|
52
|
+
def boolean(value)
|
53
|
+
if value == false
|
54
|
+
false
|
55
|
+
elsif blank?(value)
|
56
|
+
nil
|
57
|
+
else
|
58
|
+
!FALSE_VALUES.include?(value)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Cast a value to a Time object.
|
63
|
+
#
|
64
|
+
# @param value [Object]
|
65
|
+
# @return [Time]
|
66
|
+
def time(value)
|
67
|
+
value = nil if value.nil? || value.to_s.empty?
|
68
|
+
return nil if value.nil?
|
69
|
+
time = if value.is_a?(Numeric)
|
70
|
+
Time.at(value)
|
71
|
+
elsif value.respond_to?(:to_time)
|
72
|
+
value.to_time
|
73
|
+
else
|
74
|
+
Time.parse(value.to_s)
|
75
|
+
end
|
76
|
+
if time.respond_to?(:in_time_zone) && Time.respond_to?(:zone)
|
77
|
+
time = time.in_time_zone(Time.zone)
|
78
|
+
end
|
79
|
+
time
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Boolean] true if the value is nil or empty.
|
83
|
+
def blank?(value)
|
84
|
+
return true if value.nil?
|
85
|
+
if value.respond_to?(:empty?)
|
86
|
+
value.empty?
|
87
|
+
else
|
88
|
+
value.to_s.empty?
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [Boolean] true if the value is not nil and not empty.
|
93
|
+
def present?(value)
|
94
|
+
!blank?(value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "singleton"
|
4
|
-
|
5
3
|
module UltraSettings
|
6
4
|
class Configuration
|
7
5
|
include Singleton
|
@@ -9,24 +7,33 @@ module UltraSettings
|
|
9
7
|
ALLOWED_NAME_PATTERN = /\A[a-z_][a-zA-Z0-9_]*\z/
|
10
8
|
ALLOWED_TYPES = [:string, :symbol, :integer, :float, :boolean, :datetime, :array].freeze
|
11
9
|
|
12
|
-
class_attribute :environment_variables_disabled, instance_accessor: false, default: false
|
13
|
-
|
14
|
-
class_attribute :runtime_settings_disabled, instance_accessor: false, default: false
|
15
|
-
|
16
|
-
class_attribute :yaml_config_disabled, instance_accessor: false, default: false
|
17
|
-
|
18
|
-
class_attribute :env_var_delimiter, instance_accessor: false, default: "_"
|
19
|
-
|
20
|
-
class_attribute :setting_delimiter, instance_accessor: false, default: "."
|
21
|
-
|
22
|
-
class_attribute :env_var_upcase, instance_accessor: false, default: true
|
23
|
-
|
24
|
-
class_attribute :setting_upcase, instance_accessor: false, default: false
|
25
|
-
|
26
|
-
class_attribute :yaml_config_directory, instance_accessor: false, default: "config"
|
27
|
-
|
28
10
|
class << self
|
29
|
-
|
11
|
+
# Define a field on the configuration. This will create a getter method for the field.
|
12
|
+
# The field value will be read from the environment, runtime settings, or a YAML file
|
13
|
+
# and coerced to the specified type. Empty strings will be converted to nil.
|
14
|
+
#
|
15
|
+
# @param name [Symbol, String] The name of the field.
|
16
|
+
# @param type [Symbol] The type of the field. Valid types are :string, :symbol, :integer,
|
17
|
+
# :float, :boolean, :datetime, and :array. The default type is :string. The :array type
|
18
|
+
# will return an array of strings.
|
19
|
+
# @param description [String] A description of the field.
|
20
|
+
# @param default [Object] The default value of the field.
|
21
|
+
# @param default_if [Proc, Symbol] A proc that returns true if the default value should be used.
|
22
|
+
# By default, the default value will be used if the field evaluates to nil. You can also set
|
23
|
+
# this to a symbol with the name of an instance method to call.
|
24
|
+
# @param static [Boolean] If true, the field value should never be changed. This is useful for
|
25
|
+
# fields that are used at startup to set static values in the application. Static field cannot
|
26
|
+
# be read from runtime settings.
|
27
|
+
# @param runtime_setting [String, Symbol] The name of the runtime setting to use for the field.
|
28
|
+
# By default this will be the underscored name of the class plus a dot plus the field name
|
29
|
+
# (i.e. MyServiceConfiguration#foo becomes "my_service.foo").
|
30
|
+
# @param env_var [String, Symbol] The name of the environment variable to use for the field.
|
31
|
+
# By default this will be the underscored name of the class plus an underscore plus the field name
|
32
|
+
# all in uppercase (i.e. MyServiceConfiguration#foo becomes "MY_SERVICE_FOO").
|
33
|
+
# @param yaml_key [String, Symbol] The name of the YAML key to use for the field. By default
|
34
|
+
# this is the name of the field.
|
35
|
+
# @return [void]
|
36
|
+
def field(name, type: :string, description: nil, default: nil, default_if: nil, static: nil, runtime_setting: nil, env_var: nil, yaml_key: nil)
|
30
37
|
name = name.to_s
|
31
38
|
type = type.to_sym
|
32
39
|
static = !!static
|
@@ -39,27 +46,25 @@ module UltraSettings
|
|
39
46
|
raise ArgumentError.new("Invalid type: #{type.inspect}")
|
40
47
|
end
|
41
48
|
|
42
|
-
unless default_if.nil? || default_if.is_a?(Proc)
|
43
|
-
raise ArgumentError.new("default_if must be a Proc")
|
49
|
+
unless default_if.nil? || default_if.is_a?(Proc) || default_if.is_a?(Symbol)
|
50
|
+
raise ArgumentError.new("default_if must be a Proc or Symbol")
|
44
51
|
end
|
45
52
|
|
46
53
|
defined_fields[name] = Field.new(
|
47
54
|
name: name,
|
48
55
|
type: type,
|
56
|
+
description: description,
|
49
57
|
default: default,
|
50
58
|
default_if: default_if,
|
51
|
-
env_var: env_var,
|
52
|
-
|
53
|
-
yaml_key: yaml_key,
|
54
|
-
|
55
|
-
env_var_upcase: env_var_upcase,
|
56
|
-
setting_prefix: setting_prefix,
|
57
|
-
setting_upcase: setting_upcase
|
59
|
+
env_var: construct_env_var(name, env_var),
|
60
|
+
runtime_setting: (static ? nil : construct_runtime_setting(name, runtime_setting)),
|
61
|
+
yaml_key: construct_yaml_key(name, yaml_key),
|
62
|
+
static: static
|
58
63
|
)
|
59
64
|
|
60
65
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
|
61
66
|
def #{name}
|
62
|
-
__get_value__(#{name.inspect}
|
67
|
+
__get_value__(#{name.inspect})
|
63
68
|
end
|
64
69
|
RUBY
|
65
70
|
|
@@ -68,10 +73,41 @@ module UltraSettings
|
|
68
73
|
end
|
69
74
|
end
|
70
75
|
|
76
|
+
# List of the defined fields for the configuration.
|
77
|
+
#
|
78
|
+
# @return [Array<UltraSettings::Field>]
|
79
|
+
def fields
|
80
|
+
defined_fields.values
|
81
|
+
end
|
82
|
+
|
83
|
+
# Check if the field is defined on the configuration.
|
84
|
+
#
|
85
|
+
# @param name [Symbol, String] The name of the field.
|
86
|
+
# @return [Boolean]
|
87
|
+
def include?(name)
|
88
|
+
name = name.to_s
|
89
|
+
return true if defined_fields.include?(name)
|
90
|
+
|
91
|
+
if superclass <= Configuration
|
92
|
+
superclass.include?(name)
|
93
|
+
else
|
94
|
+
false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Override the default environment variable prefix. By default this wil be
|
99
|
+
# the underscored name of the class plus an underscore
|
100
|
+
# (i.e. MyServiceConfiguration has a prefix of "MY_SERVICE_").
|
101
|
+
#
|
102
|
+
# @param value [String]
|
103
|
+
# @return [void]
|
71
104
|
def env_var_prefix=(value)
|
72
105
|
@env_var_prefix = value&.to_s
|
73
106
|
end
|
74
107
|
|
108
|
+
# Get the environment variable prefix.
|
109
|
+
#
|
110
|
+
# @return [String]
|
75
111
|
def env_var_prefix
|
76
112
|
unless defined?(@env_var_prefix)
|
77
113
|
@env_var_prefix = default_env_var_prefix
|
@@ -79,89 +115,310 @@ module UltraSettings
|
|
79
115
|
@env_var_prefix
|
80
116
|
end
|
81
117
|
|
82
|
-
|
83
|
-
|
118
|
+
# Override the default runtime setting prefix. By default this wil be
|
119
|
+
# the underscored name of the class plus a dot (i.e. MyServiceConfiguration
|
120
|
+
# has a prefix of "my_service.").
|
121
|
+
#
|
122
|
+
# @param value [String]
|
123
|
+
# @return [void]
|
124
|
+
def runtime_setting_prefix=(value)
|
125
|
+
@runtime_setting_prefix = value&.to_s
|
84
126
|
end
|
85
127
|
|
86
|
-
|
87
|
-
|
88
|
-
|
128
|
+
# Get the runtime setting prefix.
|
129
|
+
#
|
130
|
+
# @return [String]
|
131
|
+
def runtime_setting_prefix
|
132
|
+
unless defined?(@runtime_setting_prefix)
|
133
|
+
@runtime_setting_prefix = default_runtime_setting_prefix
|
89
134
|
end
|
90
|
-
@
|
135
|
+
@runtime_setting_prefix
|
91
136
|
end
|
92
137
|
|
138
|
+
# Override the default YAML config path. By default this will be the
|
139
|
+
# file matching the underscored name of the class in the configuration
|
140
|
+
# directory (i.e. MyServiceConfiguration has a default config path of
|
141
|
+
# "my_service.yml").
|
142
|
+
#
|
143
|
+
# @param value [String, Pathname]
|
144
|
+
# @return [void]
|
93
145
|
def configuration_file=(value)
|
94
146
|
value = Pathname.new(value) if value.is_a?(String)
|
95
|
-
value = Rails.root + value if value && !value.absolute?
|
96
147
|
@configuration_file = value
|
97
148
|
end
|
98
149
|
|
150
|
+
# Get the YAML file path.
|
151
|
+
#
|
152
|
+
# @return [Pathname, nil]
|
99
153
|
def configuration_file
|
100
154
|
unless defined?(@configuration_file)
|
101
155
|
@configuration_file = default_configuration_file
|
102
156
|
end
|
103
|
-
@configuration_file
|
157
|
+
return nil? unless @configuration_file
|
158
|
+
|
159
|
+
path = @configuration_file
|
160
|
+
if path.relative? && yaml_config_path
|
161
|
+
path = yaml_config_path.join(path)
|
162
|
+
end
|
163
|
+
path.expand_path
|
164
|
+
end
|
165
|
+
|
166
|
+
# Set to true to disable loading configuration from environment variables.
|
167
|
+
#
|
168
|
+
# @param value [Boolean]
|
169
|
+
# @return [void]
|
170
|
+
def environment_variables_disabled=(value)
|
171
|
+
set_inheritable_class_attribute(:@environment_variables_disabled, !!value)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Check if loading configuration from environment variables is disabled.
|
175
|
+
#
|
176
|
+
# @return [Boolean]
|
177
|
+
def environment_variables_disabled?
|
178
|
+
get_inheritable_class_attribute(:@environment_variables_disabled, false)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Set to true to disable loading configuration from runtime settings.
|
182
|
+
#
|
183
|
+
# @param value [Boolean]
|
184
|
+
# @return [void]
|
185
|
+
def runtime_settings_disabled=(value)
|
186
|
+
set_inheritable_class_attribute(:@runtime_settings_disabled, !!value)
|
187
|
+
end
|
188
|
+
|
189
|
+
# Check if loading configuration from runtime settings is disabled.
|
190
|
+
#
|
191
|
+
# @return [Boolean]
|
192
|
+
def runtime_settings_disabled?
|
193
|
+
get_inheritable_class_attribute(:@runtime_settings_disabled, false)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Set to true to disable loading configuration from YAML files.
|
197
|
+
#
|
198
|
+
# @param value [Boolean]
|
199
|
+
# @return [void]
|
200
|
+
def yaml_config_disabled=(value)
|
201
|
+
set_inheritable_class_attribute(:@yaml_config_disabled, !!value)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Check if loading configuration from YAML files is disabled.
|
205
|
+
#
|
206
|
+
# @return [Boolean]
|
207
|
+
def yaml_config_disabled?
|
208
|
+
get_inheritable_class_attribute(:@yaml_config_disabled, false)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Set the environment variable delimiter used to construct the environment
|
212
|
+
# variable name for a field. By default this is an underscore.
|
213
|
+
#
|
214
|
+
# @param value [String]
|
215
|
+
def env_var_delimiter=(value)
|
216
|
+
set_inheritable_class_attribute(:@env_var_delimiter, value.to_s)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Get the environment variable delimiter.
|
220
|
+
#
|
221
|
+
# @return [String]
|
222
|
+
def env_var_delimiter
|
223
|
+
get_inheritable_class_attribute(:@env_var_delimiter, "_")
|
224
|
+
end
|
225
|
+
|
226
|
+
# Set the runtime setting delimiter used to construct the runtime setting
|
227
|
+
# name for a field. By default this is a dot.
|
228
|
+
#
|
229
|
+
# @param value [String]
|
230
|
+
# @return [void]
|
231
|
+
def runtime_setting_delimiter=(value)
|
232
|
+
set_inheritable_class_attribute(:@runtime_setting_delimiter, value.to_s)
|
233
|
+
end
|
234
|
+
|
235
|
+
# Get the runtime setting delimiter.
|
236
|
+
#
|
237
|
+
# @return [String]
|
238
|
+
def runtime_setting_delimiter
|
239
|
+
get_inheritable_class_attribute(:@runtime_setting_delimiter, ".")
|
104
240
|
end
|
105
241
|
|
242
|
+
# Set to true to upcase the environment variable name for a field. This
|
243
|
+
# is true by default.
|
244
|
+
#
|
245
|
+
# @param value [Boolean]
|
246
|
+
# @return [void]
|
247
|
+
def env_var_upcase=(value)
|
248
|
+
set_inheritable_class_attribute(:@env_var_upcase, !!value)
|
249
|
+
end
|
250
|
+
|
251
|
+
# Check if the environment variable name for a field should be upcased.
|
252
|
+
#
|
253
|
+
# @return [Boolean]
|
254
|
+
def env_var_upcase?
|
255
|
+
get_inheritable_class_attribute(:@env_var_upcase, true)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Set to true to upcase the runtime setting name for a field. This
|
259
|
+
# is false by default.
|
260
|
+
#
|
261
|
+
# @param value [Boolean]
|
262
|
+
# @return [void]
|
263
|
+
def runtime_setting_upcase=(value)
|
264
|
+
set_inheritable_class_attribute(:@runtime_setting_upcase, !!value)
|
265
|
+
end
|
266
|
+
|
267
|
+
# Check if the runtime setting name for a field should be upcased.
|
268
|
+
#
|
269
|
+
# @return [Boolean]
|
270
|
+
def runtime_setting_upcase?
|
271
|
+
get_inheritable_class_attribute(:@runtime_setting_upcase, false)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Set the directory where YAML files will be loaded from. By default this
|
275
|
+
# is the current working directory.
|
276
|
+
#
|
277
|
+
# @param value [String, Pathname]
|
278
|
+
# @return [void]
|
279
|
+
def yaml_config_path=(value)
|
280
|
+
value = Pathname.new(value) if value.is_a?(String)
|
281
|
+
value = value.expand_path if value&.relative?
|
282
|
+
set_inheritable_class_attribute(:@yaml_config_path, value)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Get the directory where YAML files will be loaded from.
|
286
|
+
#
|
287
|
+
# @return [Pathname, nil]
|
288
|
+
def yaml_config_path
|
289
|
+
get_inheritable_class_attribute(:@yaml_config_path, nil)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Set the environment namespace used in YAML file name. By default this
|
293
|
+
# is "development". Settings from the specific environment hash in the YAML
|
294
|
+
# file will be merged with base settings specified in the "shared" hash.
|
295
|
+
#
|
296
|
+
# @param value [String]
|
297
|
+
# @return [void]
|
298
|
+
def yaml_config_env=(value)
|
299
|
+
set_inheritable_class_attribute(:@yaml_config_env, value)
|
300
|
+
end
|
301
|
+
|
302
|
+
# Get the environment namespace used in YAML file name.
|
303
|
+
#
|
304
|
+
# @return [String]
|
305
|
+
def yaml_config_env
|
306
|
+
get_inheritable_class_attribute(:@yaml_config_env, "development")
|
307
|
+
end
|
308
|
+
|
309
|
+
# Override field values within a block.
|
310
|
+
#
|
311
|
+
# @param values [Hash<Symbol, Object>]] List of fields with the values they
|
312
|
+
# should return within the block.
|
313
|
+
# @return [Object] The value returned by the block.
|
314
|
+
def override!(values, &block)
|
315
|
+
instance.override!(values, &block)
|
316
|
+
end
|
317
|
+
|
318
|
+
# Load the YAML file for this configuration and return the values for the
|
319
|
+
# current environment.
|
320
|
+
#
|
321
|
+
# @return [Hash]
|
106
322
|
def load_yaml_config
|
107
323
|
return nil unless configuration_file
|
108
|
-
return nil unless configuration_file.exist?
|
324
|
+
return nil unless configuration_file.exist? && configuration_file.file?
|
109
325
|
|
110
|
-
|
326
|
+
YamlConfig.new(configuration_file, yaml_config_env).to_h
|
111
327
|
end
|
112
328
|
|
113
329
|
private
|
114
330
|
|
115
331
|
def defined_fields
|
116
332
|
unless defined?(@defined_fields)
|
117
|
-
|
118
|
-
if superclass
|
119
|
-
superclass.send(:defined_fields).
|
120
|
-
@defined_fields[name] = Field.new(
|
121
|
-
name: field.name,
|
122
|
-
type: field.type,
|
123
|
-
default: field.default,
|
124
|
-
default_if: field.default_if,
|
125
|
-
env_var: field.env_var,
|
126
|
-
setting_name: field.setting_name,
|
127
|
-
yaml_key: field.yaml_key,
|
128
|
-
env_var_prefix: env_var_prefix,
|
129
|
-
env_var_upcase: env_var_upcase,
|
130
|
-
setting_prefix: setting_prefix,
|
131
|
-
setting_upcase: setting_upcase
|
132
|
-
)
|
133
|
-
end
|
333
|
+
fields = {}
|
334
|
+
if superclass <= Configuration
|
335
|
+
fields = superclass.send(:defined_fields).dup
|
134
336
|
end
|
337
|
+
@defined_fields = fields
|
135
338
|
end
|
136
339
|
@defined_fields
|
137
340
|
end
|
138
341
|
|
139
342
|
def root_name
|
140
|
-
name.sub(/Configuration\z/, "")
|
343
|
+
name.sub(/Configuration\z/, "").split("::").collect do |part|
|
344
|
+
part.gsub(/([A-Z])(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }.downcase
|
345
|
+
end.join("/")
|
346
|
+
end
|
347
|
+
|
348
|
+
def set_inheritable_class_attribute(name, value)
|
349
|
+
instance_variable_set(name, value)
|
350
|
+
end
|
351
|
+
|
352
|
+
def get_inheritable_class_attribute(name, default = nil)
|
353
|
+
if instance_variable_defined?(name)
|
354
|
+
instance_variable_get(name)
|
355
|
+
elsif self != Configuration
|
356
|
+
superclass.send(:get_inheritable_class_attribute, name, default)
|
357
|
+
else
|
358
|
+
default
|
359
|
+
end
|
141
360
|
end
|
142
361
|
|
143
362
|
def default_configuration_file
|
144
|
-
path = Pathname.new(
|
145
|
-
path
|
146
|
-
path.join(*"#{root_name.underscore}.yml".split("/"))
|
363
|
+
path = Pathname.new(yaml_config_path)
|
364
|
+
path.join(*"#{root_name}.yml".split("/"))
|
147
365
|
end
|
148
366
|
|
149
367
|
def default_env_var_prefix
|
150
|
-
prefix = root_name.
|
151
|
-
prefix = prefix.upcase if env_var_upcase
|
368
|
+
prefix = root_name.gsub("/", env_var_delimiter) + env_var_delimiter
|
369
|
+
prefix = prefix.upcase if env_var_upcase?
|
152
370
|
prefix
|
153
371
|
end
|
154
372
|
|
155
|
-
def
|
156
|
-
prefix = root_name.
|
157
|
-
prefix = prefix.upcase if
|
373
|
+
def default_runtime_setting_prefix
|
374
|
+
prefix = root_name.gsub("/", runtime_setting_delimiter) + runtime_setting_delimiter
|
375
|
+
prefix = prefix.upcase if runtime_setting_upcase?
|
158
376
|
prefix
|
159
377
|
end
|
378
|
+
|
379
|
+
def construct_env_var(name, env_var)
|
380
|
+
return nil if env_var == false
|
381
|
+
return nil if environment_variables_disabled? && env_var.nil?
|
382
|
+
|
383
|
+
env_var = nil if env_var == true
|
384
|
+
|
385
|
+
if env_var.nil?
|
386
|
+
env_var = "#{env_var_prefix}#{name}"
|
387
|
+
env_var = env_var.upcase if env_var_upcase?
|
388
|
+
end
|
389
|
+
|
390
|
+
env_var
|
391
|
+
end
|
392
|
+
|
393
|
+
def construct_runtime_setting(name, runtime_setting)
|
394
|
+
return nil if runtime_setting == false
|
395
|
+
return nil if runtime_settings_disabled? && runtime_setting.nil?
|
396
|
+
|
397
|
+
runtime_setting = nil if runtime_setting == true
|
398
|
+
|
399
|
+
if runtime_setting.nil?
|
400
|
+
runtime_setting = "#{runtime_setting_prefix}#{name}"
|
401
|
+
runtime_setting = runtime_setting.upcase if runtime_setting_upcase?
|
402
|
+
end
|
403
|
+
|
404
|
+
runtime_setting
|
405
|
+
end
|
406
|
+
|
407
|
+
def construct_yaml_key(name, yaml_key)
|
408
|
+
return nil if yaml_key == false
|
409
|
+
return nil if yaml_config_disabled? && yaml_key.nil?
|
410
|
+
|
411
|
+
yaml_key = nil if yaml_key == true
|
412
|
+
yaml_key = name if yaml_key.nil?
|
413
|
+
|
414
|
+
yaml_key
|
415
|
+
end
|
160
416
|
end
|
161
417
|
|
162
418
|
def initialize
|
163
419
|
@mutex = Mutex.new
|
164
420
|
@memoized_values = {}
|
421
|
+
@override_values = {}
|
165
422
|
end
|
166
423
|
|
167
424
|
def [](name)
|
@@ -169,30 +426,60 @@ module UltraSettings
|
|
169
426
|
end
|
170
427
|
|
171
428
|
def include?(name)
|
172
|
-
self.class.
|
429
|
+
self.class.include?(name.to_s)
|
173
430
|
end
|
174
431
|
|
175
|
-
|
432
|
+
def override!(values, &block)
|
433
|
+
save_val = @override_values[Thread.current.object_id]
|
176
434
|
|
177
|
-
|
178
|
-
|
179
|
-
|
435
|
+
temp_values = (save_val || {}).dup
|
436
|
+
values.each do |key, value|
|
437
|
+
temp_values[key.to_s] = value
|
438
|
+
end
|
439
|
+
|
440
|
+
begin
|
441
|
+
@mutex.synchronize do
|
442
|
+
@override_values[Thread.current.object_id] = temp_values
|
443
|
+
end
|
444
|
+
yield
|
445
|
+
ensure
|
446
|
+
@mutex.synchronize do
|
447
|
+
@override_values[Thread.current.object_id] = save_val
|
448
|
+
end
|
180
449
|
end
|
450
|
+
end
|
181
451
|
|
452
|
+
def __source__(name)
|
453
|
+
field = self.class.send(:defined_fields)[name]
|
454
|
+
source = field.source(env: ENV, settings: UltraSettings.__runtime_settings__, yaml_config: __yaml_config__)
|
455
|
+
source || :default
|
456
|
+
end
|
457
|
+
|
458
|
+
private
|
459
|
+
|
460
|
+
def __get_value__(name)
|
182
461
|
field = self.class.send(:defined_fields)[name]
|
183
462
|
return nil unless field
|
184
463
|
|
185
|
-
if
|
186
|
-
|
464
|
+
if field.static? && @memoized_values.include?(name)
|
465
|
+
return @memoized_values[name]
|
187
466
|
end
|
188
467
|
|
189
|
-
|
190
|
-
|
191
|
-
|
468
|
+
if @override_values[Thread.current.object_id]&.include?(name)
|
469
|
+
value = field.coerce(@override_values[Thread.current.object_id][name])
|
470
|
+
else
|
471
|
+
env = ENV if field.env_var
|
472
|
+
settings = UltraSettings.__runtime_settings__ if field.runtime_setting
|
473
|
+
yaml_config = __yaml_config__ if field.yaml_key
|
192
474
|
|
193
|
-
|
475
|
+
value = field.value(yaml_config: yaml_config, env: env, settings: settings)
|
476
|
+
end
|
477
|
+
|
478
|
+
if __use_default?(value, field.default_if)
|
479
|
+
value = field.default
|
480
|
+
end
|
194
481
|
|
195
|
-
if static
|
482
|
+
if field.static?
|
196
483
|
@mutex.synchronize do
|
197
484
|
if @memoized_values.include?(name)
|
198
485
|
value = @memoized_values[name]
|
@@ -205,8 +492,20 @@ module UltraSettings
|
|
205
492
|
value
|
206
493
|
end
|
207
494
|
|
208
|
-
def
|
209
|
-
|
495
|
+
def __use_default?(value, default_if)
|
496
|
+
return true if value.nil?
|
497
|
+
|
498
|
+
if default_if.is_a?(Proc)
|
499
|
+
default_if.call(value)
|
500
|
+
elsif default_if.is_a?(Symbol)
|
501
|
+
begin
|
502
|
+
send(default_if, value)
|
503
|
+
rescue NoMethodError
|
504
|
+
raise NoMethodError, "default_if method `#{default_if}' not defined for #{self.class.name}"
|
505
|
+
end
|
506
|
+
else
|
507
|
+
false
|
508
|
+
end
|
210
509
|
end
|
211
510
|
|
212
511
|
def __yaml_config__
|