Class: UltraSettings::Configuration

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/ultra_settings/configuration.rb

Constant Summary collapse

ALLOWED_NAME_PATTERN =
/\A[a-z_][a-zA-Z0-9_]*\z/
ALLOWED_TYPES =
[:string, :symbol, :integer, :float, :boolean, :datetime, :array].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Returns a new instance of Configuration.



463
464
465
466
467
468
# File 'lib/ultra_settings/configuration.rb', line 463

def initialize
  @ultra_settings_mutex = Mutex.new
  @ultra_settings_memoized_values = {}
  @ultra_settings_override_values = {}
  @ultra_settings_yaml_config = nil
end

Class Method Details

.configuration_filePathname?

Get the YAML file path.

Returns:

  • (Pathname, nil)


176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/ultra_settings/configuration.rb', line 176

def configuration_file
  unless defined?(@configuration_file)
    default_file = default_configuration_file
    return nil if default_file.nil?

    @configuration_file = default_configuration_file
  end
  return nil? unless @configuration_file

  path = @configuration_file
  if path.relative? && yaml_config_path
    path = yaml_config_path.join(path)
  end
  path.expand_path
end

.configuration_file=(value) ⇒ void

This method returns an undefined value.

Override the default YAML config path. By default this will be the file matching the underscored name of the class in the configuration directory (i.e. MyServiceConfiguration has a default config path of “my_service.yml”).

Parameters:

  • value (String, Pathname, false, nil)


167
168
169
170
171
# File 'lib/ultra_settings/configuration.rb', line 167

def configuration_file=(value)
  value = nil if value == false
  value = Pathname.new(value) if value.is_a?(String)
  @configuration_file = value
end

.description(text = nil) ⇒ void

This method returns an undefined value.

Set a description for the configuration. This is optional. It will be displayed in the web UI if provided. On large projects with many configurations, this can help identify the purpose of each configuration.

Parameters:

  • text (String) (defaults to: nil)

    The description text.



21
22
23
24
# File 'lib/ultra_settings/configuration.rb', line 21

def description(text = nil)
  @description = text.to_s.strip unless text.nil?
  @description
end

.env_var_delimiterString

Get the environment variable delimiter.

Returns:

  • (String)


248
249
250
# File 'lib/ultra_settings/configuration.rb', line 248

def env_var_delimiter
  get_inheritable_class_attribute(:@env_var_delimiter, "_")
end

.env_var_delimiter=(value) ⇒ Object

Set the environment variable delimiter used to construct the environment variable name for a field. By default this is an underscore.

Parameters:

  • value (String)


241
242
243
# File 'lib/ultra_settings/configuration.rb', line 241

def env_var_delimiter=(value)
  set_inheritable_class_attribute(:@env_var_delimiter, value.to_s)
end

.env_var_prefixString

Get the environment variable prefix.

Returns:

  • (String)


133
134
135
136
137
138
# File 'lib/ultra_settings/configuration.rb', line 133

def env_var_prefix
  unless defined?(@env_var_prefix)
    @env_var_prefix = default_env_var_prefix
  end
  @env_var_prefix
end

.env_var_prefix=(value) ⇒ void

This method returns an undefined value.

Override the default environment variable prefix. By default this wil be the underscored name of the class plus an underscore (i.e. MyServiceConfiguration has a prefix of “MY_SERVICE_”).

Parameters:

  • value (String)


126
127
128
# File 'lib/ultra_settings/configuration.rb', line 126

def env_var_prefix=(value)
  @env_var_prefix = value&.to_s
end

.env_var_upcase=(value) ⇒ void

This method returns an undefined value.

Set to true to upcase the environment variable name for a field. This is true by default.

Parameters:

  • value (Boolean)


273
274
275
# File 'lib/ultra_settings/configuration.rb', line 273

def env_var_upcase=(value)
  set_inheritable_class_attribute(:@env_var_upcase, !!value)
end

.env_var_upcase?Boolean

Check if the environment variable name for a field should be upcased.

