Class: TTY::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/tty/config.rb,
lib/tty/config/version.rb

Constant Summary collapse

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.2.0".freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of Config

Yields:

  • (_self)

Yield Parameters:

  • _self (TTY::Config)

    the object that the method was called on



53
54
55
56
57
58
59
60
61
62
63
# File 'lib/tty/config.rb', line 53

def initialize(settings = {})
  @location_paths = []
  @settings = settings
  @validators = {}
  @filename = 'config'
  @extname = '.yml'
  @extensions = ['.yaml', '.yml', '.json', '.toml']
  @key_delim = '.'

  yield(self) if block_given?
end

Instance Attribute Details

#extnameObject

The name of the configuration file extension



47
48
49
# File 'lib/tty/config.rb', line 47

def extname
  @extname
end

#filenameObject

The name of the configuration file without extension



43
44
45
# File 'lib/tty/config.rb', line 43

def filename
  @filename
end

#key_delimObject (readonly)

The key delimiter used for specifying deeply nested keys



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

def key_delim
  @key_delim
end

#location_pathsObject (readonly)

A collection of config paths



35
36
37
# File 'lib/tty/config.rb', line 35

def location_paths
  @location_paths
end

#validatorsObject (readonly)

The validations for this configuration



51
52
53
# File 'lib/tty/config.rb', line 51

def validators
  @validators
end

Class Method Details

.coerce(hash, &block) ⇒ Object



18
19
20
# File 'lib/tty/config.rb', line 18

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

.normalize_hash(hash, method = :to_sym) ⇒ 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



25
26
27
28
29
30
31
# File 'lib/tty/config.rb', line 25

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

Instance Method Details

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

Append values to an already existing nested key

Parameters:

  • values (Array[String|Symbol])

    the values to append



158
159
160
161
# File 'lib/tty/config.rb', line 158

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

#append_path(path) ⇒ Object

Add path to locations to search in



80
81
82
# File 'lib/tty/config.rb', line 80

def append_path(path)
  @location_paths << path
end

#assert_valid(key, value) ⇒ 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.

Check if key passes all registered validations



198
199
200
201
202
# File 'lib/tty/config.rb', line 198

def assert_valid(key, value)
  validators[key].each do |validator|
    validator.call(key, value)
  end
end

#delay_validation(key, callback) ⇒ 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.

Delay key validation



207
208
209
210
211
212
213
# File 'lib/tty/config.rb', line 207

def delay_validation(key, callback)
  -> do
    val = callback.()
    assert_valid(key, val)
    val
  end
end

#delete(*keys) ⇒ Object

Delete a value from a nested key

Parameters:

  • keys (Array[String|Symbol])

    the keys for a value deletion



180
181
182
183
# File 'lib/tty/config.rb', line 180

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

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

Fetch value under a composite key

Parameters:

  • keys (Array[String|Symbol])

    the keys to get value at

  • default (Object)


133
134
135
136
137
138
139
140
141
# File 'lib/tty/config.rb', line 133

def fetch(*keys, default: nil, &block)
  keys = convert_to_keys(keys)
  value = deep_fetch(@settings, *keys)
  value = block || default if value.nil?
  while callable_without_params?(value)
    value = value.call
  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.



216
217
218
219
220
221
222
# File 'lib/tty/config.rb', line 216

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) ⇒ Object

Merge in other configuration settings

Parameters:

  • other_settings (Hash[Object])


148
149
150
# File 'lib/tty/config.rb', line 148

def merge(other_settings)
  @settings = deep_merge(@settings, other_settings)
end

#persisted?Boolean

Check if configuration file exists

Returns:

  • (Boolean)


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

def persisted?
  !find_file.nil?
end

#prepend_path(path) ⇒ Object

Insert location path at the begining



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

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

#read(file = find_file) ⇒ 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

Raises:



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

def read(file = find_file)
  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

  merge(unmarshal(file))
end

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

Remove a set of values from a nested key

Parameters:

  • keys (Array[String|Symbol])

    the keys for a value removal



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

def remove(*values, from: nil)
  keys = Array(from)
  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



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 95

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_if_empty(*keys, value: nil, &block) ⇒ Object

Set a value for a composite key if not present already

Parameters:

  • keys (Array[String|Symbol])

    the keys to set value for



121
122
123
124
# File 'lib/tty/config.rb', line 121

def set_if_empty(*keys, value: nil, &block)
  return unless deep_find(@settings, keys.last.to_s).nil?
  block ? set(*keys, &block) : set(*keys, value: value)
end

#to_hashObject Also known as: to_h

Current configuration



282
283
284
# File 'lib/tty/config.rb', line 282

def to_hash
  @settings.dup
end

#validate(*keys, &validator) ⇒ Object

Register validation for a nested key



188
189
190
191
192
193
# File 'lib/tty/config.rb', line 188

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

#write(file = find_file, force: false) ⇒ Object

Write current configuration to a file.

Parameters:

  • file (String) (defaults to: find_file)

    the path to a file



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/tty/config.rb', line 261

def write(file = find_file, force: false)
  if file && ::File.exist?(file)
    if !force
      raise WriteError, "File `#{file}` already exists. " \
                        "Use :force option to overwrite."
    elsif !::File.writable?(file)
      raise WriteError, "Cannot write to #{file}."
    end
  end

  if file.nil?
    dir = @location_paths.empty? ? Dir.pwd : @location_paths.first
    file = ::File.join(dir, "#{filename}#{@extname}")
  end

  marshal(file, @settings)
end