README

Path: README
Last Update: Sat May 01 09:15:28 -0600 2010

Configurable

Class configurations that map to the command line.

Description

Configurable allows the declaration of class configurations. Configurations are inheritable, delegate to methods, and have hash-like access. Configurable maps configurations to the command line through ConfigParser and is used by the Tap framework.

Check out these links for development and bug tracking.

Minimal Example

Include Configurable and declare configurations using the config method. Configs now have accessors and initialize with the default value.

  class ConfigClass
    include Configurable
    config :key, 'default', :short => 'k'   # a sample config
  end

  c = ConfigClass.new
  c.key                   # => 'default'
  c.key = 'new value'
  c.config[:key]          # => 'new value'

Use a ConfigParser to parse configurations from the command line. Non-class options may be defined with (mostly) the same syntax as OptionParser:

  parser = ConfigParser.new

  # add class configurations
  parser.add(ConfigClass.configurations)

  # define an option a-la OptionParser
  parser.on '-s', '--long ARGUMENT', 'description' do |value|
    parser[:long] = value
  end

  parser.parse "one two --key value -s VALUE three"
  # => ['one', 'two', 'three']

  parser.config
  # => {
  # :key => 'value',
  # :long => 'VALUE'
  # }

  "\n" + parser.to_s
  # => %Q{
  #    -k, --key KEY                    a sample config
  #    -s, --long ARGUMENT              description
  # }

Usage

The config method is used to declare class configurations. A block may be provided to validate/transform inputs; many standard validations are available through the ‘c’ method (an alias for the Validation module).

  class ConfigClass
    include Configurable

    # basic #

    config :key, 'default'                    # a simple config
    config :flag, false, &c.flag              # a flag config
    config :switch, false, &c.switch          # a --[no-]switch config
    config :num, 10, &c.integer               # integer only

    # fancy #

    config :range, 1..10, &c.range            # specifies a range
    config :select, 'a', &c.select('a','b')   # value must be 'a' or 'b'
    config :list, [], &c.list                 # allows a list of entries

    # custom #

    config :upcase, 'default' do |value|      # custom transformation
      value.upcase
    end

    config :alt, 'default',                   # alternative flags
      :short => 's',
      :long => 'long',
      :arg_name => 'CUSTOM'

    # Initializes a new instance, setting the overriding configs.
    def initialize(config={})
      initialize_config(config)
    end
  end

ConfigParser uses the config declarations to parse configurations and to make a documented help string:

  parser = ConfigParser.new
  parser.add(ConfigClass.configurations)

  parser.parse "a b --key=value --flag --no-switch --num 8 c"
  # => ['a', 'b', 'c']

  parser.config
  # => {
  # :key => 'value',
  # :flag => true,
  # :switch => false,
  # :num => '8',
  # :range => 1..10,
  # :select => 'a',
  # :list => [],
  # :upcase => 'default',
  # :alt => 'default'
  # }

  "\n" + parser.to_s
  # => %Q{
  #        --key KEY                    a simple config
  #        --flag                       a flag config
  #        --[no-]switch                a --[no-]switch config
  #        --num NUM                    integer only
  #        --range RANGE                specifies a range
  #        --select SELECT              value must be 'a' or 'b'
  #        --list LIST                  allows a list of entries
  #        --upcase UPCASE              custom transformation
  #    -s, --long CUSTOM                alternative flags
  # }

Configurable classes typically call initialize_config to set configurations during initialization. The validation/transformation blocks are called as configurations are set. Notice how the :num and :upcase configs are translated on the instance:

  c = ConfigClass.new(parser.config)
  c.config.to_hash
  # => {
  # :key => 'value',
  # :flag => true,
  # :switch => false,
  # :num => 8,                    # no longer a string
  # :range => 1..10,
  # :select => 'a',
  # :list => [],
  # :upcase => 'DEFAULT',         # no longer downcase
  # :alt => 'default'
  # }

Configurations automatically generate accessors (the blocks are basically writer methods), and may be accessed through the config object. Configurations are validated every time they are set, regardless of whether they are set through an accessor or config.

  c.upcase                    # => 'DEFAULT'

  c.config[:upcase] = 'neW valuE'
  c.upcase                    # => 'NEW VALUE'

  c.upcase = 'fiNal Value'
  c.config[:upcase]           # => 'FINAL VALUE'

  c.select = 'b'              # ok
  c.select = 'c'              # !> ValidationError
  c.config[:select] = 'c'     # !> ValidationError

By default config treats string and symbol keys identically, making YAML an obvious choice for configuration files.

  yaml_str = %Q{
  key: a new value
  flag: false
  range: 1..100
  }

  c.reconfigure(YAML.load(yaml_str))
  c.config.to_hash
  # => {
  # :key => 'a new value',
  # :flag => false,
  # :switch => false,
  # :num => 8,
  # :range => 1..100,
  # :select => 'b',
  # :list => [],
  # :upcase => 'FINAL VALUE',
  # :alt => 'default'
  # }

See the Configurable module for more details.

Installation

Configurable is available as a gem on Gemcutter.

  % gem install configurable

Info

Developer:Simon Chiang
License:MIT-Style

[Validate]