Class: FeduxOrgStdlib::AppConfig

Inherits:
Object
  • Object
show all
Defined in:
lib/fedux_org_stdlib/app_config.rb,
lib/fedux_org_stdlib/app_config/exceptions.rb

Overview

This class makes a config file available as an object. The config file needs to be ‘YAML` by default. It is read by `Psych` and converted to a hash. If you chose to use a different file format: Each config file needs to translatable to a hash or another data structure which responds to `[]` by the given `config_engine`. If no suitable config file can be found the config uses only the defined defaults within the class.

By default it will look for a suitable config file in the given order:

  1. $HOME/.config/<application_name>/<config_file>.yaml

  2. $HOME/.<application_name>/<config_file>.yaml

  3. $HOME/.<config_file>.yaml

  4. $HOME/.<config_file>rc

  5. /etc/.<application_name>/<config_file>.yaml

Please keep in mind

  • application_name: Module of your class, e.g. “MyApplication” becomes “my_application”

  • config_file: Pluarized name of your class and “Config” strip off, e.g “ClientConfig” becomes “clients.yaml” (mind the pluralized name)

Most conventions defined by me are implemented as separate methods. If one convention is not suitable for your use case, just overwrite the method.

If you prefer to use a different path to the config file or name of the config file one of the following methods needs to be overwritten:

  • config_file

  • config_name

  • application_name

If you want the class to look for your config file at a different place overwrite the following method

  • allowed_config_file_paths

Below you find some examples for the usage of the class:

Examples:

Create config with one writer and reader

module MyApplication
  class ClientConfig < AppConfig
    # 'data1' is the default for option1
    # if you create a file
    option :option1, 'data1'
  end
end

Create config with a reader only

module MyApplication
  class ClientConfig < AppConfig
    # 'data1' is the default for option1
    # if you create a file
    option_reader :option1, 'data1'
  end
end

Config yaml file for the classes above: clients.yaml

---
option1: 'data2'

Defined Under Namespace

Modules: Exceptions

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file: _available_config_file, config_engine: Psych, logger: FeduxOrgStdlib::Logging::Logger.new, check_unknown_options: true) ⇒ AppConfig

Create a new instance of config

It tries to find a suitable configuration file. If it doesn’t find one the config is empty and uses the defaults defined within a config class

Parameters:

  • file (String) (defaults to: _available_config_file)

    Path where config file is stored. The file will be read by the ‘config_engine`.

  • config_engine (Object) (defaults to: Psych)

    (Psych) The engine to read config file

  • check_unknown_options (true, false) (defaults to: true)

    Should a warning be put to stdout if there are unknown options in yaml config file

Raises:



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/fedux_org_stdlib/app_config.rb', line 182

def initialize(
  file: _available_config_file, 
  config_engine: Psych,
  logger: FeduxOrgStdlib::Logging::Logger.new,
  check_unknown_options: true
)
  @logger = logger

  unless file
    logger.debug "No configuration file found at #{_allowed_config_file_paths.to_list}, therefor I'm going to use an empty config object instead." 
    @__config = {}

    return 
  end

  begin
    yaml = Psych.safe_load(File.read(file), [Symbol])
  rescue StandardError => e
    fail Exceptions::ConfigFileNotReadable, JSON.dump(message: e.message, file: file)
  end

  if yaml.blank?
    @__config = {}
  elsif yaml.kind_of? Hash
    yaml = yaml.deep_symbolize_keys

    yaml_withknown_options = yaml.deep_symbolize_keys.slice(*self.known_options)
    unknown_options = yaml.keys - yaml_withknown_options.keys

    logger.warn "Unknown config options #{(unknown_options).to_list} in config file #{file} detected. Please define them in your config class or remove the entries in your config file or disable check via `check_unknown_options: false` to get rid of this warning." unless unknown_options.blank? && check_unknown_options == true

    @__config = Hash(yaml_withknown_options)
  else
    logger.warn "There seems to be a problem transforming config file \"#{file}\" to a hash, therefor I will use an empty config object."
    @__config = {}
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(*args, &block) ⇒ Object (private)



