class ARMS::StructCoder
this is a ActiveRecord
serialization class intended to serialize from a Struct class on the loaded ruby side to something JSON-compatible on the dumped database side.
This coder relies on ‘loaded_class`, the Struct class which will be used to instantiate the column data. properties (members) of the loaded class will correspond to keys of the dumped json object.
the data may be either a single instance of the loaded class (serialized as one hash) or an array of them (serialized as an array of hashes), indicated by the boolean keyword argument ‘array`.
the column behind the attribute may be an actual JSON column (postgres json or jsonb - hstore should work too if you only have string attributes) or may be a string column with a string serializer after StructCoder
.
Public Class Methods
@param loaded_class [Class] the Struct class to load @param array [Boolean] whether the column holds an array of Struct instances instead of just one
# File lib/arms/struct_coder.rb, line 19 def initialize(loaded_class, array: false) @loaded_class = loaded_class # this notes the order of the keys as they were in the json, used by dump_object to generate # json that is equivalent to the json/jsonifiable that came in, so that AR's #changed_attributes # can tell whether the attribute has been changed. @loaded_class.send(:attr_accessor, :arms_object_json_coder_keys_order) @array = array end
Public Instance Methods
@param object [loaded_class, Array] @return [Hash, Array<Hash>]
# File lib/arms/struct_coder.rb, line 45 def dump(object) return nil if object.nil? jsonifiable = begin if @array unless object.respond_to?(:to_ary) raise DumpError, "expected array-like attribute; got: #{object.class}: #{object.inspect}" end object.map do |el| dump_object(el) end else dump_object(object) end end jsonifiable end
@param data [Hash, Array<Hash>] @return [loaded_class, Array]
# File lib/arms/struct_coder.rb, line 30 def load(data) return nil if data.nil? object = if @array unless data.respond_to?(:to_ary) raise LoadError, "expected array-like column data; got: #{data.class}: #{data.inspect}" end data.map { |el| load_object(el) } else load_object(data) end object end
Private Instance Methods
@param object [loaded_class] @return [Hash]
# File lib/arms/struct_coder.rb, line 84 def dump_object(object) if object.is_a?(@loaded_class) keys = (object.arms_object_json_coder_keys_order || []) | @loaded_class.members.map(&:to_s) keys.map { |member| {member => object[member]} }.inject({}, &:update) else raise TypeError, "expected instance(s) of #{@loaded_class}; got: #{object.class}: #{object.inspect}" end end
@param data [Hash] @return [loaded_class]
# File lib/arms/struct_coder.rb, line 66 def load_object(data) if data.respond_to?(:to_hash) data = data.to_hash good_keys = @loaded_class.members.map(&:to_s) bad_keys = data.keys - good_keys unless bad_keys.empty? raise LoadError, "expected keys #{good_keys}; got unrecognized keys: #{bad_keys}" end instance = @loaded_class.new(*@loaded_class.members.map { |m| data[m.to_s] }) instance.arms_object_json_coder_keys_order = data.keys instance else raise LoadError, "expected instance(s) of #{Hash}; got: #{data.class}: #{data.inspect}" end end