# encoding: UTF-8 # Copyright 2011-2013 innoQ Deutschland GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'active_support/concern' require 'active_record/model_schema' module DeepCloning extend ActiveSupport::Concern extend ActiveRecord::ModelSchema included do #:nodoc: alias_method_chain :dup, :deep_cloning end # clones an ActiveRecord model. # if passed the :include option, it will deep clone the given associations # if passed the :except option, it won't clone the given attributes # # === Usage: # # ==== Cloning a model without an attribute # pirate.clone except: :name # # ==== Cloning a model without multiple attributes # pirate.clone except: [:name, :nick_name] # ==== Cloning one single association # pirate.clone include: :mateys # # ==== Cloning multiple associations # pirate.clone include: [:mateys, :treasures] # # ==== Cloning really deep # pirate.clone include: {treasures: :gold_pieces} # # ==== Cloning really deep with multiple associations # pirate.clone include: [:mateys, {treasures: :gold_pieces}] # # ==== Cloning multiple associations - but only the join table entries without cloning the associated objects themselves # pirate.clone include_association: [:matey_ids, :treasure_ids] # def dup_with_deep_cloning(options = {}) kopy = dup_without_deep_cloning if options[:except] Array(options[:except]).each do |attribute| kopy.send("#{attribute}=", kopy.class.column_defaults[attribute.to_s]) end end if options[:include_association] Array(options[:include_association]).each do |association_attribute| kopy.send("#{association_attribute}=", self.send("#{association_attribute}")) end end if options[:include] Array(options[:include]).each do |association, deep_associations| if (association.kind_of? Hash) deep_associations = association[association.keys.first] association = association.keys.first end opts = deep_associations.blank? ? {} : { include: deep_associations } association_reflection = self.class.reflect_on_association(association) cloned_object = case association_reflection.macro when :belongs_to, :has_one self.send(association) && self.send(association).dup(opts) when :has_many, :has_and_belongs_to_many fk = association_reflection.options[:foreign_key]# || self.class.to_s.underscore self.send(association).collect do |obj| cloned_obj = obj.dup(opts) cloned_obj.send("#{fk}=", kopy.id) unless fk.blank? cloned_obj end end kopy.send("#{association}=", cloned_object) end end return kopy end end