Class: TTY::Config

Inherits:
Object
  • Object
show all
Includes:
Marshallers
Defined in:
lib/tty/config.rb,
lib/tty/config/version.rb,
lib/tty/config/generator.rb,
lib/tty/config/marshaller.rb,
lib/tty/config/marshallers.rb,
lib/tty/config/dependency_loader.rb,
lib/tty/config/marshaller_registry.rb,
lib/tty/config/marshallers/hcl_marshaller.rb,
lib/tty/config/marshallers/ini_marshaller.rb,
lib/tty/config/marshallers/xml_marshaller.rb,
lib/tty/config/marshallers/json_marshaller.rb,
lib/tty/config/marshallers/toml_marshaller.rb,
lib/tty/config/marshallers/yaml_marshaller.rb,
lib/tty/config/marshallers/java_props_marshaller.rb

Overview

Responsible for managing application configuration

Defined Under Namespace

Modules: DependencyLoader, Generator, Marshaller, Marshallers Classes: MarshallerRegistry

Constant Summary collapse

DependencyLoadError =

Error raised when failed to load a dependency

Class.new(StandardError)
ReadError =

Error raised when key fails validation

Class.new(StandardError)
WriteError =

Error raised when issues writing configuration to a file

Class.new(StandardError)
UnsupportedExtError =

Erorrr raised when setting unknown file extension

Class.new(StandardError)
ValidationError =

Error raised when validation assertion fails

Class.new(StandardError)
VERSION =
"0.6.0"

Constants included from Marshallers

Marshallers::NO_EXT

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Marshallers

#extensions, #marshaller_registry, #marshallers, #register_marshaller, #registered_marshaller?, #unregister_marshaller

Constructor Details

#initialize(settings = {}) {|_self| ... } ⇒ Config

Create a configuration instance

Yields:

  • (_self)

Yield Parameters:

  • _self (TTY::Config)

    the object that the method was called on



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/tty/config.rb', line 91

def initialize(settings = {})
  @settings = settings
  @location_paths = []
  @validators = {}
  @filename = "config"
  @extname = ".yml"
  @key_delim = "."
  @envs = {}
  @env_prefix = ""
  @env_separator = "_"
  @autoload_env = false
  @aliases = {}

  register_marshaller :yaml, Marshallers::YAMLMarshaller
  register_marshaller :json, Marshallers::JSONMarshaller
  register_marshaller :toml, Marshallers::TOMLMarshaller
  register_marshaller :ini, Marshallers::INIMarshaller
  register_marshaller :xml, Marshallers::XMLMarshaller
  register_marshaller :hcl, Marshallers::HCLMarshaller
  register_marshaller :jprops, Marshallers::JavaPropsMarshaller

  yield(self) if block_given?
end

Instance Attribute Details

#env_prefixObject

The prefix used for searching ENV variables



82
83
84
# File 'lib/tty/config.rb', line 82

def env_prefix
  @env_prefix
end

#env_separatorObject

The string used to separate parts in ENV variable name



86
87
88
# File 'lib/tty/config.rb', line 86

def env_separator
  @env_separator
end

#extnameObject

The name of the configuration file extension



74
75
76
# File 'lib/tty/config.rb', line 74

def extname
  @extname
end

#filenameObject

The name of the configuration file without extension



70
71
72
# File 'lib/tty/config.rb', line 70

def filename
  @filename
end

#key_delimObject (readonly)

The key delimiter used for specifying deeply nested keys



66
67
68
# File 'lib/tty/config.rb', line 66

def key_delim
  @key_delim
end

#location_pathsObject (readonly)

A collection of config paths



62
63
64
# File 'lib/tty/config.rb', line 62

def location_paths
  @location_paths
end

#validatorsObject (readonly)

The validations for this configuration



78
79
80
# File 'lib/tty/config.rb', line 78

def validators
  @validators
end

Class Method Details

.coerce(hash, &block) ⇒ TTY::Config

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Coerce a hash object into Config instance

