lib/hashr.rb in hashr-1.0.0 vs lib/hashr.rb in hashr-2.0.0.rc1
- old
+ new
@@ -1,132 +1,108 @@
require 'hashr/core_ext/ruby/hash'
-class Hashr < Hash
- autoload :EnvDefaults, 'hashr/env_defaults'
+class Hashr < BasicObject
+ require 'hashr/delegate/conditional'
+ require 'hashr/env'
- TEMPLATE = new
-
class << self
- attr_accessor :raise_missing_keys
+ attr_reader :defaults
- def define(definition)
- @definition = deep_accessorize(definition.deep_symbolize_keys)
+ def inherited(other)
+ other.default(defaults)
end
- def definition
- @definition ||= {}
+ def new(*args)
+ super(self, *args)
end
def default(defaults)
- @defaults = deep_accessorize(defaults)
+ @defaults = (self.defaults || {}).deep_merge(defaults || {})
end
+ alias :define :default
- def defaults
- @defaults ||= {}
+ def const_missing(name)
+ Kernel.const_get(name)
end
+ end
- def deep_accessorize(hash)
- hash.each do |key, value|
- next unless value.is_a?(Hash)
- value[:_access] ||= []
- value[:_access] = Array(value[:_access])
- value.keys.each { |key| value[:_access] << key if value.respond_to?(key) }
- deep_accessorize(value)
- end
+ attr_reader :class
+
+ def initialize(klass, data = nil, defaults = nil, &block)
+ ::Kernel.fail ::ArgumentError.new("Invalid input #{data.inspect}") unless data.nil? || data.is_a?(::Hash)
+
+ data = (data || {}).deep_symbolize_keys
+ defaults = (defaults || klass.defaults || {}).deep_symbolize_keys
+
+ @class = klass
+ @data = defaults.deep_merge(data).inject({}) do |result, (key, value)|
+ result.merge(key => value.is_a?(::Hash) ? ::Hashr.new(value, {}) : value)
end
end
- undef :id if method_defined?(:id) # undefine deprecated method #id on 1.8.x
-
- def initialize(data = {}, definition = self.class.definition, &block)
- raise(ArgumentError.new("Invalid input #{data.inspect}")) unless data.nil? || data.is_a?(Hash)
- replace((deep_hashrize(definition.deep_merge((data || {}).deep_symbolize_keys))))
- deep_defaultize(self)
- (class << self; self; end).class_eval(&block) if block_given?
+ def defined?(key)
+ @data.key?(to_key(key))
end
- def [](key, default = nil)
- store(key.to_sym, Hashr.new(default)) if default && !key?(key)
- super(key.to_sym)
+ def [](key)
+ @data[to_key(key)]
end
def []=(key, value)
- super(key.to_sym, value.is_a?(Hash) ? self.class.new(value, {}) : value)
+ @data.store(to_key(key), value.is_a?(::Hash) ? ::Hashr.new(value, {}) : value)
end
- def set(path, value, stack = [])
- tokens = path.to_s.split('.')
- tokens.size == 1 ? self[path] = value : self[tokens.shift, Hashr.new].set(tokens.join('.'), value, stack)
+ def values_at(*keys)
+ keys.map { |key| self[key] }
end
- def respond_to?(method)
- if self.class.raise_missing_keys
- key?(method)
- else
- true
- end
+ def respond_to?(*args)
+ true
end
def method_missing(name, *args, &block)
case name.to_s[-1, 1]
when '?'
- !!self[name.to_s[0..-2].to_sym]
+ !!self[name.to_s[0..-2]]
when '='
- self[name.to_s[0..-2].to_sym] = args.first
+ self[name.to_s[0..-2]] = args.first
else
- raise(IndexError.new("Key #{name.inspect} is not defined.")) if !key?(name) && self.class.raise_missing_keys
self[name]
end
end
- def include_modules(modules)
- Array(modules).each { |mod| meta_class.send(:include, mod) } if modules
+ def try(key)
+ defined?(key) ? self[key] : nil # TODO needs to look for to_h etc
end
- def include_accessors(accessors)
- Array(accessors).each { |accessor| meta_class.send(:define_method, accessor) { self[accessor] } } if accessors
+ def to_h
+ @data.inject({}) do |hash, (key, value)|
+ hash.merge(key => value.respond_to?(:to_h) ? value.to_h : value)
+ end
end
+ alias to_hash to_h
- def meta_class
- class << self; self end
+ def ==(other)
+ to_h == other.to_h if other.respond_to?(:to_h)
end
- def to_hash
- inject({}) do |hash, (key, value)|
- hash[key] = value.is_a?(Hashr) ? value.to_hash : value
- hash
- end
+ def instance_of?(const)
+ self.class == const
end
- protected
+ def is_a?(const)
+ consts = [self.class]
+ consts << consts.last.superclass while consts.last.superclass
+ consts.include?(const)
+ end
+ alias kind_of? is_a?
- def deep_hashrize(hash)
- hash.inject(TEMPLATE.dup) do |result, (key, value)|
- case key.to_sym
- when :_include
- result.include_modules(value)
- when :_access
- result.include_accessors(value)
- else
- result.store(key.to_sym, value.is_a?(Hash) ? deep_hashrize(value) : value)
- end
- result
- end
- end
+ def inspect
+ "<#{self.class.name} #{@data.inspect}>"
+ end
- def deep_defaultize(hash)
- self.class.defaults.each do |key, value|
- case key.to_sym
- when :_include
- hash.include_modules(value)
- when :_access
- hash.include_accessors(value)
- end
- end
+ private
- hash.each do |key, value|
- deep_defaultize(value) if value.is_a?(Hash)
- end
-
- hash
+ def to_key(key)
+ key.respond_to?(:to_sym) ? key.to_sym : key
end
end