Returns:

  • (Boolean)


280
281
282
# File 'lib/ultra_settings/configuration.rb', line 280

def env_var_upcase?
  get_inheritable_class_attribute(:@env_var_upcase, true)
end

.environment_variables_disabled=(value) ⇒ void

This method returns an undefined value.

Set to true to disable loading configuration from environment variables.

Parameters:

  • value (Boolean)


196
197
198
# File 'lib/ultra_settings/configuration.rb', line 196

def environment_variables_disabled=(value)
  set_inheritable_class_attribute(:@environment_variables_disabled, !!value)
end

.environment_variables_disabled?Boolean

Check if loading configuration from environment variables is disabled.

Returns:

  • (Boolean)


203
204
205
# File 'lib/ultra_settings/configuration.rb', line 203

def environment_variables_disabled?
  get_inheritable_class_attribute(:@environment_variables_disabled, false)
end

.field(name, type: :string, description: nil, default: nil, default_if: nil, static: nil, secret: nil, runtime_setting: nil, env_var: nil, yaml_key: nil) ⇒ void

This method returns an undefined value.

Define a field on the configuration. This will create a getter method for the field. The field value will be read from the environment, runtime settings, or a YAML file and coerced to the specified type. Empty strings will be converted to nil.

Parameters:

  • name (Symbol, String)

    The name of the field.

  • type (Symbol) (defaults to: :string)

    The type of the field. Valid types are :string, :symbol, :integer, :float, :boolean, :datetime, and :array. The default type is :string. The :array type will return an array of strings.

  • description (String) (defaults to: nil)

    A description of the field.

  • default (Object) (defaults to: nil)

    The default value of the field.

  • default_if (Proc, Symbol) (defaults to: nil)

    A proc that returns true if the default value should be used. By default, the default value will be used if the field evaluates to nil. You can also set this to a symbol with the name of an instance method to call.

  • static (Boolean) (defaults to: nil)

    If true, the field value should never be changed. This is useful for fields that are used at startup to set static values in the application. Static field cannot be read from runtime settings.

  • secret (Boolean, Proc) (defaults to: nil)

    If true, the field value will be obscured in the output of to_hash. If a proc is provided, it will be called to determine if the field is secret.

  • runtime_setting (String, Symbol, Boolean) (defaults to: nil)

    The name of the runtime setting to use for the field. By default this will be the underscored name of the class plus a dot plus the field name (i.e. MyServiceConfiguration#foo becomes “my_service.foo”). If set to false, runtime settings will be ignored for this field. This can be set to true to use the default name.

  • env_var (String, Symbol, Boolean) (defaults to: nil)

    The name of the environment variable to use for the field. By default this will be the underscored name of the class plus an underscore plus the field name all in uppercase (i.e. MyServiceConfiguration#foo becomes “MY_SERVICE_FOO”). If set to false, environment variables will be ignored for this field. This can be set to true to use the default name.

  • yaml_key (String, Symbol, Boolean) (defaults to: nil)

    The name of the YAML key to use for the field. By default this is the name of the field. If set to false, YAML configuration will be ignored for this field. This can be set to true to use the default name.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ultra_settings/configuration.rb', line 56

def field(name, type: :string, description: nil, default: nil, default_if: nil, static: nil, secret: nil, runtime_setting: nil, env_var: nil, yaml_key: nil)
  name = name.to_s
  type = type.to_sym
  static = !!static
  secret = lambda { fields_secret_by_default? } if secret.nil?

  unless name.match?(ALLOWED_NAME_PATTERN)
    raise ArgumentError.new("Invalid name: #{name.inspect}")
  end

  unless ALLOWED_TYPES.include?(type)
    raise ArgumentError.new("Invalid type: #{type.inspect}")
  end

  unless default_if.nil? || default_if.is_a?(Proc) || default_if.is_a?(Symbol)
    raise ArgumentError.new("default_if must be a Proc or Symbol")
  end

  defined_fields[name] = Field.new(
    name: name,
    type: type,
    description: description,
    default: default,
    default_if: default_if,
    env_var: construct_env_var(name, env_var),
    runtime_setting: construct_runtime_setting(name, runtime_setting),
    yaml_key: construct_yaml_key(name, yaml_key),
    static: static,
    secret: secret
  )

  class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
    def #{name}
      __get_value__(#{name.inspect})
    end
  RUBY

  if type == :boolean
    alias_method :"#{name}?", name
  end
end

.fieldsArray<UltraSettings::Field>

List of the defined fields for the configuration.

Returns:



101
102
103
# File 'lib/ultra_settings/configuration.rb', line 101

def fields
  defined_fields.values
end

.fields_secret_by_default=(value) ⇒ void

This method returns an undefined value.

Sets the default value for the secret property of fields. Individual fields can still override this value by explicitly setting the secret property. By default, fields are considered secret.

Parameters:

  • value (Boolean)


341
342
343
# File 'lib/ultra_settings/configuration.rb', line 341

def fields_secret_by_default=(value)
  set_inheritable_class_attribute(:@fields_secret_by_default, !!value)
end

.fields_secret_by_default?Boolean

Check if fields are considered secret by default.

Returns:

  • (Boolean)


348
349
350
# File 'lib/ultra_settings/configuration.rb', line 348

def fields_secret_by_default?
  get_inheritable_class_attribute(:@fields_secret_by_default, true)
end

.include_field?(name) ⇒ Boolean

Check if the field is defined on the configuration.

Parameters:

  • name (Symbol, String)

    The name of the field.

Returns:

  • (Boolean)


109
110
111
112
113
114
115
116
117
118
# File 'lib/ultra_settings/configuration.rb', line 109

def include_field?(name)
  name = name.to_s
  return true if defined_fields.include?(name)

  if superclass <= Configuration
    superclass.include_field?(name)
  else
    false
  end
end

.load_yaml_configHash

Load the YAML file for this configuration and return the values for the current environment.

Returns:

  • (Hash)


365
366
367
368
369
370
# File 'lib/ultra_settings/configuration.rb', line 365

def load_yaml_config
  return nil unless configuration_file
  return nil unless configuration_file.exist? && configuration_file.file?

  YamlConfig.new(configuration_file, yaml_config_env).to_h
end

.override!(values, &block) ⇒ Object

Override field values within a block.

Parameters:

  • values (Hash<Symbol, Object>)

    ] List of fields with the values they should return within the block.

Returns:

  • (Object)

    The value returned by the block.



357
358
359
# File 'lib/ultra_settings/configuration.rb', line 357

def override!(values, &block)
  instance.override!(values, &block)
end

.runtime_setting_delimiterString

Get the runtime setting delimiter.

Returns:

  • (String)


264
265
266
# File 'lib/ultra_settings/configuration.rb', line 264

def runtime_setting_delimiter
  get_inheritable_class_attribute(:@runtime_setting_delimiter, ".")
end

.runtime_setting_delimiter=(value) ⇒ void

This method returns an undefined value.

Set the runtime setting delimiter used to construct the runtime setting name for a field. By default this is a dot.

Parameters:

  • value (String)


257
258
259
# File 'lib/ultra_settings/configuration.rb', line 257

def runtime_setting_delimiter=(value)
  set_inheritable_class_attribute(:@runtime_setting_delimiter, value.to_s)
end

.runtime_setting_prefixString

Get the runtime setting prefix.

Returns:

  • (String)


153
154
155
156
157
158
# File 'lib/ultra_settings/configuration.rb', line 153

def runtime_setting_prefix
  unless defined?(@runtime_setting_prefix)
    @runtime_setting_prefix = default_runtime_setting_prefix
  end
  @runtime_setting_prefix
end

.runtime_setting_prefix=(value) ⇒ void

This method returns an undefined value.

Override the default runtime setting prefix. By default this wil be the underscored name of the class plus a dot (i.e. MyServiceConfiguration has a prefix of “my_service.”).

Parameters:

  • value (String)


146
147
148
# File 'lib/ultra_settings/configuration.rb', line 146

def runtime_setting_prefix=(value)
  @runtime_setting_prefix = value&.to_s
end

.runtime_setting_upcase=(value) ⇒ void

This method returns an undefined value.

Set to true to upcase the runtime setting name for a field. This is false by default.

Parameters:

  • value (Boolean)


289
290
291
# File 'lib/ultra_settings/configuration.rb', line 289

def runtime_setting_upcase=(value)
  set_inheritable_class_attribute(:@runtime_setting_upcase, !!value)
end

.runtime_setting_upcase?Boolean

Check if the runtime setting name for a field should be upcased.

Returns:

  • (Boolean)


296
297
298
# File 'lib/ultra_settings/configuration.rb', line 296

def runtime_setting_upcase?
  get_inheritable_class_attribute(:@runtime_setting_upcase, false)
end

.runtime_settings_disabled=(value) ⇒ void

This method returns an undefined value.

Set to true to disable loading configuration from runtime settings.

Parameters:

  • value (Boolean)


211
212
213
# File 'lib/ultra_settings/configuration.rb', line 211

def runtime_settings_disabled=(value)
  set_inheritable_class_attribute(:@runtime_settings_disabled, !!value)
end

.runtime_settings_disabled?Boolean

Check if loading configuration from runtime settings is disabled.

Returns:

  • (Boolean)


218
219
220
# File 'lib/ultra_settings/configuration.rb', line 218

def runtime_settings_disabled?
  get_inheritable_class_attribute(:@runtime_settings_disabled, false)
end

.yaml_config_disabled=(value) ⇒ void

This method returns an undefined value.

Set to true to disable loading configuration from YAML files.

Parameters:

  • value (Boolean)


226
227
228
# File 'lib/ultra_settings/configuration.rb', line 226

def yaml_config_disabled=(value)
  set_inheritable_class_attribute(:@yaml_config_disabled, !!value)
end

.yaml_config_disabled?Boolean

Check if loading configuration from YAML files is disabled.

Returns:

  • (Boolean)


233
234
235
# File 'lib/ultra_settings/configuration.rb', line 233

def yaml_config_disabled?
  get_inheritable_class_attribute(:@yaml_config_disabled, false) || configuration_file.nil?
end

.yaml_config_envString

Get the environment namespace used in YAML file name.

Returns:

  • (String)


331
332
333
# File 'lib/ultra_settings/configuration.rb', line 331

def yaml_config_env
  get_inheritable_class_attribute(:@yaml_config_env, "development")
end

.yaml_config_env=(value) ⇒ void

This method returns an undefined value.

Set the environment namespace used in YAML file name. By default this is “development”. Settings from the specific environment hash in the YAML file will be merged with base settings specified in the “shared” hash.

Parameters:

  • value (String)


324
325
326
# File 'lib/ultra_settings/configuration.rb', line 324

def yaml_config_env=(value)
  set_inheritable_class_attribute(:@yaml_config_env, value)
end

.yaml_config_pathPathname?

Get the directory where YAML files will be loaded from.

Returns:

  • (Pathname, nil)


314
315
316
# File 'lib/ultra_settings/configuration.rb', line 314

def yaml_config_path
  get_inheritable_class_attribute(:@yaml_config_path, nil)
end

.yaml_config_path=(value) ⇒ void

This method returns an undefined value.

Set the directory where YAML files will be loaded from. By default this is the current working directory.

Parameters:

  • value (String, Pathname)


305
306
307
308
309
# File 'lib/ultra_settings/configuration.rb', line 305

def yaml_config_path=(value)
  value = Pathname.new(value) if value.is_a?(String)
  value = value.expand_path if value&.relative?
  set_inheritable_class_attribute(:@yaml_config_path, value)
end

Instance Method Details

#[](name) ⇒ Object



470
471
472
# File 'lib/ultra_settings/configuration.rb', line 470

def [](name)
  send(name.to_s) if include?(name)
end

#__available_sources__(name) ⇒ Array<Symbol>

Returns an array of the available data sources for the field.

Parameters:

  • name (String, Symbol)

    the name of the field.

Returns:

  • (Array<Symbol>)

    The available sources (:env, :settings, :yaml, :default).

Raises:

  • (ArgumentError)


537
538
539
540
541
542
543
544
545
546
547
# File 'lib/ultra_settings/configuration.rb', line 537

def __available_sources__(name)
  field = self.class.send(:defined_fields)[name.to_s]
  raise ArgumentError.new("Unknown field: #{name.inspect}") unless field

  sources = []
  sources << :env if field.env_var
  sources << :settings if field.runtime_setting && UltraSettings.__runtime_settings__
  sources << :yaml if field.yaml_key && self.class.configuration_file
  sources << :default unless field.default.nil?
  sources
end

#__source__(name) ⇒ Symbol?

Get the current source for the field.

Parameters:

  • name (String, Symbol)

    the name of the field.

Returns:

  • (Symbol, nil)

    The source of the value (:env, :settings, :yaml, or :default).

Raises:

  • (ArgumentError)


502
503
504
505
506
507
508
# File 'lib/ultra_settings/configuration.rb', line 502

def __source__(name)
  field = self.class.send(:defined_fields)[name.to_s]
  raise ArgumentError.new("Unknown field: #{name.inspect}") unless field

  source = field.source(env: ENV, settings: UltraSettings.__runtime_settings__, yaml_config: __yaml_config__)
  source || :default
end

#__to_hash__Hash

Output the current state of the configuration as a hash. If the field is marked as a secret, then the value will be a secure hash of the value instead of the value itself.

The intent of this method is to provide a serializable value that captures the current state of the configuration without exposing any secrets. You could, for instance, use the output to compare the configuration of you application between two different environments.

Returns:

  • (Hash)


557
558
559
560
561
562
563
564
565
566
567
# File 'lib/ultra_settings/configuration.rb', line 557

def __to_hash__
  payload = {}
  self.class.fields.each do |field|
    value = self[field.name]
    if field.secret? && !value.nil?
      value = "securehash:#{Digest::MD5.hexdigest(Digest::SHA256.hexdigest(value.to_s))}"
    end
    payload[field.name] = value
  end
  payload
end

#__value_from_source__(name, source) ⇒ Object

Get the value of the field from the specified source.

Parameters:

  • name (String, Symbol)

    the name of the field.

  • source (Symbol)

    the source of the value (:env, :settings, :yaml, or :default).

Returns:

  • (Object)

    The value of the field.

Raises:

  • (ArgumentError)


515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# File 'lib/ultra_settings/configuration.rb', line 515

def __value_from_source__(name, source)
  field = self.class.send(:defined_fields)[name.to_s]
  raise ArgumentError.new("Unknown field: #{name.inspect}") unless field

  case source
  when :env
    field.value(env: ENV)
  when :settings
    field.value(settings: UltraSettings.__runtime_settings__)
  when :yaml
    field.value(yaml_config: __yaml_config__)
  when :default
    field.default
  else
    raise ArgumentError.new("Unknown source: #{source.inspect}")
  end
end

#include?(name) ⇒ Boolean

Returns:

  • (Boolean)


474
475
476
# File 'lib/ultra_settings/configuration.rb', line 474

def include?(name)
  self.class.include_field?(name.to_s)
end

#override!(values, &block) ⇒ Object



478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'lib/ultra_settings/configuration.rb', line 478

def override!(values, &block)
  save_val = @ultra_settings_override_values[Thread.current.object_id]

  temp_values = (save_val || {}).dup
  values.each do |key, value|
    temp_values[key.to_s] = value
  end

  begin
    @ultra_settings_mutex.synchronize do
      @ultra_settings_override_values[Thread.current.object_id] = temp_values
    end
    yield
  ensure
    @ultra_settings_mutex.synchronize do
      @ultra_settings_override_values[Thread.current.object_id] = save_val
    end
  end
end