130 lines
3.0 KiB
Ruby
130 lines
3.0 KiB
Ruby
require 'date'
|
|
require 'time'
|
|
|
|
module Sawyer
|
|
class Serializer
|
|
def self.any_json
|
|
yajl || multi_json || json || begin
|
|
raise RuntimeError, "Sawyer requires a JSON gem: yajl, multi_json, or json"
|
|
end
|
|
end
|
|
|
|
def self.yajl
|
|
require 'yajl'
|
|
new(Yajl)
|
|
rescue LoadError
|
|
end
|
|
|
|
def self.json
|
|
require 'json'
|
|
new(JSON)
|
|
rescue LoadError
|
|
end
|
|
|
|
def self.multi_json
|
|
require 'multi_json'
|
|
new(MultiJson)
|
|
rescue LoadError
|
|
end
|
|
|
|
def self.message_pack
|
|
require 'msgpack'
|
|
new(MessagePack, :pack, :unpack)
|
|
rescue LoadError
|
|
end
|
|
|
|
# Public: Wraps a serialization format for Sawyer. Nested objects are
|
|
# prepared for serialization (such as changing Times to ISO 8601 Strings).
|
|
# Any serialization format that responds to #dump and #load will work.
|
|
def initialize(format, dump_method_name = nil, load_method_name = nil)
|
|
@format = format
|
|
@dump = @format.method(dump_method_name || :dump)
|
|
@load = @format.method(load_method_name || :load)
|
|
end
|
|
|
|
# Public: Encodes an Object (usually a Hash or Array of Hashes).
|
|
#
|
|
# data - Object to be encoded.
|
|
#
|
|
# Returns an encoded String.
|
|
def encode(data)
|
|
@dump.call(encode_object(data))
|
|
end
|
|
|
|
alias dump encode
|
|
|
|
# Public: Decodes a String into an Object (usually a Hash or Array of
|
|
# Hashes).
|
|
#
|
|
# data - An encoded String.
|
|
#
|
|
# Returns a decoded Object.
|
|
def decode(data)
|
|
return nil if data.nil? || data.strip.empty?
|
|
decode_object(@load.call(data))
|
|
end
|
|
|
|
alias load decode
|
|
|
|
def encode_object(data)
|
|
case data
|
|
when Hash then encode_hash(data)
|
|
when Array then data.map { |o| encode_object(o) }
|
|
else data
|
|
end
|
|
end
|
|
|
|
def encode_hash(hash)
|
|
hash.keys.each do |key|
|
|
case value = hash[key]
|
|
when Date then hash[key] = value.to_time.utc.xmlschema
|
|
when Time then hash[key] = value.utc.xmlschema
|
|
when Hash then hash[key] = encode_hash(value)
|
|
end
|
|
end
|
|
hash
|
|
end
|
|
|
|
def decode_object(data)
|
|
case data
|
|
when Hash then decode_hash(data)
|
|
when Array then data.map { |o| decode_object(o) }
|
|
else data
|
|
end
|
|
end
|
|
|
|
def decode_hash(hash)
|
|
hash.keys.each do |key|
|
|
hash[key.to_sym] = decode_hash_value(key, hash.delete(key))
|
|
end
|
|
hash
|
|
end
|
|
|
|
def decode_hash_value(key, value)
|
|
if time_field?(key, value)
|
|
if value.is_a?(String)
|
|
begin
|
|
Time.parse(value)
|
|
rescue ArgumentError
|
|
value
|
|
end
|
|
elsif value.is_a?(Integer) || value.is_a?(Float)
|
|
Time.at(value)
|
|
else
|
|
value
|
|
end
|
|
elsif value.is_a?(Hash)
|
|
decode_hash(value)
|
|
elsif value.is_a?(Array)
|
|
value.map { |o| decode_hash_value(key, o) }
|
|
else
|
|
value
|
|
end
|
|
end
|
|
|
|
def time_field?(key, value)
|
|
value && (key =~ /_(at|on)\z/ || key =~ /(\A|_)date\z/)
|
|
end
|
|
end
|
|
end
|