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