Returns:



38
39
40
# File 'lib/tty/config.rb', line 38

def self.coerce(hash, &block)
  new(normalize_hash(hash), &block)
end

.normalize_hash(hash, method = :to_sym) ⇒ Hash{Symbol => Object}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Convert string keys via method

Parameters:

  • hash (Hash)

    the hash to normalize keys for

  • method (Symbol) (defaults to: :to_sym)

    the method to use for converting keys

Returns:

  • (Hash{Symbol => Object})

    the converted hash



53
54
55
56
57
58
# File 'lib/tty/config.rb', line 53

def self.normalize_hash(hash, method = :to_sym)
  hash.each_with_object({}) do |(key, val), acc|
    value = val.is_a?(::Hash) ? normalize_hash(val, method) : val
    acc[key.public_send(method)] = value
  end
end

Instance Method Details

#alias_setting(*keys, to: nil) ⇒ Object

Define an alias to a nested key

Examples:

alias_setting(:foo, to: :bar)

Parameters:

  • keys (Array<String>)

    the alias key



392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/tty/config.rb', line 392

def alias_setting(*keys, to: nil)
  flat_setting = flatten_keys(keys)
  alias_keys = Array(to)
  alias_key = flatten_keys(alias_keys)

  if alias_key == flat_setting
    raise ArgumentError, "Alias matches setting key"
  end

  if fetch(alias_key)
    raise ArgumentError, "Setting already exists with an alias " \
                         "'#{alias_keys.map(&:inspect).join(', ')}'"
  end

  @aliases[alias_key] = flat_setting
end

#append(*values, to: nil) ⇒ Array<Object>

Append values to an already existing nested key

Examples:

append(1, 2, to: %i[foo bar])

Parameters:

  • values (Array<Object>)

    the values to append

  • Array<String, (Array<String, Symbol] to the nested key to append to)

    Symbol] to the nested key to append to

Returns:

  • (Array<Object>)

    the values for a nested key



335
336
337
338
# File 'lib/tty/config.rb', line 335

def append(*values, to: nil)
  keys = Array(to)
  set(*keys, value: Array(fetch(*keys)) + values)
end

#append_path(path) ⇒ Array<String>

Add path to locations to search in

Examples:

append_path(Dir.pwd)

Parameters:

  • path (String)

    the path to append

Returns:

  • (Array<String>)


139
140
141
# File 'lib/tty/config.rb', line 139

def append_path(path)
  @location_paths << path
end

#autoload_envObject

Auto load env variables



170
171
172
# File 'lib/tty/config.rb', line 170

def autoload_env
  @autoload_env = true
end

#autoload_env?Boolean

Check if env variables are auto loaded

Returns:

  • (Boolean)


163
164
165
# File 'lib/tty/config.rb', line 163

def autoload_env?
  @autoload_env == true
end

#delete(*keys) {|key| ... } ⇒ Object

Delete a value from a nested key

Examples:

delete(:foo, :bar, :baz)
delete(:unknown) { |key| "#{key} isn't set" }

Parameters:

  • keys (Array<String, Symbol>)

    the keys for a value deletion

Yields:

  • (key)

    Invoke the block with a missing key

Returns:

  • (Object)

    the deleted value(s)



378
379
380
381
# File 'lib/tty/config.rb', line 378

def delete(*keys, &default)
  keys = convert_to_keys(keys)
  deep_delete(*keys, @settings, &default)
end

#exist?Boolean Also known as: persisted?

Check if configuration file exists

Returns:

  • (Boolean)


441
442
443
# File 'lib/tty/config.rb', line 441

def exist?
  !find_file.nil?
end

#fetch(*keys, default: nil, &block) ⇒ Object

Fetch value under a composite key

Examples:

fetch(:foo, :bar, :baz)
fetch("foo.bar.baz")

Parameters:

  • keys (Array<String, Symbol>, String)

    the keys to get value at

  • default (Object) (defaults to: nil)

    the default value

Returns:

  • (Object)


