Class: HatiConfig::Setting

Inherits:
Object
  • Object
show all
Extended by:
Cache, Encryption, Environment, Schema
Includes:
Environment
Defined in:
lib/hati_config/setting.rb

Overview

Setting class provides a configuration tree structure for managing settings.

This class allows for dynamic configuration management, enabling the loading of settings from hashes, YAML, or JSON formats.

Examples:

Basic usage

settings = Setting.new do
  config(:key1, value: "example")
  config(:key2, type: :int)
end

Instance Method Summary collapse

Methods included from Environment

current_environment, current_environment, current_environment=, development?, environment, environment?, production?, staging?, test?, with_environment

Methods included from Schema

migration, schema_definition, schema_version

Methods included from Cache

cache, cache_config

Methods included from Encryption

encryption, encryption_config

Constructor Details

#initialize {|self| ... } ⇒ Setting

Initializes a new Setting instance.

Yields:

  • (self) —

    Configures the instance upon creation if a block is given.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/hati_config/setting.rb', line 51

def initialize(&block)
  @config_tree = {}
  @schema = {}
  @immutable_schema = {}
  @encrypted_tree = {}

  if self.class.encryption_config.key_provider
    self.class.encryption do
      key_provider :env
    end
  end

  instance_eval(&block) if block_given?
end

Instance Method Details

#[](key) ⇒ Object

Provides hash-like access to configuration values

Parameters:

  • key (Symbol, String) —

    The key to access

Returns:

  • (Object) —

    The value associated with the key

Raises:

  • (NoMethodError)


237
238
239
240
241
242
# File 'lib/hati_config/setting.rb', line 237

def [](key)
  key = key.to_sym if key.is_a?(String)
  return get_value(key) if config_tree.key?(key)

  raise NoMethodError, "undefined method `[]' with key #{key} for #{self.class}"
end

#[]=(key, value) ⇒ Object

Sets a configuration value using hash-like syntax

Parameters:

  • key (Symbol, String) —

    The key to set

  • value (Object) —

    The value to set



248
249
250
251
# File 'lib/hati_config/setting.rb', line 248

def []=(key, value)
  key = key.to_sym if key.is_a?(String)
  config(key => value)
end

#config(setting = nil, type: nil, lock: nil, encrypted: false, **opt) ⇒ self

Configures a setting with a given name and type.

Examples:

Configuring a setting

settings.config(max_connections: 10, type: :int)

Parameters:

  • setting (Symbol, Hash, nil) (defaults to: nil) —

    The name of the setting or a hash of settings.

  • type (Symbol, nil) (defaults to: nil) —

    The expected type of the setting.

  • opt (Hash) —

    Additional options for configuration.

Returns:

  • (self) —

    The current instance for method chaining.

Raises:



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/hati_config/setting.rb', line 128

def config(setting = nil, type: nil, lock: nil, encrypted: false, **opt)
  return self if !setting && opt.empty?

  # If setting is a symbol/string and we have keyword options, merge them
  if (setting.is_a?(Symbol) || setting.is_a?(String)) && !opt.empty?
    raw_stngs = opt.merge(setting => opt[:value])
    raw_stngs.delete(:value)
  else
    raw_stngs = setting || opt
  end
  stngs = extract_setting_info(raw_stngs)

  stng_lock = determine_lock(stngs, lock)
  stng_type = determine_type(stngs, type)
  stng_encrypted = determine_encrypted(stngs, encrypted)

  if stng_encrypted
    value = stngs[:value]
    if value.nil? && config_tree[stngs[:name]]
      value = config_tree[stngs[:name]]
      value = self.class.encryption_config.decrypt(value) if @encrypted_tree[stngs[:name]]
    end

    if value.is_a?(HatiConfig::Setting)
      # Handle nested settings
      value.instance_eval(&block) if block_given?
    elsif !value.nil?
      # If we're setting a new value or updating an existing one
      raise SettingTypeError.new('string (encrypted values must be strings)', value) unless value.is_a?(String)

      stngs[:value] = self.class.encryption_config.encrypt(value)
      @encrypted_tree[stngs[:name]] = true
      # If we're just marking an existing value as encrypted
    elsif config_tree[stngs[:name]]
      value = config_tree[stngs[:name]]
      raise SettingTypeError.new('string (encrypted values must be strings)', value) unless value.is_a?(String)

      stngs[:value] = self.class.encryption_config.encrypt(value)
      @encrypted_tree[stngs[:name]] = true
    end
  end

  validate_and_set_configuration(stngs, stng_lock, stng_type, stng_encrypted)
  self
end

#configure(node) {|Setting| ... } ⇒ Object

Configures a node of the configuration tree.

Examples:

Configuring a new node

settings.configure(:database) do
  config(:host, value: "localhost")
  config(:port, value: 5432)
end

Parameters:

  • node (Symbol, String) —

    The name of the config node key.

Yields:

  • (Setting) —

    A block that configures the new node.



110
111
112
113
114
115
116
# File 'lib/hati_config/setting.rb', line 110

