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.



483
484
485
486
487
488
# File 'lib/ultra_settings/configuration.rb', line 483

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)


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

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)


169
170
171
172
173
# File 'lib/ultra_settings/configuration.rb', line 169

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

.descendant_configurationsArray<Class>

Get all descendant configuration classes (subclasses and their subclasses, recursively).

Returns:

  • (Array<Class>)

    All classes that inherit from this class.



377
378
379
380
# File 'lib/ultra_settings/configuration.rb', line 377

def descendant_configurations
  @descendants ||= []
  @descendants.flat_map { |subclass| [subclass] + subclass.descendant_configurations }
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.



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

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

.env_var_delimiterString

Get the environment variable delimiter.

Returns:

  • (String)


250
251
252
# File 'lib/ultra_settings/configuration.rb', line 250

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)


243
244
245
# File 'lib/ultra_settings/configuration.rb', line 243

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)


135
136
137
138
139
140
# File 'lib/ultra_settings/configuration.rb', line 135

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)


128
129
130
# File 'lib/ultra_settings/configuration.rb', line 128

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)


275
276
277
# File 'lib/ultra_settings/configuration.rb', line 275

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)


282
283
284
# File 'lib/ultra_settings/configuration.rb', line 282

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)


198
199
200
# File 'lib/ultra_settings/configuration.rb', line 198

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)


205
206
207
# File 'lib/ultra_settings/configuration.rb', line 205

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.



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
97
98
# File 'lib/ultra_settings/configuration.rb', line 57

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
  )

  caller_location = caller_locations(1, 1).first
  class_eval "    def \#{name}\n      __get_value__(\#{name.inspect})\n    end\n  RUBY\n\n  if type == :boolean\n    alias_method :\"\#{name}?\", name\n  end\nend\n", caller_location.path, caller_location.lineno # rubocop:disable Security/Eval, Style/EvalWithLocation

.fieldsArray<UltraSettings::Field>

List of the defined fields for the configuration.

Returns:



103
104
105
# File 'lib/ultra_settings/configuration.rb', line 103

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)


343
344
345
# File 'lib/ultra_settings/configuration.rb', line 343

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)


350
351
352
# File 'lib/ultra_settings/configuration.rb', line 350

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)


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

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)


367
368
369
370
371
372
# File 'lib/ultra_settings/configuration.rb', line 367

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.



359
360
361
# File 'lib/ultra_settings/configuration.rb', line 359

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

.runtime_setting_delimiterString

Get the runtime setting delimiter.

Returns:

  • (String)


266
267
268
# File 'lib/ultra_settings/configuration.rb', line 266

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)


259
260
261
# File 'lib/ultra_settings/configuration.rb', line 259

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)


155
156
157
158
159
160
# File 'lib/ultra_settings/configuration.rb', line 155

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)


148
149
150
# File 'lib/ultra_settings/configuration.rb', line 148

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)


291
292
293
# File 'lib/ultra_settings/configuration.rb', line 291

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)


298
299
300
# File 'lib/ultra_settings/configuration.rb', line 298

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)


213
214
215
# File 'lib/ultra_settings/configuration.rb', line 213

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)


220
221
222
# File 'lib/ultra_settings/configuration.rb', line 220

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)


228
229
230
# File 'lib/ultra_settings/configuration.rb', line 228

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)


235
236
237
# File 'lib/ultra_settings/configuration.rb', line 235

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)


333
334
335
# File 'lib/ultra_settings/configuration.rb', line 333

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)


326
327
328
# File 'lib/ultra_settings/configuration.rb', line 326

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)


316
317
318
# File 'lib/ultra_settings/configuration.rb', line 316

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)


307
308
309
310
311
# File 'lib/ultra_settings/configuration.rb', line 307

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



490
491
492
# File 'lib/ultra_settings/configuration.rb', line 490

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)


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

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)


522
523
524
525
526
527
528
# File 'lib/ultra_settings/configuration.rb', line 522

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)


577
578
579
580
581
582
583
584
585
586
587
# File 'lib/ultra_settings/configuration.rb', line 577

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)


535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
# File 'lib/ultra_settings/configuration.rb', line 535

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)


494
495
496
# File 'lib/ultra_settings/configuration.rb', line 494

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

#override!(values, &block) ⇒ Object



498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
# File 'lib/ultra_settings/configuration.rb', line 498

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