require "mashed/stringy_hash" module Mashed class Mash < BasicObject def singleton_method_added(symbol) @singleton_methods ||= [] @singleton_methods << symbol end def singleton_method_removed(symbol) @singleton_methods ||= [] @singleton_methods.delete symbol end def singleton_method_undefined(symbol) singleton_method_removed(symbol) end unless defined?(object_id) def object_id __id__ end end def to_hash unwrap @hash end alias to_h to_hash def to_json(*args) to_hash.to_json(*args) end def puts(*args) ::Kernel.puts(*args) end klass = self define_method(:class) { klass } define_method(:methods) { klass.instance_methods + @singleton_methods + keys } def self.name "Mashed::Mash" end def initialize(hash) @singleton_methods ||= [] hash = if hash.respond_to?(:to_h) hash.to_h else hash.to_hash end @hash = StringyHash.stringify(hash) end def is_a?(other) other == self.class end alias kind_of? is_a? def delete(key) wrap_up @hash.delete(key) end def method_missing(symbol, *args, &blk) string = symbol.to_s if blk super elsif args.length == 0 if @hash.key?(string) self[string] elsif string =~ /\?$/ !!self[string[0..-2]] else nil end elsif args.length == 1 && string =~ /=$/ self[string[0..-2]] = args.first else super end end def respond_to?(symbol) methods.map(&:to_s).include?(symbol.to_s) end def inspect "##{@hash.inspect}>" end alias to_s inspect # I hate pry def pretty_inspect(*) inspect end def pretty_print(*) inspect end private def [](key) key = key.to_s wrap_up @hash[key] end def []=(key, value) key = key.to_s @hash[key] = value end def keys @hash.keys end def wrap_up(thing) if thing.respond_to?(:to_ary) thing.map { |t| wrap(t) } else wrap(thing) end end def wrap(thing) if thing.respond_to?(:to_hash) self.class.new thing else thing end end def unwrap(thing) if thing.respond_to?(:to_hash) thing.to_hash.each_with_object({}) do |(key, value), hash| hash[key] = unwrap(value) end elsif thing.respond_to?(:to_ary) thing.map { |t| unwrap(t) } else thing end end end end