|
require 'active_support/inflector'
|
|
|
|
module Frederick
|
|
module Models
|
|
class Model
|
|
def initialize(options = {})
|
|
options.each do |key, value|
|
|
send(:"#{key}=", value)
|
|
end
|
|
end
|
|
|
|
def to_hash
|
|
hash = {}
|
|
self.instance_variables.each do |var|
|
|
value = self.instance_variable_get var
|
|
if value.is_a? Array
|
|
new_value = hash_list(value)
|
|
elsif value.is_a? Frederick::Models::Model
|
|
new_value = value.to_hash
|
|
else
|
|
new_value = value
|
|
end
|
|
hash[var[1..-1]] = new_value
|
|
end
|
|
hash
|
|
end
|
|
|
|
def to_json
|
|
Oj.dump(to_hash, mode: :compat)
|
|
end
|
|
|
|
def attr_values
|
|
attr_values = {}
|
|
self.class::ATTRS.each do |name|
|
|
instance_var_value = instance_variable_get("@#{name}")
|
|
attr_values[name] = instance_var_value unless instance_var_value.nil?
|
|
end
|
|
attr_values
|
|
end
|
|
|
|
# Built in Hash#hash method returns diff code across ruby instances
|
|
# See: http://stackoverflow.com/questions/6783811/why-is-ruby-string-hash-inconsistent-across-machines
|
|
# Instead use MD5 to ensure hashes for objects with same state (instance_values) are always the same
|
|
# Use MD5 hashing algorithm over SHA because it is faster
|
|
# Chance of collision using MD5 is still infinitesimally small (1/2^128)
|
|
|
|
# WARNING! CHANGING ORDER OF WHEN YOUR INSTANCE VARS ARE DEFINED
|
|
# OR CHANGING WHICH INSTANCE VARS ARE SET PER INSTANCE
|
|
# WILL RETURN NEW HASH CODES
|
|
# AS THIS WILL CHANGE THE INSTANCE VALUES STRING THAT IS PASSED INTO THE DIGEST ROUTINE
|
|
def hash; Digest::MD5.hexdigest(attr_values.to_s); end
|
|
|
|
def self.hash_codes_with_index(models)
|
|
hash_code_map = {}
|
|
models.each_with_index { |model, i| hash_code_map[model.hash] = i }
|
|
hash_code_map
|
|
end
|
|
|
|
def self.from_hash(hash)
|
|
model = self.new
|
|
hash.each do |key, value|
|
|
if model.respond_to?(:"#{key}")
|
|
constantized = self.constantize(key)
|
|
if constantized
|
|
if value.is_a? Array
|
|
model.send(:"#{key}=", constantized.from_list(value))
|
|
else
|
|
model.send(:"#{key}=", constantized.from_hash(value))
|
|
end
|
|
else
|
|
model.send(:"#{key}=", value)
|
|
end
|
|
end
|
|
end
|
|
model
|
|
end
|
|
|
|
def self.from_list(array)
|
|
list = []
|
|
array.each do |item|
|
|
model = self.from_hash(item)
|
|
list << model
|
|
end
|
|
list
|
|
end
|
|
|
|
def self.constantize(key)
|
|
begin
|
|
Frederick::Models.const_get("Frederick::Models::#{ActiveSupport::Inflector.camelize(ActiveSupport::Inflector.singularize("#{key}"))}")
|
|
rescue NameError
|
|
nil
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def hash_list(array)
|
|
list = []
|
|
array.each do |item|
|
|
if item.is_a? Array
|
|
list << hash_list(item)
|
|
elsif item.is_a? Frederick::Models::Model
|
|
list << item.to_hash
|
|
else
|
|
list << item
|
|
end
|
|
end
|
|
list
|
|
end
|
|
end
|
|
end
|
|
end
|