ActiveRecord: How can I clone nested associations? - ruby-on-rails

I'm currently cloning a single-level association like this:
class Survey < ActiveRecord::Base
def duplicate
new_template = self.clone
new_template.questions << self.questions.collect { |question| question.clone }
new_template.save
end
end
So that clones the Survey then clones the Questions associated with that survey. Fine. That works quite well.
But what I'm having trouble with is that each question has_many Answers. So Survey has_many Questions which has_many Answers.
I can't figure out how to clone the answers properly. I've tried this:
def duplicate
new_template = self.clone
self.questions.each do |question|
new_question = question.clone
new_question.save
question.answers.each do |answer|
new_answer = answer.clone
new_answer.save
new_question.answers << answer
end
new_template.questions << question
end
new_template.save
end
But that does some weird stuff with actually replacing the original answers then creating new ones, so ID's stop matching correctly.

Use deep_clonable gem
new_survey = original_survey.clone :include => [:questions => :answers]

You may also like the Amoeba gem for ActiveRecord 3.2.
In your case, you probably want to make use of the nullify, regex or prefix options available in the configuration DSL.
It supports easy and automatic recursive duplication of has_one, has_many and has_and_belongs_to_many associations, field preprocessing and a highly flexible and powerful configuration DSL that can be applied both to the model and on the fly.
be sure to check out the Amoeba Documentation but usage is pretty easy...
just
gem install amoeba
or add
gem 'amoeba'
to your Gemfile
then add the amoeba block to your model and run the dup method as usual
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
enable
end
end
class Comment < ActiveRecord::Base
belongs_to :post
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :posts
end
class PostsController < ActionController
def some_method
my_post = Post.find(params[:id])
new_post = my_post.dup
new_post.save
end
end
You can also control which fields get copied in numerous ways, but for example, if you wanted to prevent comments from being duplicated but you wanted to maintain the same tags, you could do something like this:
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
exclude_field :comments
end
end
You can also preprocess fields to help indicate uniqueness with both prefixes and suffixes as well as regexes. In addition, there are also numerous options so you can write in the most readable style for your purpose:
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
include_field :tags
prepend :title => "Copy of "
append :contents => " (copied version)"
regex :contents => {:replace => /dog/, :with => "cat"}
end
end
Recursive copying of associations is easy, just enable amoeba on child models as well
class Post < ActiveRecord::Base
has_many :comments
amoeba do
enable
end
end
class Comment < ActiveRecord::Base
belongs_to :post
has_many :ratings
amoeba do
enable
end
end
class Rating < ActiveRecord::Base
belongs_to :comment
end
The configuration DSL has yet more options, so be sure to check out the documentation.
Enjoy! :)

Without using gems, you can do the following:
class Survey < ApplicationRecord
has_and_belongs_to_many :questions
def copy_from(last_survey)
last_survery.questions.each do |question|
new_question = question.dup
new_question.save
questions << new_question
end
save
end
…
end
Then you can call:
new_survey = Survey.create
new_survey.copy_from(past_survey)
That will duplicate all questions from last Survey to new Survey and tie them.

Shouldn't it be..
new_question.answers << new_answer
end
new_template.questions << new_question

You can also alias the rails dup method, as follows:
class Survey
has_many :questions, :inverse_of=>:survey, :autosave=>true
alias orig_dup dup
def dup
copy=orig_dup
copy.questions=questions
copy
end
end
class Questions
belongs_to :survey, :inverse_of=>:questions
has_many :answers, :inverse_of=>:question, :autosave=>true
alias orig_dup dup
def dup
copy=orig_dup
copy.answers=answers
copy
end
end
class Answer
belongs_to :question
end
and then you can do this
aaa = Survey.find(123).dup
aaa.save

Related

Rails has_many STI with sub STI

