lib/undo/serializer/active_model.rb in undo-serializer-active_model-0.0.4 vs lib/undo/serializer/active_model.rb in undo-serializer-active_model-0.1.0

- old
+ new

@@ -1,66 +1,125 @@ -require "active_support" - module Undo module Serializer class ActiveModel - def initialize(*args) - options = args.extract_options! - @serializer = args.first - @serializer_source = options.fetch :serializer, - ->(object) { object.active_model_serializer.new object } + def initialize(options = {}) + load_options options end - def serialize(object) - serializer(object).as_json + def serialize(object, options = {}) + return object.map do |record| + serialize record, options + end if array? object + + load_options options + + attributes = serialize_attributes(object) || {} + associations = {} + Array(options[:include]).map do |association| + associations[association] = serialize(object.public_send association) + end + pk_attributes = symbolize_keys(attributes).select do |attribute| + primary_key_fields.include? attribute + end + + { + attributes: attributes, + associations: associations, + meta: { + pk_attributes: pk_attributes, + class_name: object.class.name, + } + } end - def deserialize(hash) - object_handler, data = hash.first - return unless data.is_a? Hash - data.stringify_keys! + def deserialize(object, options = {}) + return object.map do |record| + deserialize record + end if array? object - ActiveRecord::Base.transaction do - initialize_object(object_handler, data).tap do |object| - data.each do |field, value| - next if "id" == field && object.persisted? + load_options options - case field - when /___(.*)/ then deserialize_association object, $1, value - else deserialize_field object, field, value - end + hash = symbolize_keys object + object_meta = hash.fetch :meta + associations = hash.fetch :associations + attributes = hash.fetch :attributes + + with_transaction do + initialize_object(object_meta).tap do |object| + attributes.each do |field, value| + deserialize_field object, field, value end - object.save! + # QUESTION: Set associations? object.association_name = deserialize association ? + associations.each do |(association_name, association)| + deserialize association + end + + persist object end end - end private - attr_reader :serializer_source + attr_reader :serialize_attributes_source, + :initialize_object_source, + :persist_object_source - def serializer(object) - @serializer ||= serializer_source.call object + def deserialize_field(object, field, value) + object.send "#{field}=", value # not public_send! end - def deserialize_association(object, association, values) - Array.wrap(values).each do |value| - deserialize object.public_send(association) => value + def initialize_object(meta) + object_class = constantize meta.fetch(:class_name) + pk_attributes = meta.fetch :pk_attributes + + find_or_initialize object_class, pk_attributes + end + + def with_transaction(&block) + if defined? ActiveRecord + ActiveRecord::Base.transaction(&block) + else + block.call end end - def deserialize_field(object, field, value) - object.send "#{field}=", value # not public_send! + def load_options(options) + @serialize_attributes_source = options.fetch :serialize_attributes, @serialize_attributes_source || + ->(object) { object.attributes } + + @initialize_object_source = options.fetch :find_or_initialize, @initialize_object_source || + ->(object_class, pk_query) { object_class.respond_to?(:where) && object_class.where(pk_query).first || object_class.new(pk_query) } + + @persist_object_source = options.fetch :persist, @persist_object_source || + ->(object) { object.respond_to?(:save!) && object.save! } + + @primary_key_fields = options.fetch :primary_key, @primary_key_fields || :id end - def initialize_object(object_handler, data) - id = data.fetch "id" - relation = case object_handler - when String, Symbol then object_handler.to_s.camelize.constantize - else object_handler end + def primary_key_fields + Array(@primary_key_fields) + end - relation.where(id: id).first || relation.new(id: id) + def serialize_attributes(*args); serialize_attributes_source.call(*args) end + def find_or_initialize(*args); initialize_object_source.call(*args) end + def persist(*args); persist_object_source.call(*args) end + + def array?(object) + object.respond_to?(:map) && ! object.is_a?(Hash) + end + # ActiveSupport methods + def symbolize_keys(hash) + hash.each_with_object({}) do |(key, value), result| + new_key = key.is_a?(String) ? key.to_sym : key + new_value = value.is_a?(Hash) ? symbolize_keys(value) : value + + result[new_key] = new_value + end + end + + def constantize(class_name) + class_name.split('::').inject(Kernel) { |object, name| object = object.const_get(name); object } end end end end