356
357
358
359
360
# File 'lib/fedux_org_stdlib/app_config.rb', line 356

def method_missing(*args, &block)
  $stderr.puts "Please check if you have defined an option for #{args.first}."

  super
end

Class Method Details

.known_optionsObject

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.



75
76
77
# File 'lib/fedux_org_stdlib/app_config.rb', line 75

def known_options
  @options ||= Set.new
end

.option(option, default_value) ⇒ Object

Define a writer and a reader for option

Parameters:

  • option (Symbol)

    Name of option

  • default_value (Object)

    Default value of option



144
145
146
147
# File 'lib/fedux_org_stdlib/app_config.rb', line 144

def option(option, default_value)
  option_reader(option, default_value)
  option_writer(option)
end

.option_reader(option, default_value) ⇒ Object

Define a reader for option

Parameters:

  • option (Symbol)

    Name of option

  • default_value (Object)

    The default value of this option



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/fedux_org_stdlib/app_config.rb', line 98

def option_reader(option, default_value)
  option =  option.to_sym

  fail Exceptions::OptionNameForbidden, JSON.dump(option: option) if _reserved_key_words.include? option

  define_method option do
    _config.fetch(option, default_value)
  end

  self.known_options << option
end

.option_writer(option) ⇒ 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.

Define a writer for option

Please make sure that you define a reader as well. Otherwise you cannot access the option. Under normal conditions it does not make sense to use this method.

Parameters:

  • option (Symbol)

    Name of option

Raises:



123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/fedux_org_stdlib/app_config.rb', line 123

def option_writer(option)
  fail Exceptions::OptionNameForbidden, JSON.dump(option: option) if _reserved_key_words.include? "#{option}=".to_sym

  define_method "#{option}=".to_sym do |value|
    begin
      _config[option.to_sym] = value
    rescue RuntimeError
      raise Exceptions::ConfigLocked
    end
  end

  self.known_options << option
end

.process_environmentObject

Get access to process environment

This might be handy to define default options

Examples:

Get variable


process_environment.fetch('HOME')
# => ENV['HOME']


87
88
89
# File 'lib/fedux_org_stdlib/app_config.rb', line 87

def process_environment
  @process_environment ||= ProcessEnvironment.new
end

Instance Method Details

#known_optionsObject

Show known options for configuration



221
222
223
# File 'lib/fedux_org_stdlib/app_config.rb', line 221

def known_options
  self.class.known_options
end

#lockObject

Lock the configuration



226
227
228
# File 'lib/fedux_org_stdlib/app_config.rb', line 226

def lock
  _config.freeze
end

#preferred_configuration_fileString

Return the path to the preferred configuration file

Returns:

  • (String)

    The path to the preferred configuration file



264
265
266
# File 'lib/fedux_org_stdlib/app_config.rb', line 264

def preferred_configuration_file
  _allowed_config_file_paths.first
end

#to_sString

Output a string presentation of the configuration

Returns:

  • (String)

    An formatted version of the configuration



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/fedux_org_stdlib/app_config.rb', line 234

def to_s
  # header 'length' = 6 letters
  length = self.class.known_options.inject(6) { |memo, o| o.size > memo ? o.size : memo }

  result = []
  result << sprintf("%#{length}s | %s", 'option', 'value')
  result << sprintf("%s + %s", '-' * length, '-' * 80)

  self.class.known_options.each do |o|
    value = public_send(o)

    value = if value == false
              Array(value)
            elsif value.blank?
              Array('is undefined')
            elsif value.is_a?(Hash) || value.is_a?(Array)
              value
            else
              Array(value)
            end

    result << sprintf("%#{length}s | %s", o, value.to_list)
  end

  result.join("\n")
end