class Metadata

MetadataHash - A specific use of ruby’s Hash

overrides Hash’s method missing, providing the following functionality:

  1. Access Nested hashes using the method / attribute syntax

i.e.: h = {}
  h.middle.inner == {}
  1. Access to values stored in nested hashes via method call syntax

i.e.: h = { middle: { inner: { key: "value" } } }
  h.middle.inner.key == "value"
  1. 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 } }
  1. 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

new(hash = {}) click to toggle source

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

[](key) click to toggle source

Metdata has indifferent access

Calls superclass method
# File lib/metahash/metadata.rb, line 84
def [](key)
  # self.send(key)
  super(key.to_sym)
end
[]=(key, value) click to toggle source

Metadata has indifferent access, so just say that all the keys are symbols.

Calls superclass method
# 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
key_not_in_use?(key) click to toggle source

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
method_missing(method_name, *args) click to toggle source

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
to_a()
Alias for: to_ary
to_ary() click to toggle source
# File lib/metahash/metadata.rb, line 123
def to_ary
  self.to_hash.to_a
end
Also aliased as: to_a
to_hash() click to toggle source

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

assign_value(key, value) click to toggle source

@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