285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/tty/config.rb', line 285

def fetch(*keys, default: nil, &block)
  # check alias
  real_key = @aliases[flatten_keys(keys)]
  keys = real_key.split(key_delim) if real_key

  keys = convert_to_keys(keys)
  env_key = autoload_env? ? to_env_key(keys[0]) : @envs[flatten_keys(keys)]
  # first try settings
  value = deep_fetch(@settings, *keys)
  # then try ENV var
  if value.nil? && env_key
    value = ENV[env_key]
  end
  # then try default
  value = block || default if value.nil?

  while callable_without_params?(value)
    value = value.()
  end
  value
end

#find_fileObject Also known as: source_file

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Find configuration file matching filename and extension



427
428
429
430
431
432
433
# File 'lib/tty/config.rb', line 427

def find_file
  @location_paths.each do |location_path|
    path = search_in_path(location_path)
    return path if path
  end
  nil
end

#merge(other_settings) ⇒ Hash?

Merge in other configuration settings

Parameters:

  • other_settings (Hash{Symbol => Object])

Returns:

  • (Hash, nil)

    the combined settings or nil



315
316
317
318
319
# File 'lib/tty/config.rb', line 315

def merge(other_settings)
  return unless other_settings.respond_to?(:to_hash)

  @settings = deep_merge(@settings, other_settings)
end

#prepend_path(path) ⇒ Array<String>

Insert location path at the begining

Examples:

prepend_path(Dir.pwd)

Parameters:

  • path (String)

    the path to prepend

Returns:

  • (Array<String>)


154
155
156
# File 'lib/tty/config.rb', line 154

def prepend_path(path)
  @location_paths.unshift(path)
end

#read(file = find_file, format: :auto) ⇒ Object

Find and read a configuration file.

If the file doesn’t exist or if there is an error loading it the TTY::Config::ReadError will be raised.

Parameters:

  • file (String) (defaults to: find_file)

    the path to the configuration file to be read

  • format (String) (defaults to: :auto)

    the format to read configuration in

Raises:



460
461
462
463
464
465
466
467
468
469
470
471
472
473
# File 'lib/tty/config.rb', line 460

def read(file = find_file, format: :auto)
  if file.nil?
    raise ReadError, "No file found to read configuration from!"
  elsif !::File.exist?(file)
    raise ReadError, "Configuration file `#{file}` does not exist!"
  end

  (file)

  ext = (format == :auto ? extname : ".#{format}")
  content = ::File.read(file)

  merge(unmarshal(content, ext: ext))
end

#remove(*values, from: nil) ⇒ Object

Remove a set of values from a nested key

Examples:

remove(1, 2, from: :foo)
remove(1, 2, from: %i[foo bar])

Parameters:

  • values (Array<Object>)

    the values to remove from a nested key

  • from (Array<String, Symbol>, String) (defaults to: nil)

    the nested key to remove values from

Raises:

  • (ArgumentError)


354
355
356
357
358
359
# File 'lib/tty/config.rb', line 354

def remove(*values, from: nil)
  keys = Array(from)
  raise ArgumentError, "Need to set key to remove from" if keys.empty?

  set(*keys, value: Array(fetch(*keys)) - values)
end

#set(*keys, value: nil, &block) ⇒ Object

Set a value for a composite key and overrides any existing keys Keys are case-insensitive

Examples:

set(:foo, :bar, :baz, value: 2)
set(:foo, :bar, :baz) { 2 }
set("foo.bar.baz", value: 2)

Parameters:

  • keys (Array<String, Symbol>, String)

    the nested key to set value for

  • value (Object) (defaults to: nil)

    the value to set

Returns:

  • (Object)

    the set value



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/tty/config.rb', line 195

