Class: Innate::Options

Inherits:
Object
  • Object
show all
Defined in:
lib/innate/options/dsl.rb

Overview

Provides a minimal DSL to describe options with defaults and metadata.

The example below should demonstrate the major features, note that key lookup wanders up the hierarchy until there is a match found or the parent of the Options class is itself, in which case nil will be returned.

Usage:

class Calculator
  @options = Options.new(:foo)
  def self.options; @options; end

  options.dsl do
    o "Which method to use", :method, :plus
    o "Default arguments", :args, [1, 2]
    sub(:minus){ o("Default arguments", :args, [4, 3]) }
  end

  def self.calculate(method = nil, *args)
    method ||= options[:method]
    args = args.empty? ? options[method, :args] : args
    self.send(method, *args)
  end

  def self.plus(n1, n2)
    n1 + n2
  end

  def self.minus(n1, n2)
    n1 - n2
  end
end

Calculator.calculate
# => 3
Calculator.options[:method] = :minus
# => :minus
Calculator.calculate
# => 1
Calculator.calculate(:plus, 4, 5)
# => 9

Instance Method Summary collapse

Constructor Details

#initialize(name, parent = self) {|_self| ... } ⇒ Options

Returns a new instance of Options.

Yields:

  • (_self)

Yield Parameters:



45
46
47
48
49
# File 'lib/innate/options/dsl.rb', line 45

def initialize(name, parent = self)
  @name, @parent, = name, parent
  @hash = {}
  yield(self) if block_given?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object



147
148
149
150
151
152
153
154
# File 'lib/innate/options/dsl.rb', line 147

def method_missing(meth, *args)
  case meth.to_s
  when /^(.*)=$/
    self[$1] = args.first
  else
    self[meth]
  end
end

Instance Method Details

#[](*keys) ⇒ Object

Retrieve only the :value from the value hash if found via keys.



125
126
127
128
129
# File 'lib/innate/options/dsl.rb', line 125

def [](*keys)
  if value = get(*keys)
    value.is_a?(Hash) ? value[:value] : value
  end
end

#[]=(key, value) ⇒ Object

Assign new :value to the value hash on the current instance.

TODO: allow arbitrary assignments



134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/innate/options/dsl.rb', line 134

def []=(key, value)
  ks = key.to_sym
  if @hash.has_key? ks
    ns = @hash[ks]
    ns[:value] = value
    ns[:trigger].call(value) if ns[:trigger].respond_to?(:call)
  elsif existing = get(key)
    option(existing[:doc].to_s.dup, key, value)
  else
    raise(ArgumentError, "No key for %p exists" % [key])
  end
end

#default(doc, value, other = {}) ⇒ Object

To avoid lookup on the parent, we can set a default to the internal Hash. Parameters as in #o, but without the key.



89
90
91
# File 'lib/innate/options/dsl.rb', line 89

def default(doc, value, other = {})
  @hash.default = other.merge(:doc => doc, :value => value)
end

#dsl(&block) ⇒ Object

Shortcut for instance_eval



52
53
54
55
# File 'lib/innate/options/dsl.rb', line 52

def dsl(&block)
  instance_eval(&block) if block
  self
end

#each_option(&block) ⇒ Object



166
167
168
# File 'lib/innate/options/dsl.rb', line 166

def each_option(&block)
  @hash.each(&block)
end

#each_pairObject



170
171
172
173
174
# File 'lib/innate/options/dsl.rb', line 170

def each_pair
  @hash.each do |key, values|
    yield(key, self[key])
  end
end

#get(key, *keys) ⇒ Object

Try to retrieve the corresponding Hash for the passed keys, will try to retrieve the key from a parent if no match is found on the current instance. If multiple keys are passed it will try to find a matching child and pass the request on to it.



102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/innate/options/dsl.rb', line 102

def get(key, *keys)
  if keys.empty?
    if value = @hash[key.to_sym]
      value
    elsif @parent != self
      @parent.get(key)
    else
      nil
    end
  elsif sub_options = get(key)
    sub_options.get(*keys)
  end
end

#inspectObject



176
177
178
# File 'lib/innate/options/dsl.rb', line 176

def inspect
  @hash.inspect
end

#merge!(hash) ⇒ Object



156
157
158
159
160
# File 'lib/innate/options/dsl.rb', line 156

def merge!(hash)
  hash.each_pair do |key, value|
    set_value(key.to_s.split('.'), value)
  end
end

#option(doc, key, value, other = {}, &block) ⇒ Object Also known as: o

Store an option in the Options instance.

Parameters:

  • doc (#to_s)

    describing the purpose of this option

  • key (#to_sym)

    used to access

  • value (Object)

    may be anything

  • other (Hash) (defaults to: {})

    optional Hash containing meta-data :doc, :value keys will be ignored



79
80
81
82
83
84
# File 'lib/innate/options/dsl.rb', line 79

def option(doc, key, value, other = {}, &block)
  trigger = block || other[:trigger]
  convert = {:doc => doc.to_s, :value => value}
  convert[:trigger] = trigger if trigger
  @hash[key.to_sym] = other.merge(convert)
end

#pretty_print(q) ⇒ Object



180
181
182
# File 'lib/innate/options/dsl.rb', line 180

def pretty_print(q)
  q.pp_hash @hash
end

#set_value(keys, value) ⇒ Object

Parameters:

  • keys (Array)
  • value (Object)

Raises:

  • (IndexError)


118
119
120
121
122
# File 'lib/innate/options/dsl.rb', line 118

def set_value(keys, value)
  got = get(*keys)
  return got[:value] = value if got
  raise(IndexError, "There is no option available for %p" % [keys])
end

#sub(name, &block) ⇒ Object

Create a new Options instance with name and pass block on to its #dsl. Assigns the new instance to the name Symbol on current instance.



59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/innate/options/dsl.rb', line 59

def sub(name, &block)
  name = name.to_sym

  case found = @hash[name]
  when Options
    found.dsl(&block)
  else
    found = @hash[name] = Options.new(name, self).dsl(&block)
  end

  found
end

#to_hashObject



162
163
164
# File 'lib/innate/options/dsl.rb', line 162

def to_hash
  @hash
end

#trigger(key, &block) ⇒ Object

Add a block that will be called when a new value is set.



94
95
96
# File 'lib/innate/options/dsl.rb', line 94

def trigger(key, &block)
  @hash[key.to_sym][:trigger] = block
end