lib/conf.rb in conf-0.0.5 vs lib/conf.rb in conf-0.0.6
- old
+ new
@@ -1,13 +1,61 @@
class Conf
class InvalidKeyError < StandardError
end
-
+
class InvalidStateError < StandardError
end
+ module ConfigValue
+ def self.create(root, key, obj = Object.new)
+ begin
+ obj.extend(self)
+ obj.__setup__(root, key)
+ rescue TypeError
+ # can't extend numbers, false, nil etc.
+ end
+
+ obj
+ end
+
+ def __setup__(root, key)
+ @__root__ = root
+ @__key__ = key
+
+ self
+ end
+
+ def method_missing(meth, *args, &blk)
+ m = meth.to_s
+ if m =~ /^(\w+)=/ || args.size == 1
+ @__root__.check_lock
+ key = [@__key__, $1 || m].compact.join(".")
+ @__root__[key] = ConfigValue.create(@__root__, key, args.first)
+ else
+ key = [@__key__, m].compact.join(".")
+
+ obj = @__root__.data[key]
+
+ if obj.nil?
+ if @__root__.locked?
+ obj = @__root__.fetch(key) { raise Conf::InvalidKeyError, key }
+ else
+ obj = @__root__.data[key] = ConfigValue.create(@__root__, key)
+ end
+ end
+
+ if blk
+ @__root__.check_lock
+ obj.instance_eval(&blk)
+ end
+
+ obj
+ end
+ end
+ end # ConfigValue
+
def self.configs
@configs ||= {}
end
def self.define(name, parent = nil, &blk)
@@ -15,127 +63,107 @@
when String, Symbol
parent = get(parent)
when Configuration, nil
# ok
else
- raise TypeError, "expected String, Symbol, Configuration or nil, got #{parent.inspect}:#{parent.class}"
+ raise TypeError,
+ "expected String, Symbol, Configuration or nil, got #{parent.inspect}:#{parent.class}"
end
conf = configs[name] ||= Configuration.new(parent)
-
conf.instance_eval(&blk)
+
conf.lock!
conf
end
def self.get(name)
configs[name] or raise ArgumentError, "no config named #{name.inspect}"
end
class Configuration
+ include ConfigValue
+
def initialize(parent = nil)
if parent and not parent.kind_of? self.class
raise TypeError, "expected #{self.class}, got #{parent.inspect}:#{parent.class}"
end
- @parent = parent
- @data = {}
- @current_nesting = []
- @locked = false
+ @parent = parent
+ @data = {}
+ @locked = false
+ @__root__ = self
end
def key?(key)
@data.key?(key) || (@parent && @parent.key?(key))
end
def lock!
@locked = true
end
-
+
def unlock!
@locked = false
end
-
+
+ def unlocked(&blk)
+ unlock!
+ yield
+ lock!
+ end
+
+ def check_lock
+ if locked?
+ raise InvalidStateError, "config is locked #{@data.keys.inspect}"
+ end
+ end
+
def edit(&blk)
edit!
instance_eval(&blk)
done!
end
-
+
def locked?
@locked
end
def section(start_key)
result = @parent ? @parent.section(start_key) : {}
+ rx = /^#{Regexp.escape(start_key).gsub("\\*", ".+?")}/
@data.each do |key, value|
- result[key] = value if key =~ /^#{Regexp.escape start_key}/
+ result[key] = value if key =~ rx and not value.instance_of? Object
end
result
end
- protected
-
def data() @data end
- def [](key)
- k = expand_key(key)
- val = @data[k]
- val.nil? ? @parent && @parent[k] : val
+ def fetch(key, &blk)
+ val = self[key]
+ if val.nil?
+ @data[key] = yield(key)
+ else
+ val
+ end
end
- def []=(key, value)
- @data[expand_key(key)] = value
- @current_nesting.clear
- end
- def expand_key(key)
- [@current_nesting, key].flatten.compact.join "."
- end
+ def [](key)
+ val = @data[key]
- def method_missing(meth, *args, &blk)
- m = meth.to_s
-
- if m =~ /^(\w+)=/ || args.size == 1
- check_lock
- key = $1 || m
- self[key] = args.first
- elsif blk
- check_lock
- @current_nesting << m
- instance_eval(&blk)
- @current_nesting.pop
+ if val.nil?
+ @parent && @parent[key]
else
- obj = self[m]
- if obj != nil
- @current_nesting.clear
- obj
- else
- @current_nesting << m
- validate_nesting if locked?
- self
- end
+ val
end
end
- def validate_nesting
- current = expand_key(nil)
- match_proc = Proc.new { |key,_| key =~ /^#{Regexp.escape current}/ }
-
- unless @data.any?(&match_proc) || (@parent && @parent.data.any?(&match_proc))
- @current_nesting.clear
- raise InvalidKeyError, "no such key: #{current.inspect}"
- end
+ def []=(key, value)
+ @data[key] = value
end
-
- def check_lock
- if locked?
- @current_nesting.clear
- raise InvalidStateError, "config is locked"
- end
- end
end
-end
-
+end