class Metadata
MetadataHash - A specific use of ruby’s Hash
overrides Hash’s method missing, providing the following functionality:
-
Access Nested hashes using the method / attribute syntax
i.e.: h = {} h.middle.inner == {}
-
Access to values stored in nested hashes via method call syntax
i.e.: h = { middle: { inner: { key: "value" } } } h.middle.inner.key == "value"
-
Set values for nested hash structures without middle nested hashes
having to be defined i.e.: h = {} h.middle.inner = 3 h == { middle: { inner: 3 } }
-
Old hash square bracket access still works
i.e.: h = { inner: { key: "value" } } h[:inner][:key] == "value"
Constants
- METHOD_BACKUP_KEY
in the event we are overriding a method, have a way to get back to the original
Public Class Methods
the hash being passed in will have all its subhashes converted to metadata hashes. this is needed to we can have the
@raise [ArgumentError] if one of the keys is method of Hash
@raise [ArgumentError] if hash is not a type of Hash
or Metadata
@param [Hash] hash the structure to convert to Metadata
# File lib/metahash/metadata.rb, line 35 def initialize(hash = {}) # for maybe instantiating nested hashes that we # aren't yet sure if they are going to have values or not @empty_nested_hashes = [] if hash.is_a?(Metadata) # we have nothing to do return hash elsif hash.is_a?(Hash) # recursively create nested metadata objects hash.each do |key, value| self[ key ] = ( if value.is_a?(Hash) Metadata.new(value) elsif value.is_a?(Array) # ensure hashes kept in an array are also converted to metadata array = value.map{ |element| element.is_a?(Hash) ? Metadata.new(element) : element } else value end ) end else raise ArgumentError.new("Field must be a Hash or Metadata") end end
Public Instance Methods
Metdata has indifferent access
# File lib/metahash/metadata.rb, line 84 def [](key) # self.send(key) super(key.to_sym) end
Metadata
has indifferent access, so just say that all the keys are symbols.
# File lib/metahash/metadata.rb, line 91 def []=(key, value) if value.is_a?(Hash) && !value.is_a?(Metadata) value = Metadata.new(value) end super(key.to_sym, value) end
tests the ability to use this key as a key in a hash @param [Symbol] key @return [Boolean] whether or not this can be used as a hash key
# File lib/metahash/metadata.rb, line 101 def key_not_in_use?(key) not self.respond_to?(key) end
this is what allows functionality mentioned in the class comment to happen @raise [ArgumentError] if one of the keys is method of Hash
# File lib/metahash/metadata.rb, line 68 def method_missing(method_name, *args) # check for assignment if method_name.to_s[-1] == "=" assign_value(method_name, args[0]) else value = self[method_name] if value.nil? @empty_nested_hashes << method_name value = self end value end end
# File lib/metahash/metadata.rb, line 123 def to_ary self.to_hash.to_a end
convert to regular hash, recursively
# File lib/metahash/metadata.rb, line 106 def to_hash hash = {} self.each do |k,v| hash[k] = ( if v.is_a?(Metadata) v.to_hash elsif v.is_a?(Array) v.map{ |e| e.is_a?(Metadata) ? e.to_hash : e } else v end ) end hash end
Private Instance Methods
@param [Symbol] key “field_name=”“
# File lib/metahash/metadata.rb, line 132 def assign_value(key, value) key = key.to_s.chop deepest_metadata = self value = Metadata.new(value) if value.is_a?(Hash) if not @empty_nested_hashes.empty? @empty_nested_hashes.each do |key| deepest_metadata = deepest_metadata[key] = Metadata.new end @empty_nested_hashes = [] deepest_metadata[key] = value # override any existing method with the key deepest_metadata.instance_define(key){ self[key] } else self[key] = value # override any existing method with the key self.instance_define(key){ value } end end