# 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) if first.class.ancestors.include?(ActiveRecord::Base) && first.persisted? @first = first end if @first && (second.class == @first.class) && second.persisted? @second = second end end attr_reader :first, :second validates :second, presence: true def provide(options = {}) transaction do Links.new(second).each { |item, key| rebind(item, key, options) } remove(options) end end private # Initializes a hash, whose keys are instances linked to given one, # and their values are names of methods for linking to another object. class Links class << self def new(item) @item = item return hash end # Returns a hash whose keys are "has_many" associations' names # and their values are corresponding foreign keys def refs @item.class.reflect_on_all_associations(:has_many). inject({}){ |hash, item| hash.merge(item.name => item.foreign_key) } end def hash @hash = {} refs.each do |name, foreign_key| @item.send(name).each{ |item| @hash[item] = "#{ foreign_key }=" } end @hash end end end # Deletes or destroys second object def remove(validate: true) change(second) { validate ? second.destroy! : second.delete } end # Re-assigns given object to the #first def rebind(item, key, options) change item do item.send key, first.id item.save! options end end end end