I think it is more of a "Model Design" issue than a rails issue.
For clarity sake here is the business logic: I've Venues and I want to implement multiple APIs to get data about those venues. All this APIs have a lot in common, therefore I used STI.
# /app/models/venue.rb
class Venue < ApplicationRecord
has_one :google_api
has_one :other_api
has_many :apis
end
# /app/models/api.rb
class Api < ApplicationRecord
belongs_to :venue
end
# /app/models/google_api.rb
class GoogleApi < Api
def find_venue_reference
# ...
end
def synch_data
# ...
end
end
# /app/models/other_api.rb
class OtherApi < Api
def find_venue_reference
# ...
end
def synch_data
# ...
end
end
That part works, now what I'm trying to add is Photos to the venue. I will be fetching those photos from the API and I realise that every API might be different. I thought about using STI for that as well and I will end up with something like that
# /app/models/api_photo.rb
class ApiPhoto < ApplicationRecord
belongs_to :api
end
# /app/models/google_api_photo.rb
class GoogleApiPhoto < ApiPhoto
def url
"www.google.com/#{reference}"
end
end
# /app/models/other_api_photo.rb
class OtherApiPhoto < ApiPhoto
def url
self[url] || nil
end
end
My goal being to have this at the end
# /app/models/venue.rb
class Venue < ApplicationRecord
has_one :google_api
has_one :other_api
has_many :apis
has_many :photos :through => :apis
end
# /app/views/venues/show.html.erb
<%# ... %>
#venue.photos.each do |photo|
photo.url
end
<%# ... %>
And photo.url will give me the right formatting that is dependent of the api it is.
As I'm going deeper in the integration, something seems not right. If I had to Api the has_many :google_api_photo then every Api will have GoogleApiPhoto. What does not make sense to me.
Any idea how I should proceed from here?
I think I solved it.
By adding this to venue.rb
has_many :apis, :dependent => :destroy
has_many :photos, :through => :apis, :source => :api_photos
By calling venue.photos[0].url call the right Class based on the type field of the ApiPhoto

What is the best method to create a copy of an item in Ruby? [duplicate]

