/usr/lib/ruby/vendor_ruby/innate/options/dsl.rb is in ruby-innate 2013.02.21-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 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 173 174 175 176 177 178 179 180 181 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 | module Innate
# 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
#
class Options
def initialize(name, parent = self)
@name, @parent, = name, parent
@hash = {}
yield(self) if block_given?
end
# Shortcut for instance_eval
def dsl(&block)
instance_eval(&block) if block
self
end
# 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.
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
# Store an option in the Options instance.
#
# @param [#to_s] doc describing the purpose of this option
# @param [#to_sym] key used to access
# @param [Object] value may be anything
# @param [Hash] other optional Hash containing meta-data
# :doc, :value keys will be ignored
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
alias o option
# To avoid lookup on the parent, we can set a default to the internal Hash.
# Parameters as in {Options#o}, but without the +key+.
def default(doc, value, other = {})
@hash.default = other.merge(:doc => doc, :value => value)
end
# Add a block that will be called when a new value is set.
def trigger(key, &block)
@hash[key.to_sym][:trigger] = block
end
# 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.
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
# @param [Array] keys
# @param [Object] value
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
# Retrieve only the :value from the value hash if found via +keys+.
def [](*keys)
if value = get(*keys)
value.is_a?(Hash) ? value[:value] : value
end
end
# Assign new :value to the value hash on the current instance.
#
# TODO: allow arbitrary assignments
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
def method_missing(meth, *args)
case meth.to_s
when /^(.*)=$/
self[$1] = args.first
else
self[meth]
end
end
def merge!(hash)
hash.each_pair do |key, value|
set_value(key.to_s.split('.'), value)
end
end
def to_hash
@hash
end
def each_option(&block)
@hash.each(&block)
end
def each_pair
@hash.each do |key, values|
yield(key, self[key])
end
end
def inspect
@hash.inspect
end
def pretty_print(q)
q.pp_hash @hash
end
end
# extend your class with this
module Optioned
def self.included(into)
into.extend(SingletonMethods)
snaked = into.name.split('::').last
snaked = snaked.gsub(/\B[A-Z][^A-Z]/, '_\&').downcase.gsub(' ', '_')
options = Innate.options.sub(snaked)
into.instance_variable_set(:@options, options)
end
module SingletonMethods
attr_reader :options
end
private
def options
self.class.options
end
end
end
|