class Serializer::Serializable

Some utility methods to faciliate serialization of data fields to Hash, JSON, or YAML shared by all subclasses. This class assumes that HappyMapper is used for declaration of fields to be serialized.

Data Model

@see github.com/jnunemaker/happymapper @note Copyright © 2012 by The Board of Trustees of the Leland Stanford Junior University.

All rights reserved.  See {file:LICENSE.rdoc} for details.

Public Class Methods

deep_diff(*hashes) click to toggle source

@api internal @param hashes [Array<Hash>] The hashes to be compared, with optional name tags @return [Hash] Generate a hash containing the differences between two hashes

(recursively descend parallel trees of hashes)

@see gist.github.com/146844

# File lib/serializer/serializable.rb, line 142
def self.deep_diff(*hashes)
  diff = {}
  case hashes.length
  when 4
    ltag, left, rtag, right = hashes
  when 2
    ltag = :left
    left = hashes[0]
    rtag = :right
    right = hashes[1]
  else
    raise ArgumentError, "wrong number of arguments (#{hashes.length} for 2 or 4)"
  end
  (left.keys | right.keys).each do |k|
    if left[k] != right[k]
      diff[k] = if left[k].is_a?(Hash) && right[k].is_a?(Hash)
                  deep_diff(ltag, left[k], rtag, right[k])
                else
                  { ltag => left[k], rtag => right[k] }
                end
    end
  end
  diff
end
new(opts = {}) click to toggle source

A flexible initializer based on the DataMapper “create factory” design pattern. @see datamapper.org/docs/create_and_destroy.html @see Serializable#initialize @param opts [Hash<Symbol,Object>] a hash containing any number of symbol => value pairs.

The symbols should correspond to attributes declared using HappyMapper syntax
# File lib/serializer/serializable.rb, line 22
def initialize(opts = {})
  opts.each do |key, value|
    errmsg = "#{key} is not a variable name in #{self.class.name}"
    raise(Moab::MoabRuntimeError, errmsg) unless variable_names.include?(key.to_s) || key == :test

    instance_variable_set("@#{key}", value)
  end
end

Public Instance Methods

array_to_hash(array, summary = false) click to toggle source

@api internal @param array [Array] The array to be converted to a hash @return [Hash] Generate a hash from an array of objects.

If the array member has a field tagged as a key, that field will be used as the hash.key.
Otherwise the index position of the array member will be used as the key
# File lib/serializer/serializable.rb, line 83
def array_to_hash(array, summary = false)
  item_hash = {}
  array.each_index do |index|
    item = array[index]
    ikey = item.respond_to?(:key) && item.key ? item.key : index
    item_hash[ikey] = item.respond_to?(:to_hash) ? item.to_hash(summary) : item
  end
  item_hash
end
diff(other) click to toggle source

@api internal @param other [Serializable] The other object being compared @return [Hash] Generate a hash containing the differences between two objects of the same type

# File lib/serializer/serializable.rb, line 122
def diff(other)
  raise(Moab::MoabRuntimeError, 'Cannot compare different classes') if self.class != other.class

  left = other.to_hash
  right = to_hash
  if key.nil? || other.key.nil?
    ltag = :old
    rtag = :new
  else
    ltag = other.key
    rtag = key
  end
  Serializable.deep_diff(ltag, left, rtag, right)
end
key() click to toggle source

@api internal @return [String] For the current object instance, return the string to use as a hash key

# File lib/serializer/serializable.rb, line 72
def key
  return send(key_name) if key_name

  nil
end
key_name() click to toggle source

@api internal @return [String] Determine which attribute was marked as an object instance key.

Keys are indicated by option :key=true when declaring the object's variables.
This follows the same convention as used by DataMapper

@see datamapper.org/docs/properties.html

# File lib/serializer/serializable.rb, line 57
def key_name
  unless defined?(@key_name)
    @key_name = nil
    self.class.attributes.each do |attribute|
      if attribute.options[:key]
        @key_name = attribute.name
        break
      end
    end
  end
  @key_name
end
summary() click to toggle source

@return [Hash] Calls to_hash(summary=true)

# File lib/serializer/serializable.rb, line 115
def summary
  to_hash(true)
end
to_hash(summary = false) click to toggle source

@api internal @return [Hash] Recursively generate an Hash containing the object’s properties @param summary [Boolean] Controls the depth and detail of recursion

# File lib/serializer/serializable.rb, line 96
def to_hash(summary = false)
  oh = {}
  vars = summary ? variables.select { |v| summary_fields.include?(v.name) } : variables
  vars.each do |variable|
    key = variable.name.to_s
    value = send(variable.name)
    oh[key] = case value
              when Array
                array_to_hash(value, summary)
              when Serializable
                value.to_hash
              else
                value
              end
  end
  oh
end
to_json(summary = false) click to toggle source

@api internal @return [String] Generate JSON output from a hash of the object’s variables

# File lib/serializer/serializable.rb, line 169
def to_json(summary = false)
  hash = to_hash(summary)
  JSON.pretty_generate(hash)
end
to_yaml(summary = false) click to toggle source

@api internal @return [String] Generate YAML output from a hash of the object’s variables

# File lib/serializer/serializable.rb, line 176
def to_yaml(summary = false)
  to_hash(summary).to_yaml
end
variable_names() click to toggle source

@api internal @return [Array] Extract the names of the variables

# File lib/serializer/serializable.rb, line 48
def variable_names
  variables.collect(&:name)
end
variables() click to toggle source

@api internal @return [Array] A list of HappyMapper xml attribute, element and text nodes declared for the class

# File lib/serializer/serializable.rb, line 33
def variables
  attributes = self.class.attributes
  elements   = self.class.elements
  attributes + elements
  # text_node enhancement added by unhappymapper, which is not being used
  # It enables elements having both attributes and a text value
  #text_node = []
  #if self.class.instance_variable_defined?("@text_node")
  #  text_node << self.class.instance_variable_get("@text_node")
  #end
  #attributes + elements + text_node
end