def set(*keys, value: nil, &block)
  assert_either_value_or_block(value, block)

  keys = convert_to_keys(keys)
  key = flatten_keys(keys)
  value_to_eval = block || value

  if validators.key?(key)
    if callable_without_params?(value_to_eval)
      value_to_eval = delay_validation(key, value_to_eval)
    else
      assert_valid(key, value)
    end
  end

  deepest_setting = deep_set(@settings, *keys[0...-1])
  deepest_setting[keys.last] = value_to_eval
  deepest_setting[keys.last]
end

#set_file_metadata(file) ⇒ Object

Set file name and extension

Examples:

("config.yml")

Parameters:

  • file (File)

    the file to set metadata for



517
518
519
520
# File 'lib/tty/config.rb', line 517

def (file)
  self.extname  = ::File.extname(file)
  self.filename = ::File.basename(file, extname)
end

#set_from_env(*keys, &block) ⇒ Object

Bind a key to ENV variable

Examples:

set_from_env(:host)
set_from_env(:foo, :bar) { 'HOST' }

Parameters:

  • keys (Array<String>)

    the keys to bind to ENV variables



246
247
248
249
250
251
# File 'lib/tty/config.rb', line 246

def set_from_env(*keys, &block)
  key = flatten_keys(keys)
  env_key = block.nil? ? key : block.()
  env_key = to_env_key(env_key)
  @envs[key.to_s.downcase] = env_key
end

#set_if_empty(*keys, value: nil, &block) ⇒ Object?

Set a value for a composite key if not present already

Examples:

set_if_empty(:foo, :bar, :baz, value: 2)

Parameters:

  • keys (Array<String, Symbol>)

    the keys to set value for

  • value (Object) (defaults to: nil)

    the value to set

Returns:

  • (Object, nil)

    the set value or nil



229
230
231
232
233
234
# File 'lib/tty/config.rb', line 229

def set_if_empty(*keys, value: nil, &block)
  keys = convert_to_keys(keys)
  return unless deep_fetch(@settings, *keys).nil?

  block ? set(*keys, &block) : set(*keys, value: value)
end

#to_env_key(key) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Convert config key to standard ENV var name

Parameters:

  • key (String)

Returns:

  • (String)


260
261
262
263
264
265
266
267
# File 'lib/tty/config.rb', line 260

def to_env_key(key)
  env_key = key.to_s.gsub(key_delim, env_separator).upcase
  if @env_prefix == ""
    env_key
  else
    "#{@env_prefix.to_s.upcase}#{env_separator}#{env_key}"
  end
end

#to_hashObject Also known as: to_h

Current configuration



525
526
527
# File 'lib/tty/config.rb', line 525

def to_hash
  @settings.dup
end

#validate(*keys, &validator) ⇒ Object

Register a validation rule for a nested key

Parameters:

  • keys (Array<String>)

    a deep nested keys

  • validator (Proc)

    the logic to use to validate given nested key



417
418
419
420
421
422
# File 'lib/tty/config.rb', line 417

def validate(*keys, &validator)
  key = flatten_keys(keys)
  values = validators[key] || []
  values << validator
  validators[key] = values
end

#write(file = find_file, create: false, force: false, format: :auto, path: nil) ⇒ Object

Write current configuration to a file.

Examples:

write(force: true, create: true)

Parameters:

  • file (String) (defaults to: find_file)

    the file to write to

  • create (Boolean) (defaults to: false)

    whether or not to create missing path directories, false by default

  • force (Boolean) (defaults to: false)

    whether or not to overwrite existing configuration file, false by default

  • format (String) (defaults to: :auto)

    the format name for the configuration file, :auto by defualt

  • path (String) (defaults to: nil)

    the custom path to use to write a file to

Raises:



494
495
496
497
498
499
500
501
502
503
504
505
506
# File 'lib/tty/config.rb', line 494

def write(file = find_file, create: false, force: false, format: :auto,
          path: nil)
  file = fullpath(file, path)
  check_can_write(file, force)

  (file)
  ext = (format == :auto ? extname : ".#{format}")
  content = marshal(@settings, ext: ext)
  filepath = Pathname.new(file)

  create_missing_dirs(filepath, create)
  ::File.write(filepath, content)
end