Skip to content

Instantly share code, notes, and snippets.

@pmn4
Last active August 29, 2015 14:13
Show Gist options
  • Save pmn4/9fb598e4f4a8313c0155 to your computer and use it in GitHub Desktop.
Save pmn4/9fb598e4f4a8313c0155 to your computer and use it in GitHub Desktop.
Ruby Classes as Hash Wrappers (for faster serialization)
#!/usr/bin/env ruby
# More often than not at @RTR_tech, our ruby layer is a pass-through from our
# services to our client application. In those cases, it doesn't make much
# sense to parse the JSON string.
#
# Other times, however, we want to work with the objects that come back, so
# like good ruby developers, we create classes which reflect the data we
# expect, but as an intermittant step, we convert the JSON to a Hash, which
# we convert to an object.
#
# The latter case can get expensive, _especially_ when we don't even use the
# object that we create.
#
# With these two considerations in mind, consider the following code which
# lazily parses a JSON string iff some other ruby code needs it. Additionally,
# it only calls #to_json if data has changed, otherwise it simply returns the
# original JSON string.
#
# let me know what you think!
require 'json'
class ServiceResponseObject
# Internals:
# attr_accessor :json, :hash
def initialize(json)
# Store the json string, to be returned via #to_json if nothing changes
@__json = json
end
def as_json(*args)
# ActiveModel? great! as_json ftw
__hash.as_json(*args) if __hash.respond_to?(:as_json)
__hash
end
def to_json(*args)
# Unless the internally stored json string has been wiped out, return it
return @__json unless @__json.nil?
# re-jsonify self
@__json = as_json(*args).to_json
end
class << self
# Defines getters and setters on your class
# @param [symbol] accessor - accessor methods name
# @param [string] key - hash key where data is stored for this accessor
def field(accessor, key)
# `field :abc, 'xyz'` yields the inline comments below
class_eval %Q{
def #{accessor} # def abc
__hash['#{key}'] # __hash['xyz']
end # end
def #{accessor}=(val) # def abc=(val)
@__json = nil unless __hash['#{key}'] == val # @__json = nil unless __hash['xyz'] == val
__hash['#{key}'] = val # __hash['xyz'] = val
end # end
}
end
end
private
# Lazy-load the internal Hash
def __hash
@__hash ||= JSON.parse(@__json)
end
end
@pmn4
Copy link
Author

pmn4 commented Jan 13, 2015

# ==================================
#         An Example Usage
# ==================================

class Item < ServiceResponseObject
  # field :accessor_method, 'JSON-Key'
  field :abc_def, 'abcDef'
  field :ghi_jkl, 'ghiJkl'
end

i = Item.new({'abcDef' => 123, 'ghiJkl' => 456}.to_json)
# no parsing has taken place yet.

"abc_def: #{i.abc_def}"
# parsing just took place

"ghi_jkl: #{i.ghi_jkl}"
# no additional parsing happens after the first one

# this will return the original JSON string
i.to_json

# modifying the object resets the internally-stored JSON string
i.abc_def = 999

# calling to_json will re-jsonify the object when it has been modified
i.to_json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment