# encoding: utf-8 module ActiveMerge # Service Object responds for merging two ActiveRecord instances as a whole # transaction. # # All objects linked to the second instance via "has_many" association # are re-associated to the first one. # # Then the second instance removed. # All errors collected in the #errors method. # class SimpleService < ActivePatterns::BaseService include ActiveModel::Validations def initialize(first, second) @klass = first.class return unless klass.ancestors.include?(ActiveRecord::Base) && second.is_a?(klass) && first.persisted? && second.persisted? @first, @second = first, second @links = find_links end attr_reader :klass, :first, :second, :links validates :first, :second, presence: true def provide(validate: true) transaction do remove_second! validate save_links! validate end end private # Finds links to the second record and rebinds it to the first one. # Doesn't validate and save links yet! def find_links return [] unless first && second klass.reflect_on_all_associations(:has_many).inject([]) do |arr, item| arr + second.send(item.name).map do |link| link.send "#{ item.foreign_key }=", first.id link end end end # Removes the second record. # Any errors are moved to the service. def remove_second!(validate) begin validate ? second.destroy! : second.delete rescue => error second.errors.each{ |_, message| errors.add :second, message } raise error end end # Saves changed links. # Any errors are moved to the service. def save_links!(validate) links.each do |link| begin validate ? link.save! : link.save(validate: false) rescue => error link.errors.each{ |_, message| errors.add :base, message } raise error end end end end end