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
more...

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

[View source]

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:

[View source]

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

[View source]

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

[View source]

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

[View source]

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>)
[View source]

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

def append_path(path)
  @location_paths << path
end

#autoload_envObject

Auto load env variables

[View source]

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)
[View source]

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)

[View source]

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)
[View source]

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)
[View source]

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

[View source]

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

[View source]

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>)
[View source]

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:

[View source]

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)
[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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)
[View source]

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

[View source]

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

[View source]

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:

[View source]

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