I want to make a copy of an ActiveRecord object, changing a single field in the process (in addition to the id). What is the simplest way to accomplish this?
I realize I could create a new record, and then iterate over each of the fields copying the data field-by-field - but I figured there must be an easier way to do this.
Perhaps something like this:
new_record = Record.copy(:id)
To get a copy, use the dup (or clone for < rails 3.1+) method:
#rails >= 3.1
new_record = old_record.dup
# rails < 3.1
new_record = old_record.clone
Then you can change whichever fields you want.
ActiveRecord overrides the built-in Object#clone to give you a new (not saved to the DB) record with an unassigned ID.
Note that it does not copy associations, so you'll have to do this manually if you need to.
Rails 3.1 clone is a shallow copy, use dup instead...
Depending on your needs and programming style, you can also use a combination of the new method of the class and merge. For lack of a better simple example, suppose you have a task scheduled for a certain date and you want to duplicate it to another date. The actual attributes of the task aren't important, so:
old_task = Task.find(task_id)
new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))
will create a new task with :id => nil, :scheduled_on => some_new_date, and all other attributes the same as the original task. Using Task.new, you will have to explicitly call save, so if you want it saved automatically, change Task.new to Task.create.
Peace.
You may also like the Amoeba gem for ActiveRecord 3.2.
In your case, you probably want to make use of the nullify, regex or prefix options available in the configuration DSL.
It supports easy and automatic recursive duplication of has_one, has_many and has_and_belongs_to_many associations, field preprocessing and a highly flexible and powerful configuration DSL that can be applied both to the model and on the fly.
be sure to check out the Amoeba Documentation but usage is pretty easy...
just
gem install amoeba
or add
gem 'amoeba'
to your Gemfile
then add the amoeba block to your model and run the dup method as usual
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
enable
end
end
class Comment < ActiveRecord::Base
belongs_to :post
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :posts
end
class PostsController < ActionController
def some_method
my_post = Post.find(params[:id])
new_post = my_post.dup
new_post.save
end
end
You can also control which fields get copied in numerous ways, but for example, if you wanted to prevent comments from being duplicated but you wanted to maintain the same tags, you could do something like this:
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
exclude_field :comments
end
end
You can also preprocess fields to help indicate uniqueness with both prefixes and suffixes as well as regexes. In addition, there are also numerous options so you can write in the most readable style for your purpose:
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
include_field :tags
prepend :title => "Copy of "
append :contents => " (copied version)"
regex :contents => {:replace => /dog/, :with => "cat"}
end
end
Recursive copying of associations is easy, just enable amoeba on child models as well
class Post < ActiveRecord::Base
has_many :comments
amoeba do
enable
end
end
class Comment < ActiveRecord::Base
belongs_to :post
has_many :ratings
amoeba do
enable
end
end
class Rating < ActiveRecord::Base
belongs_to :comment
end
The configuration DSL has yet more options, so be sure to check out the documentation.
Enjoy! :)
Use ActiveRecord::Base#dup if you don't want to copy the id
I usually just copy the attributes, changing whatever I need changing:
new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
If you need a deep copy with associations, I recommend the deep_cloneable gem.
In Rails 5 you can simply create duplicate object or record like this.
new_user = old_user.dup
Here is a sample of overriding ActiveRecord #dup method to customize instance duplication and include relation duplication as well:
class Offer < ApplicationRecord
has_many :offer_items
def dup
super.tap do |new_offer|
# change title of the new instance
new_offer.title = "Copy of #{#offer.title}"
# duplicate offer_items as well
self.offer_items.each { |offer_item| new_offer.offer_items << offer_item.dup }
end
end
end
Note: this method doesn't require any external gem but it requires newer ActiveRecord version with #dup method implemented
The easily way is:
#your rails >= 3.1 (i was done it with Rails 5.0.0.1)
o = Model.find(id)
# (Range).each do |item|
(1..109).each do |item|
new_record = o.dup
new_record.save
end
Or
# if your rails < 3.1
o = Model.find(id)
(1..109).each do |item|
new_record = o.clone
new_record.save
end
You can also check the acts_as_inheritable gem.
"Acts As Inheritable is a Ruby Gem specifically written for Rails/ActiveRecord models. It is meant to be used with the Self-Referential Association, or with a model having a parent that share the inheritable attributes. This will let you inherit any attribute or relation from the parent model."
By adding acts_as_inheritable to your models you will have access to these methods:
inherit_attributes
class Person < ActiveRecord::Base
acts_as_inheritable attributes: %w(favorite_color last_name soccer_team)
# Associations
belongs_to :parent, class_name: 'Person'
has_many :children, class_name: 'Person', foreign_key: :parent_id
end
parent = Person.create(last_name: 'Arango', soccer_team: 'Verdolaga', favorite_color:'Green')
son = Person.create(parent: parent)
son.inherit_attributes
son.last_name # => Arango
son.soccer_team # => Verdolaga
son.favorite_color # => Green
inherit_relations
class Person < ActiveRecord::Base
acts_as_inheritable associations: %w(pet)
# Associations
has_one :pet
end
parent = Person.create(last_name: 'Arango')
parent_pet = Pet.create(person: parent, name: 'Mango', breed:'Golden Retriver')
parent_pet.inspect #=> #<Pet id: 1, person_id: 1, name: "Mango", breed: "Golden Retriver">
son = Person.create(parent: parent)
son.inherit_relations
son.pet.inspect # => #<Pet id: 2, person_id: 2, name: "Mango", breed: "Golden Retriver">
Hope this can help you.
Since there could be more logic, when duplicating a model, I would suggest to create a new class, where you handle all the needed logic.
To ease that, there's a gem that can help: clowne
As per their documentation examples, for a User model:
class User < ActiveRecord::Base
# create_table :users do |t|
# t.string :login
# t.string :email
# t.timestamps null: false
# end
has_one :profile
has_many :posts
end
You create your cloner class:
class UserCloner < Clowne::Cloner
adapter :active_record
include_association :profile, clone_with: SpecialProfileCloner
include_association :posts
nullify :login
# params here is an arbitrary Hash passed into cloner
finalize do |_source, record, params|
record.email = params[:email]
end
end
class SpecialProfileCloner < Clowne::Cloner
adapter :active_record
nullify :name
end
and then use it:
user = User.last
#=> <#User(login: 'clown', email: 'clown#circus.example.com')>
cloned = UserCloner.call(user, email: 'fake#example.com')
cloned.persisted?
# => false
cloned.save!
cloned.login
# => nil
cloned.email
# => "fake#example.com"
# associations:
cloned.posts.count == user.posts.count
# => true
cloned.profile.name
# => nil
Example copied from the project, but it will give a clear vision of what you can achieve.
For a quick and simple record I would go with:
Model.new(Model.last.attributes.reject {|k,_v| k.to_s == 'id'}
Try rails's dup method:
new_record = old_record.dup.save

Rails: includes with polymorphic association

I read this interesting article about Using Polymorphism to Make a Better Activity Feed in Rails.
We end up with something like
class Activity < ActiveRecord::Base
belongs_to :subject, polymorphic: true
end
Now, if two of those subjects are for example:
class Event < ActiveRecord::Base
has_many :guests
after_create :create_activities
has_one :activity, as: :subject, dependent: :destroy
end
class Image < ActiveRecord::Base
has_many :tags
after_create :create_activities
has_one :activity, as: :subject, dependent: :destroy
end
With create_activities defined as
def create_activities
Activity.create(subject: self)
end
And with guests and tags defined as:
class Guest < ActiveRecord::Base
belongs_to :event
end
class Tag < ActiveRecord::Base
belongs_to :image
end
If we query the last 20 activities logged, we can do:
Activity.order(created_at: :desc).limit(20)
We have a first N+1 query issue that we can solve with:
Activity.includes(:subject).order(created_at: :desc).limit(20)
But then, when we call guests or tags, we have another N+1 query problem.
What's the proper way to solve that in order to be able to use pagination ?
Edit 2: I'm now using rails 4.2 and eager loading polymorphism is now a feature :)
Edit: This seemed to work in the console, but for some reason, my suggestion of use with the partials below still generates N+1 Query Stack warnings with the bullet gem. I need to investigate...
Ok, I found the solution ([edit] or did I ?), but it assumes that you know all subjects types.
class Activity < ActiveRecord::Base
belongs_to :subject, polymorphic: true
belongs_to :event, -> { includes(:activities).where(activities: { subject_type: 'Event' }) }, foreign_key: :subject_id
belongs_to :image, -> { includes(:activities).where(activities: { subject_type: 'Image' }) }, foreign_key: :subject_id
end
And now you can do
Activity.includes(:part, event: :guests, image: :tags).order(created_at: :desc).limit(10)
But for eager loading to work, you must use for example
activity.event.guests.first
and not
activity.part.guests.first
So you can probably define a method to use instead of subject
def eager_loaded_subject
public_send(subject.class.to_s.underscore)
end
So now you can have a view with
render partial: :subject, collection: activity
A partial with
# _activity.html.erb
render :partial => 'activities/' + activity.subject_type.underscore, object: activity.eager_loaded_subject
And two (dummy) partials
# _event.html.erb
<p><%= event.guests.map(&:name).join(', ') %></p>
# _image.html.erb
<p><%= image.tags.first.map(&:name).join(', ') %></p>
This will hopefully be fixed in rails 5.0. There is already an issue and a pull request for it.
https://github.com/rails/rails/pull/17479
https://github.com/rails/rails/issues/8005
I have forked rails and applied the patch to 4.2-stable and it works for me. Feel free to use my fork, even though I cannot guarantee to sync with upstream on a regular basis.
https://github.com/ttosch/rails/tree/4-2-stable
You can use ActiveRecord::Associations::Preloader to preload guests and tags linked, respectively, to each of the event and image objects that are associated as a subject with the collection of activities.
class ActivitiesController < ApplicationController
def index
activities = current_user.activities.page(:page)
#activities = Activities::PreloadForIndex.new(activities).run
end
end
class Activities::PreloadForIndex
def initialize(activities)
#activities = activities
end
def run
preload_for event(activities), subject: :guests
preload_for image(activities), subject: :tags
activities
end
private
def preload_for(activities, associations)
ActiveRecord::Associations::Preloader.new.preload(activities, associations)
end
def event(activities)
activities.select &:event?
end
def image(activities)
activities.select &:image?
end
end
image_activities = Activity.where(:subject_type => 'Image').includes(:subject => :tags).order(created_at: :desc).limit(20)
event_activities = Activity.where(:subject_type => 'Event').includes(:subject => :guests).order(created_at: :desc).limit(20)
activities = (image_activities + event_activities).sort_by(&:created_at).reverse.first(20)
I would suggest adding the polymorphic association to your Event and Guest models.
polymorphic doc
class Event < ActiveRecord::Base
has_many :guests
has_many :subjects
after_create :create_activities
end
class Image < ActiveRecord::Base
has_many :tags
has_many :subjects
after_create :create_activities
end
and then try doing
Activity.includes(:subject => [:event, :guest]).order(created_at: :desc).limit(20)
Does this generate a valid SQL query or does it fail because events can't be JOINed with tags and images can't be JOINed with guests?
class Activity < ActiveRecord::Base
self.per_page = 10
def self.feed
includes(subject: [:guests, :tags]).order(created_at: :desc)
end
end
# in the controller
Activity.feed.paginate(page: params[:page])
This would use will_paginate.

Rails Polymorphic associations without nesting forms

I am still new to rails and I am trying to figure out how to implement a polymorphic association without using a nested route or form. I tried searching but everything seemed to be about nesting forms or adding comments, which is not what I am trying to do.
Here are my models
Article.rb
class Article < ActiveRecord::Base
belongs_to :articable, polymorphic: true
end
Organization.rb
class Organization < ActiveRecord::Base
has_many :articles, as: :articable
end
People.rb
class People < ActiveRecord::Base
has_many :articles, as: :articable
end
I want to implement a 'New Article' link from a Organization or People show page and have the correct article_id and article_type entered. What would the correct syntax be to generate this link?
Thanks!
Routes:
resource :people do
resource :articles
end
resource :organizations do
resource :articles
end
ArticlesController:
def create
article = Article.new(params[:article])
if params[:people_id]
people = People.find(params[:people_id])
people.articles << article
else
organization = Organization.find(params[:organization_id])
organization.articles << article
end
article.save
end
Organizations view:
link_to new_organization_article_path(#organization)...

What is the easiest way to duplicate an activerecord record?

I want to make a copy of an ActiveRecord object, changing a single field in the process (in addition to the id). What is the simplest way to accomplish this?
I realize I could create a new record, and then iterate over each of the fields copying the data field-by-field - but I figured there must be an easier way to do this.
Perhaps something like this:
new_record = Record.copy(:id)
To get a copy, use the dup (or clone for < rails 3.1+) method:
#rails >= 3.1
new_record = old_record.dup
# rails < 3.1
new_record = old_record.clone
Then you can change whichever fields you want.
ActiveRecord overrides the built-in Object#clone to give you a new (not saved to the DB) record with an unassigned ID.
Note that it does not copy associations, so you'll have to do this manually if you need to.
Rails 3.1 clone is a shallow copy, use dup instead...
Depending on your needs and programming style, you can also use a combination of the new method of the class and merge. For lack of a better simple example, suppose you have a task scheduled for a certain date and you want to duplicate it to another date. The actual attributes of the task aren't important, so:
old_task = Task.find(task_id)
new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))
will create a new task with :id => nil, :scheduled_on => some_new_date, and all other attributes the same as the original task. Using Task.new, you will have to explicitly call save, so if you want it saved automatically, change Task.new to Task.create.
Peace.
You may also like the Amoeba gem for ActiveRecord 3.2.
In your case, you probably want to make use of the nullify, regex or prefix options available in the configuration DSL.
It supports easy and automatic recursive duplication of has_one, has_many and has_and_belongs_to_many associations, field preprocessing and a highly flexible and powerful configuration DSL that can be applied both to the model and on the fly.
be sure to check out the Amoeba Documentation but usage is pretty easy...
just
gem install amoeba
or add
gem 'amoeba'
to your Gemfile
then add the amoeba block to your model and run the dup method as usual
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
enable
end
end
class Comment < ActiveRecord::Base
belongs_to :post
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :posts
end
class PostsController < ActionController
def some_method
my_post = Post.find(params[:id])
new_post = my_post.dup
new_post.save
end
end
You can also control which fields get copied in numerous ways, but for example, if you wanted to prevent comments from being duplicated but you wanted to maintain the same tags, you could do something like this:
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
exclude_field :comments
end
end
You can also preprocess fields to help indicate uniqueness with both prefixes and suffixes as well as regexes. In addition, there are also numerous options so you can write in the most readable style for your purpose:
class Post < ActiveRecord::Base
has_many :comments
has_and_belongs_to_many :tags
amoeba do
include_field :tags
prepend :title => "Copy of "
append :contents => " (copied version)"
regex :contents => {:replace => /dog/, :with => "cat"}
end
end
Recursive copying of associations is easy, just enable amoeba on child models as well
class Post < ActiveRecord::Base
has_many :comments
amoeba do
enable
end
end
class Comment < ActiveRecord::Base
belongs_to :post
has_many :ratings
amoeba do
enable
end
end
class Rating < ActiveRecord::Base
belongs_to :comment
end
The configuration DSL has yet more options, so be sure to check out the documentation.
Enjoy! :)
Use ActiveRecord::Base#dup if you don't want to copy the id
I usually just copy the attributes, changing whatever I need changing:
new_user = User.new(old_user.attributes.merge(:login => "newlogin"))
If you need a deep copy with associations, I recommend the deep_cloneable gem.
In Rails 5 you can simply create duplicate object or record like this.
new_user = old_user.dup
Here is a sample of overriding ActiveRecord #dup method to customize instance duplication and include relation duplication as well:
class Offer < ApplicationRecord
has_many :offer_items
def dup
super.tap do |new_offer|
# change title of the new instance
new_offer.title = "Copy of #{#offer.title}"
# duplicate offer_items as well
self.offer_items.each { |offer_item| new_offer.offer_items << offer_item.dup }
end
end
end
Note: this method doesn't require any external gem but it requires newer ActiveRecord version with #dup method implemented
The easily way is:
#your rails >= 3.1 (i was done it with Rails 5.0.0.1)
o = Model.find(id)
# (Range).each do |item|
(1..109).each do |item|
new_record = o.dup
new_record.save
end
Or
# if your rails < 3.1
o = Model.find(id)
(1..109).each do |item|
new_record = o.clone
new_record.save
end
You can also check the acts_as_inheritable gem.
"Acts As Inheritable is a Ruby Gem specifically written for Rails/ActiveRecord models. It is meant to be used with the Self-Referential Association, or with a model having a parent that share the inheritable attributes. This will let you inherit any attribute or relation from the parent model."
By adding acts_as_inheritable to your models you will have access to these methods:
inherit_attributes
class Person < ActiveRecord::Base
acts_as_inheritable attributes: %w(favorite_color last_name soccer_team)
# Associations
belongs_to :parent, class_name: 'Person'
has_many :children, class_name: 'Person', foreign_key: :parent_id
end
parent = Person.create(last_name: 'Arango', soccer_team: 'Verdolaga', favorite_color:'Green')
son = Person.create(parent: parent)
son.inherit_attributes
son.last_name # => Arango
son.soccer_team # => Verdolaga
son.favorite_color # => Green
inherit_relations
class Person < ActiveRecord::Base
acts_as_inheritable associations: %w(pet)
# Associations
has_one :pet
end
parent = Person.create(last_name: 'Arango')
parent_pet = Pet.create(person: parent, name: 'Mango', breed:'Golden Retriver')
parent_pet.inspect #=> #<Pet id: 1, person_id: 1, name: "Mango", breed: "Golden Retriver">
son = Person.create(parent: parent)
son.inherit_relations
son.pet.inspect # => #<Pet id: 2, person_id: 2, name: "Mango", breed: "Golden Retriver">
Hope this can help you.
Since there could be more logic, when duplicating a model, I would suggest to create a new class, where you handle all the needed logic.
To ease that, there's a gem that can help: clowne
As per their documentation examples, for a User model:
class User < ActiveRecord::Base
# create_table :users do |t|
# t.string :login
# t.string :email
# t.timestamps null: false
# end
has_one :profile
has_many :posts
end
You create your cloner class:
class UserCloner < Clowne::Cloner
adapter :active_record
include_association :profile, clone_with: SpecialProfileCloner
include_association :posts
nullify :login
# params here is an arbitrary Hash passed into cloner
finalize do |_source, record, params|
record.email = params[:email]
end
end
class SpecialProfileCloner < Clowne::Cloner
adapter :active_record
nullify :name
end
and then use it:
user = User.last
#=> <#User(login: 'clown', email: 'clown#circus.example.com')>
cloned = UserCloner.call(user, email: 'fake#example.com')
cloned.persisted?
# => false
cloned.save!
cloned.login
# => nil
cloned.email
# => "fake#example.com"
# associations:
cloned.posts.count == user.posts.count
# => true
cloned.profile.name
# => nil
Example copied from the project, but it will give a clear vision of what you can achieve.
For a quick and simple record I would go with:
Model.new(Model.last.attributes.reject {|k,_v| k.to_s == 'id'}
Try rails's dup method:
new_record = old_record.dup.save

Resources