def configure(node, &block)
  if config_tree[node]
    config_tree[node].instance_eval(&block)
  else
    create_new_node(node, &block)
  end
end

#int(value) ⇒ Object

Sets an integer configuration value.

Parameters:

  • value (Integer) —

    The integer value to set.



39
40
41
42
43
44
45
46
# File 'lib/hati_config/setting.rb', line 39

HatiConfig::TypeMap.list_types.each do |type|
  define_method(type.downcase) do |stng, lock = nil|
    params = { type: type }
    params[:lock] = lock if lock.nil?

    config(stng, **params)
  end
end

#load_from_hash(data, schema: {}, lock_schema: {}, encrypted_fields: {}) ⇒ Object

Loads configuration from a hash with an optional schema.

Examples:

Loading from a hash with type validation

settings.load_from_hash({ name: "admin", max_connections: 10 }, schema: { name: :str, max_connections: :int })

Parameters:

  • data (Hash) —

    The hash containing configuration data.

  • schema (Hash) (defaults to: {}) —

    Optional schema for type validation.

Raises:

  • (NoMethodError) —

    If a method corresponding to a key is not defined.

  • (SettingTypeError) —

    If a value doesn't match the specified type in the schema.



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/hati_config/setting.rb', line 75

def load_from_hash(data, schema: {}, lock_schema: {}, encrypted_fields: {})
  data.each do |key, value|
    key = key.to_sym
    type = schema[key] if schema
    lock = lock_schema[key] if lock_schema
    encrypted = encrypted_fields[key] if encrypted_fields

    if value.is_a?(Hash)
      configure(key) do
        load_from_hash(value,
                       schema: schema.is_a?(Hash) ? schema[key] : {},
                       lock_schema: lock_schema.is_a?(Hash) ? lock_schema[key] : {},
                       encrypted_fields: encrypted_fields.is_a?(Hash) ? encrypted_fields[key] : {})
      end
    elsif value.is_a?(Setting)
      configure(key) do
        load_from_hash(value.to_h, schema: schema[key], lock_schema: lock_schema[key],
                                   encrypted_fields: encrypted_fields[key])
      end
    else
      config(key => value, type: type, lock: lock, encrypted: encrypted)
    end
  end
end

#lock_schema ⇒ Object



187
188
189
190
191
192
193
# File 'lib/hati_config/setting.rb', line 187

def lock_schema
  {}.tap do |hsh|
    config_tree.each do |k, v|
      v.is_a?(HatiConfig::Setting) ? (hsh[k] = v.lock_schema) : hsh.merge!(immutable_schema)
    end
  end
end

#string(value) ⇒ Object

Sets a string configuration value.

Parameters:

  • value (String) —

    The string value to set.



39
40
41
42
43
44
45
46
# File 'lib/hati_config/setting.rb', line 39

HatiConfig::TypeMap.list_types.each do |type|
  define_method(type.downcase) do |stng, lock = nil|
    params = { type: type }
    params[:lock] = lock if lock.nil?

    config(stng, **params)
  end
end

#to_h ⇒ Hash

Converts the configuration tree into a hash.

Examples:

Converting to hash

hash = settings.to_h

Returns:

  • (Hash) —

    The config tree as a hash.



200
201
202
203
204
205
206
207
208
209
210
# File 'lib/hati_config/setting.rb', line 200

def to_h
  {}.tap do |hsh|
    config_tree.each do |k, v|
      hsh[k] = if v.is_a?(HatiConfig::Setting)
                 v.to_h
               else
                 get_value(k)
               end
    end
  end
end

#to_json(*_args) ⇒ String

Converts the configuration tree into JSON format.

Examples:

Converting to JSON

json_string = settings.to_json

Returns:

  • (String) —

    The JSON representation of the configuration tree.



229
230
231
# File 'lib/hati_config/setting.rb', line 229

def to_json(*_args)
  to_h.to_json
end

#to_yaml(dump: nil) ⇒ String?

Converts the configuration tree into YAML format.

Examples:

Converting to YAML

yaml_string = settings.to_yaml
settings.to_yaml(dump: "config.yml") # Dumps to a file

Parameters:

  • dump (String, nil) (defaults to: nil) —

    Optional file path to dump the YAML.

Returns:

  • (String, nil) —

    The YAML string or nil if dumped to a file.



219
220
221
222
# File 'lib/hati_config/setting.rb', line 219

def to_yaml(dump: nil)
  yaml = to_h.to_yaml
  dump ? File.write(dump, yaml) : yaml
end

#type_schema ⇒ Hash

Returns the type schema of the configuration.

Examples:

Retrieving the type schema

schema = settings.type_schema

Returns:

  • (Hash) —

    A hash representing the type schema.



179
180
181
182
183
184
185
# File 'lib/hati_config/setting.rb', line 179

def type_schema
  {}.tap do |hsh|
    config_tree.each do |k, v|
      v.is_a?(HatiConfig::Setting) ? (hsh[k] = v.type_schema) : hsh.merge!(schema)
    end
  end
end