Helper hash subclass with three purposes:
Give a type to the objects returned, rather than just being Hashes.
Provide attribute readers that fail if the key is missing, to help detect mistakes faster.
Simplify serialization by making all keys strings, not symbols, but still allowing key access by symbol.
Convert Hash subclasses into plain hashes for YAML/XML dump. Assumptions:
Hash keys will be strings - don't need to clear them No custom objects with Hash subclass contents
# File lib/admin/stats/results.rb, line 62 def self.deep_clear_subclasses(obj) deep_convert(obj, {}, lambda { Hash.new }) end
Convert plain hashes into HashWithReaders Assumptions:
Hash keys will be strings - don't need to convert them No custom objects with Hash subclass contents
# File lib/admin/stats/results.rb, line 70 def self.deep_convert_hashes(obj) deep_convert(obj, {}, lambda { HashWithReaders.new }) end
# File lib/admin/stats/results.rb, line 75 def self.deep_convert(obj, dedup, new_lambda) id = obj.__id__ # used to deduplicate converted copies case obj when Hash return dedup[id] if dedup.has_key? id dedup[id] = copy = new_lambda.call obj.each {|k,v| copy[k.to_s] = deep_convert(v, dedup, new_lambda)} copy when Array return dedup[id] if dedup.has_key? id obj.inject(dedup[id] = []) {|a,v| a << deep_convert(v, dedup, new_lambda)} else obj # not going to operate on other kinds of objects end end
Keys switched from symbols to strings - so, translate for any code already accessing by symbol.
# File lib/admin/stats/results.rb, line 15 def [](key); super(key.to_s); end
# File lib/admin/stats/results.rb, line 16 def []=(key, *args); super(key.to_s, *args); end
# File lib/admin/stats/results.rb, line 17 def has_key?(key); super(key.to_s); end
# File lib/admin/stats/results.rb, line 38 def inspect; "#{self.class} #{super}"; end
# File lib/admin/stats/results.rb, line 18 def merge(hash); self.clone.merge! hash; end
# File lib/admin/stats/results.rb, line 19 def merge!(hash) hash.each {|k,v| self[k] = v} #ah, but now keys are converted to string. self end
provide readers for keys
# File lib/admin/stats/results.rb, line 25 def method_missing(sym, *args) # don't screen other things that operate via method_missing return super if [:to_ary, :to_json, :to_yaml, :to_xml].include? sym key = sym.to_s return self[key] if self.has_key? key raise NoSuchKey.new("#{self.class} has no key #{sym}: #{self.inspect}") end
# File lib/admin/stats/results.rb, line 39 def pretty_inspect; "#{self.class} #{super}"; end
# File lib/admin/stats/results.rb, line 32 def respond_to?(sym, *args) has_key?(sym) || super end
make classes easily identified in output
# File lib/admin/stats/results.rb, line 37 def to_s; "#{self.class} #{super}"; end