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