Skip to content

Revamping the Configuration System

davetron5000 edited this page Mar 28, 2013 · 2 revisions

The configuration system is pretty janky at the moment:

The implementation is pretty hacky and inflexible. This is for me to noodle about what to do about it.

  1. Design so it can be tested outside of an aruba test - these tests are hard to set up and make exploring edge cases difficult.
  2. Make initconfig much fancier - possibly even allowing it to read/write particular options
  3. Differentiate configuration name from all flags that set an option, e.g. standardize on the longest string for the given option (obviously, we should accept any flag name in the config, but only write one)

Design for Testability

There's four main components to consider:

  • command - the command(s) that deal with the config (currently, the command initconfig)
  • location - where is the config file?
  • runtime access - how do we get configured values?
  • manipulation - how do we read/write the config file

If each of these were just classes, they could be tested much more simply (although overwriting the contents of global_options and options with configured values is tricky to test right now)

Make initconfig fancier

initconfig made more sense in the world of single command apps, but even then it was a bit degenerate. I think the way forward is as follows:

  • initconfig stays the same semantically, but gains more flexibility in where the config file can be located and stops writing duplicate values. Effectively, we make this useable, but deprecate it
  • A new command hierarchy, rooted at config (name changeable for backwards-compatibility):
    • app config - print the location of the config file to use
    • app config init flag1=value1 flag2=value2 - init the config file with the given global values, much like initconfig today. Main difference would be to not put app defaults into the config file and to specify only one key/value pair per flag.
    • app config set flag=value - set the config value for a global flag or switch
    • app config set command flag=value - set the config value for a command-specific flag
    • app config set command subcommand flag=value - you see where this is going
    • app config delete flag=value - remove value from config
    • New global option, --config that allows explicitly stating where the config file is
  • Beef up the config_file option, or simply replace it with a configuration concept:
# Set up a config file named .my_app.conf, and locate it first in the current directory
# where the app is being run - if it's not found, look in parent directories until we find one, if
# we don't, look in the user's home directory, finally looking in /etc/.  Also add the 
# --config-file global flag
config '.my_app.conf', :locate => [:cwd,:parents,:home,'/etc/'], :flag => true

# Set up a config file named .my_app.conf, which must be in the user's home directory.
# Do NOT set up the --config-file global flag - this is how config_file more or less works
config '.my_app.conf', :locate => :home, :flag => false

All of this should be "hookable" by the developer so that I can void things like "merged configs" or other oddities that some find useful. More pseudo code:

config '.my_app.conf', 
  :locate => [:cwd,:parents,'/etc/'], 
  :flag => "config-location",
  :on_locate => lambda { |current_config,located_config|
    # current_config is a possible-empty configuration object
    # located_config is a parsed configuration object of whatever config file we just found
    if located_config.path =~ /^\/etc/
      false
    else
      current_config.merge(located_config)
      true
    end
  },
  :on_parse => lambda { |config_file_path 
    # config_file_path is the path to a located config file
    
    # Return a Config created however you like - here we
    # imagine using JSON
    Config.from_hash(JSON.parse(config_file_path))
  }