app/controllers/concerns/fae/cloneable.rb
module Fae
module Cloneable
extend ActiveSupport::Concern
private
def create_from_existing(id)
@item = @klass.find(id)
@cloned_item = @item.dup
update_cloned_attributes(@cloned_item)
@cloned_item.on_prod = false if @item.respond_to?(:on_prod)
if @cloned_item.save
update_cloneable_associations
redirect_to @index_path + '/' + @cloned_item.id.to_s + '/edit'
else
build_assets
flash[:alert] = @cloned_item.errors.full_messages
render action: 'edit'
end
end
# array of symbols used for setting up associations on cloned object
def associations_for_cloning
[]
end
# array of symbols used for cloning only specific attributes
def attributes_for_cloning
@klass.column_names
end
def update_cloned_attributes(item)
attribute_names = attributes_for_cloning.map(&:to_s) - ['id']
item.attributes.each do |attribute, value|
if attribute_names.include? attribute
rename_unique_attribute(item, attribute, value) if attr_is_unique?(item, attribute)
else
item.send("#{attribute}=", nil)
end
end
end
# set cloneable attributes and associations
def update_cloneable_associations
associations_for_cloning.each do |association|
type = @klass.reflect_on_association(association)
through_record = type.through_reflection
if through_record.present?
clone_join_relationships(through_record.plural_name)
else
clone_has_one_relationship(association,type) if type.macro == :has_one
clone_has_many_relationships(association) if type.macro == :has_many
end
end
end
def clone_has_one_relationship(association,type)
old_record = @item.send(association)
if old_record.present?
new_record = old_record.dup
if ['::Fae::Image','::Fae::File'].include?(type.options[:class_name])
new_record.send("#{type.options[:as]}_id" + '=', @cloned_item.id) if new_record.send("#{type.options[:as]}_id").present?
new_record.send("#{type.options[:as]}_type" + '=', @cloned_item.class.name) if new_record.send("#{type.options[:as]}_type").present?
Fae::AssetCloner.new(old_record, new_record, :asset).set_file if old_record.asset.present? && old_record.asset.url.present?
new_record.save
else
new_record.send("#{@klass_singular}_id" + '=', @cloned_item.id) if new_record.send("#{@klass_singular}_id").present?
end
new_record.attributes.each do |attribute, value|
rename_unique_attribute(new_record, attribute, value) if attr_is_unique?(new_record, attribute.first)
end
end
end
def clone_has_many_relationships(association)
if @item.send(association).present?
@item.send(association).reverse.each do |record|
new_record = association.to_s.classify.constantize.find_by_id(record.id).dup
new_record.send("#{@klass_singular}_id" + '=', @cloned_item.id) if new_record.send("#{@klass_singular}_id").present?
# check if associations have unique attributes
new_record.attributes.each do |attribute, value|
rename_unique_attribute(new_record, attribute, value) if attr_is_unique?(new_record, attribute.first)
end
@cloned_item.send(association) << new_record
end
end
end
def clone_join_relationships(object)
if @item.send(object.to_sym).present?
@item.send(object.to_sym).reverse.each do |record|
copied_join = object.classify.constantize.find_by_id(record.id).dup
copied_join.send("#{@klass_singular}_id" + '=', @cloned_item.id)
@cloned_item.send(object.to_sym) << copied_join
end
end
end
def rename_unique_attribute(item, attribute, value)
index = 2
symbol = attribute.to_sym
value = unique_name(item, attribute, value, index.to_s)
begin
record = item.class.where(symbol => value)
unless record.empty?
new_index = index + 1
value = value.chomp(index.to_s) + new_index.to_s
index = new_index
end
end while record.present?
item[symbol] = value
end
def attr_is_unique?(item, attribute)
item.class.validators_on(attribute.to_sym).map(&:class).include? ActiveRecord::Validations::UniquenessValidator
end
def unique_name(item, attribute, value, suffix)
item.class.validators_on(attribute.to_sym).each do |validator|
if validator.class.name.include?('LengthValidator') && validator.options[:maximum].present?
max_length = validator.options[:maximum] - (suffix.length + 1)
value = value[0...max_length]
break
end
end
"#{value}-#{suffix}"
end
end
end