module Concerns module Dupable def duplicate # copy self with attributes only, includes belongs_to associations duplicate = self.dup # copy all has_many associations... # reject through target associations, we only want to duplicate the association models self.class.reflect_on_all_associations(:has_many).select { |a| a.through_reflection.nil? }.each do |assoc| duplicate.send(:"#{assoc.name}=", self.send(assoc.name.to_sym).map { |item| item = item.respond_to?(:duplicate) ? item.duplicate : item.dup # delete foreign_key item.send(:"#{assoc.foreign_key}=", nil) # populate inverse assoc if present if assoc.inverse_of.present? item.send(:"#{assoc.inverse_of.name}=", duplicate) end item }) end # copy all has_many through target by copying collection_ids... only needed to populate throught association targets and pass some children presence validations # rails is too dumb to inspect association models and deduce through association targets # self.class.reflect_on_all_associations(:has_many).select { |a| a.through_reflection.present? }.each do |assoc| duplicate.send(:"#{assoc.name.to_s.singularize}_ids=", self.send(:"#{assoc.name.to_s.singularize.to_sym}_ids")) end # copy all has_one associations self.class.reflect_on_all_associations(:has_one).each do |assoc| item = self.send(assoc.name.to_sym) duplicate.send(:"#{assoc.name}=", item.respond_to?(:duplicate) ? item.duplicate : item.dup) if !item.nil? end duplicate end end end