ultra_settings 0.0.1.rc1 → 1.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/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__
|