GithubHelp home page GithubHelp logo

moiristo / deep_cloneable Goto Github PK

View Code? Open in Web Editor NEW
782.0 782.0 89.0 271 KB

This gem gives every ActiveRecord::Base object the possibility to do a deep clone that includes user specified associations.

License: MIT License

Ruby 100.00%

deep_cloneable's People

Contributors

amatsuda avatar ares avatar askarzinurov avatar brian-kephart avatar christophebelpaire avatar dependabot[bot] avatar dexterthedragon avatar dlackty avatar dylanwolff avatar ekohl avatar gogainda avatar hlxwell avatar ilyakuzmin avatar indrekj avatar jamesbebbington avatar jokklan avatar moiristo avatar nherzing avatar nwalke avatar rin avatar ristovskiv avatar seanculver avatar seku avatar svensson-david avatar tmlee avatar vladislav-yashin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

deep_cloneable's Issues

Prefill a nested element with simple_form and deep_cloneable

Is it possible to pre-fill a simple_fields_for input with datas from another object ?

I have a User object. User has_one Backpack, Backpack has many Pockets, and Pocket has_many Items

When building my form, i'm using

        <%= f.simple_fields_for :backpack do |backpack| %>
          <%= render 'backpack', f:backpack %>
        <% end %>

Now, when I'm on users#new, the whole form is empty. I would like to initialize the Backpack fields with datas from another Backpack object, is it possible ? I tried by doing this:

        <%= f.simple_fields_for :backpack, @backpack do |backpack| %>
          <%= render 'backpack', f:backpack %>
        <% end %>

along with this in my UsersController:

    @user = User.new
    @backpack = Backpack.find(1).deep_clone include: [ :pockets, { pockets: :items } ]

But this doesn't work: for some reason, the Backpack fields are filled, but his Pockets and their Items are empty (Only the direct attributes of Backpack are pre-filled).

Did I miss something ?

Block processing isn't in an obvious place

From the README:

Pass a block to deep_clone to modify a duped object after duplication
My assumption after reading the above was that my block would get executed right before the copy was returned.

In particular, properties excluded with the except parameter would be gone and any value I wrote to those fields would be included in the copy.

This isn't the case, as exclusion happens after the block is invoked.

For now I suggest adding something like the following to the docs:

Note: The block is invoked before properties are excluded

In a future version, I think invoking the block after all other processing would be more valuable.

Rails 5

I have troubles with Rails 5 and postgres:

foreign key field is not a number and some other fields are invalid, any ballpark date for Rails 5 compatibility ?

Problem scoping associated objects

I am duplicating an existing object as part of a new object creation action as follows:

@structure = @old_structure.deep_clone include: :steps

It's a document with a sequence of steps. When the form renders the steps, they are scoped as follows:

scope :ordered_steps, -> {
    order("position ASC")
}

And the form itself renders the steps as follows:

<%= f.fields_for :steps, @structure.steps.ordered_steps do |builder| %>
    <%= render 'step_fields', f: builder %>
<% end %>

When I have the scoping in there, none of the steps are rendered. When I remove the scoping, the steps show up. Is this a problem with my implementation or the gem?

Thank you.

Can't clone just an association without explicitly excluding all parent attributes

It would be nice to be able to clone only an association while excluding all parent attributes like so:

pirate.deep_clone only: :treasures

Obviously, this can be done by including the association and then explicitly excluding the parent attributes:

pirate.deep_clone include: :treasures, except: Pirate.column_names

but I expected only to work properly for this.

Doesn't clone all children

Sorry for no specs. I'm hoping this is just a misunderstanding on my end.

The following will clone 2 of the total 28 child flight lessons and 0 scenarios.
Course.find(2).dup(include: {:flightlessons => :scenarios}).update_attribute(:farpart_id, Farpart.find(1).id)

updating the farpart_id is needed to pass a scoped unique validation

Course
has_many :flightlessons
has_many :scenarios, through: :flightlessons (I get more cloned flight lessons if I comment this out)

Flightlesson
has_many :scenarios, as: :lesson, dependent: :destroy # poly

How can I alter this to get an accurate clone of child flightlessons: :scenarios?

versioning clones

if i want to version clones, do i give them a root_id or the same id? or make the primary key (id, clone_id)?

Wrong number of arguments under Ruby 2.0 and Rails 3.2.13

With Ruby 2.0 and Rails 3.2-STABLE, the :include isn't working:

> question.dup :include => :choices
ArgumentError: wrong number of arguments (1 for 0)

Maybe it's not removing the original dup correctly?

Fwiw, I first thought it was a bug in Rails because even regular dup didn't work

> p.dup
NoMethodError: private method `initialize_dup' called for #<Question:0x007fd6fb9d9a98>

But I just switched updated Rails past this bug in Rails 3.2.x with Ruby 2.0 that was fixed in 3.12.13.

Has many through , attributes not working

I have my associations as

Report
       has_many :projects
       has_many :project_milestones, through: :projects
       has_many :members, through: :projects

        accepts_nested_attributes_for :key_activities , :reject_if => :all_blank, :allow_destroy => true
    accepts_nested_attributes_for :risks , :reject_if => :all_blank, :allow_destroy => true
    accepts_nested_attributes_for :projects , :reject_if => :all_blank, :allow_destroy => true
    accepts_nested_attributes_for :project_milestones , :reject_if => :all_blank, :allow_destroy => true
    accepts_nested_attributes_for :members , :reject_if => :all_blank, :allow_destroy => true

and i try to deep clone

r = Report.last.deep_clone :include => [:key_activities, :risks, {:projects => [:project_milestones, :members]}]

i am getting all attributes except project_milestones and members

r.projects.last.members.last
=> #<Member:0x007f8234394ed8 id: nil, name: "", worked_hrs: "", created_at: nil, updated_at: nil, project_id: nil>

what could be wrong here? :o

Reusing cloned relation objects

I have an object a of class A that I want to clone. a has a relation to a b of class B and to a c of class C. When cloning a I want to include both b and c relation objects. According to the documentation this can be done like so:

a.deep_clone :include => [:b, :c]

Everything seems fine at the first sight. But here's my problem:

The object b has a relation to the same c that a is already related to. This means object a has a field c_id with value (say) 42 and object b also has a c_id field with the same value 42. When I now run the above code this leads to the following cloned objects:

<A-Class-Object id: 100, b_id: 120, c_id: 140>,
<B-Class-Object id: 120, c_id: 160>,
<C-Class-Object id: 140>,
<C-Class-Object id: 160>

So as you can see this clones object c twice and therefore creates two distinct objects although there was just one object that was used by multiple objects in the original relations.

What I would expect is to detect reused objects via equal _id-fields and only create one object for them which would result to the following objects after cloning:

<A-Class-Object id: 100, b_id: 120, c_id: 140>,
<B-Class-Object id: 120, c_id: 140>,
<C-Class-Object id: 140>

Did I do something wrong or is this behavior just not implemented yet?

I would definitely expect this to be included within this gem so if it's not implemented yet, please don't just close the ticket, I'd love to help getting this done. What do you think, @moiristo?

Btw: I'm using deep_cloneable 2.1.1 in a Rails 3.2.x project.

Conditional include is ignored

Using this code on an Employer model:

def duplicate(new_name, options = {})
  include_employees = options[:include_employees] || true
  new_employer = self.deep_clone include: [
    employees: [if: lambda{ |x| include_employees }]
  ], validate: false
end

If include_employees is false, employees are still included. Is my syntax incorrect, or is deep_cloneable just ignoring the condition?

Activerecord 4.1

Any plans for support for the new activerecord release? I tested 4.1 in my app and got this stack trace for a dup of one of my HABTM associations:

/var/lib/gems/2.0.0/gems/deep_cloneable-1.6.0/lib/deep_cloneable.rb:137:in `block in dup_join_association': nil is not a symbol (TypeError) 
from /var/lib/gems/2.0.0/gems/activerecord-4.1.0/lib/active_record/relation/delegation.rb:46:in `collect' 
from /var/lib/gems/2.0.0/gems/activerecord-4.1.0/lib/active_record/relation/delegation.rb:46:in `collect' 
from /var/lib/gems/2.0.0/gems/deep_cloneable-1.6.0/lib/deep_cloneable.rb:136:in `dup_join_association' 
from /var/lib/gems/2.0.0/gems/deep_cloneable-1.6.0/lib/deep_cloneable.rb:120:in `dup_has_many_through' 
from /var/lib/gems/2.0.0/gems/deep_cloneable-1.6.0/lib/deep_cloneable.rb:81:in `block in dup' 
from /var/lib/gems/2.0.0/gems/deep_cloneable-1.6.0/lib/deep_cloneable.rb:57:in `each' 
from /var/lib/gems/2.0.0/gems/deep_cloneable-1.6.0/lib/deep_cloneable.rb:57:in `dup' 

Thanks for any info!

Publish to RubyGems?

The 2.0.0 version in the gemspec apparently was never published. Was there a reason for this?

deep_clone mess up with date offset if not saved yet

Hey,

If the record is not saved yet and you try to create a deep clone of it, it will result in false date offset

foo = Foo.new(param['foo']) # param['foo'] as bar_attributes in it
foo_cloned = foo.deep_clone include: :bar

>> foo_cloned.bar.first.start_at
=> Fri, 31 Jul 2015 14:00:00 CEST +02:00
>> foo.bar.first.start_at
=> Fri, 31 Jul 2015 12:00:00 CEST +02:00

For this to not happend I've to save the record and reload it.

foo = Foo.new(param['foo']) # param['foo'] as bar_attributes in it
foo.save
foo.reload
foo_cloned = foo.deep_clone include: :bar

>> foo_cloned.bar.first.start_at
=> Fri, 31 Jul 2015 12:00:00 CEST +02:00
>> foo.bar.first.start_at
=> Fri, 31 Jul 2015 12:00:00 CEST +02:00

Do you have an idea why this happen?
Alex

Rails 3.0.9 not picking up has_many association

I am trying to follow your example. I have a set of objects arranged something like folders of forms.

So, each folder belongs to a user, and has_many forms, each form has_many answers.

When I try and clone using deep_cloneable, I don't get the parents set right. So for example:

new_folder = folder.clone :include=>[:user, {:forms => :answers}]

It populates the in-memory objects with all the cloned values correctly, except I get validation errors that the form's folder_id is nil but is required, and the answers' form_id is nil and is required. I recognize that I could remove those validation checks if I wanted, but it seems like an odd thing to fail on, given that I have :autosave=>true on the has_many relationships.

Conversely the user_id is likewise required ,but it does not throw a validation error, and that belongs_to attribute does not have autosave turned on.

Any ideas how to resolve this?

dirty attributes issue in 2.2.1 but not in 2.2.0

Hello,
Thank you for this gem :)
A little issue in version 2.2.1 of this gem.
With this code :

a = Product.create(title: 'demo')
b = a.deep_clone(except: [:title])
puts b.title.inspect #=> nil
puts b.title_was.inspect #=> nil
#with 2.2.0
puts b.title_changed?.to_s => false
#with 2.2.1
puts b.title_changed?.to_s => true

Context :
Ruby 2.3.1
Rails 4.2.6

cannot deep clone my projects

Hi,
I'm trying to deep clone my project, it has elements, and folders with experiments and elements also,
all has belongs_to and has_many relationships.
my code is this:

source_project = Projects::Project.find_by_id(params[:id])
cloned_project = source_project.deep_clone(include: [:elements, { folders: [:experiments, :elements] }])

the error I'm getting is this:

undefined method `each' for #Projects::Project:0x007fc9547a6730

and the full (relavent)trace is this:

NoMethodError - undefined method `each' for #<Projects::Project:0x007fc9547a6730>:
  activemodel (4.2.5) lib/active_model/attribute_methods.rb:433:in `method_missing'
  activerecord (4.2.5) lib/active_record/associations/collection_association.rb:365:in `replace'
  activerecord (4.2.5) lib/active_record/associations/collection_association.rb:47:in `writer'
  activerecord (4.2.5) lib/active_record/associations/builder/association.rb:123:in `samples='
  deep_cloneable (2.2.0) lib/deep_cloneable.rb:123:in `block in dup_has_many_association'
  activerecord (4.2.5) lib/active_record/relation/delegation.rb:46:in `collect'
  deep_cloneable (2.2.0) lib/deep_cloneable.rb:120:in `dup_has_many_association'
  deep_cloneable (2.2.0) lib/deep_cloneable.rb:76:in `block in deep_clone'
  deep_cloneable (2.2.0) lib/deep_cloneable.rb:40:in `deep_clone'
  app/controllers/knowledge/projects_controller.rb:363:in `duplicate'

What am I missing? Or doing wrong?
Please help....

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

Clarification in README regarding uploaders

Hello!
I had some issues with cloning models with mounted uploaders, in my case Carrierwave.
Best solution i found was using a block when cloning and setting the attributes manually to get the files to copy together with the other data.

I think it could be good to add a note in the README regarding this to make the process smoother for others.

Issue duplicating associations with attachments with pg_search installed

I have my model Customer which has many customer documents. Inside a document I am using carrierwave to save an attachment.

From looking at previous posts on how to properly copy carrierwave attachments I was able to get my documents and their attachments cloned from the code below.

new_cusotmer = self.dup include: [{ customer_documents: :document }], validate: false do |original, kopy|]
  # Clone Attachments
  kopy.attachment = original.attachment if kopy.is_a?(Document)
end
new_customer.save!

The problem I am getting is that when I included the gem pg_search under my customer model, it still clones the customer and all its documents but it doesn't create an attachment anymore. It copies the original.attachment to the kopy.attachment but if I was to run

document.attachment?

it returns false because no file was created.

I am only having this issue when I am including pg_search in my customer model and was just wondering why this could be happening and if anyone else has experianced this.

STI associations

How would this work with STI associations that are not available on the parent?

class Road
end

class Expressway < Road
end

class Highway < Road
  has_many :bridges
end

@new_highway = @road.deep_clone include: :bridges

This is failing for any non-highway roads.

ActiveRecord::Base::DeepCloneable::AssociationNotFoundException: Expressway#bridges

Rails 4.2 Compatibility

I'm using this method to deep_clone items:

def copy_to(new_date)
  self.deep_clone(include: [:photos, :variants]) do |original, copied_item|
    copied_item.deal_at = new_date if copied_item.respond_to?(:deal_at)
    copied_item.image   = original.image if copied_item.respond_to?(:image)
  end
end

Running rspec 3.1.0 against Rails 4.2.rc1 with deep_cloneable 2.0.0 fails on the deep_clone line with:

NoMethodError: undefined method `dup_has_many_has_many' for #<Deal:0x007f9b07dc7a18>

Support for cloning file attachments

Sometimes we not only clone the AR object and relationships, but also clone the files, like attachments, so it would be great if we can write some code to support the paperclip, carrierwave, attachment_fu or something else.

Conditional include only works for the first record of a has_many association

Here's my code, I want to skip any document without files on it

  def dup_info
    {
      users: [
        { profile: [:model1, :model2] },
        { documents: [ if: lambda{|doc| doc.file.present?} ] }
      ]
    }
  end

 kopy = original.deep_clone(include: dup_info)

only the first user of kopy is skipping the docs without a file on it, all other users still have all the documents on them. Am I doing anything wrong here?

I had tried put if: lambda{|doc| doc.file.present?} into an array or a hash, got the same result.

deep clone method not recognized using Rails 2.3.10

It seems the DeepClone module is loaded in such a way as to be inaccessible through the ActiveRecord hierarchy using Rails 2.3.10. I see that this is tested only using Rails 3, 3.1, but with instructions for Rails 2 it seems that the intent is to continue to support it. FYI, I've been using an old version of the plugin and that works great.

Pete

Question: how to do a really really really deep dup?

Hi, I'm trying to figure out how do a really really really really deep dup with deep_clonable
... this is the example provided pirate.dup :include => {:treasures => :gold_pieces}
Is it possible include children of :gold_pieces ? thanks

has_many relationship doesn't clone

Hello!

Here's my code:

@original_meal = Meal.find(params[:id])
@meal = @original_meal.deep_clone include: :meal_foods

This is fired off in a 'clone' action in a controller.

The meal has_many meal_foods. Each meal_foods belongs_to a meal. The meal that is being cloned has many meal_foods associated with it.

When I clone it, I do in fact see some foods in the controller before the new meal is saved, but the associations aren't saved. I see the meal itself has no id since it hasn't been saved yet, thus the meal_foods can't be associated to it.

With that said, must I clone the original meal, and then write my own code to save the meal_foods from the original meal to the new one, or is something that should be handled by this gem?

Rejecting cloning via include block

Is it possible to reject cloning certain objects in a relation by using a block? Something like:

.deep_clone(:include => {
:parameters => :details do |original, kopy|
# reject if details.somefield = "DontCopyThis"
end
})

?

Clarification in readme about cloning with a block

The README correctly shows that when using a block, the cloned object should be checked before performing some custom operation, to see if is is_a? certain type or respond_to? a particular method.

However, I learned that this is because the associated objects being cloned are also passed to this block and it would fail with a missing method error if the cloned associated object has no such method.

Could you add a note to the corresponding section so that this comes as less of a surprise?

Thanks in advance.

Cloning Many-to-Many associations

Hi there,
I would like to ask if you can spot something wrong in what I'm doing. I have an object, say User, who can have many FamilyName, and a FamilyName can be shared by many User.

So I have a linking table named UserFamilyName.

When I try to clone a user I want to clone the family name and so I'd expect to do:

u = User.first
u_new = u.clone(:include => :user_family_names)
u_new.save!

But when I do this it complains about the user_id being duplicated (in my UserFamilyName I state that a user_id, family_name_id pair can only occur once (a User can't have the same FamilyName twice)).

I would then expect that I should do:

u = User.first
u_new = u.clone(:include => :user_family_names, :except => [{:user_family_names => [:user_id]}])
u_new.save!

But I get the same error than before...

Can you spot something wrong?

This is on a Rails 3.0.5 application running on Ruby 1.9.2.

Thank you! =)

not duplicating created_at

I am duplicating a rails object, it is duplicating fine with all details except the created_at value of the object.

Here is the code . I want created_at value of raw_materials and costing_items .

@costing = @old_costing.deep_clone :include => [{style: :images}, {raw_materials: :costing_items} , :other_cost_fixeds, :other_costs, :exchange_rates ], :use_dictionary => true do |original, kopy|
                kopy.remote_picture_url = original.picture_url if kopy.is_a?(Image)
            end

Error deep cloning with named association

I'm trying to deep clone an object with several associations:

Poll has_many :questions
Question has_many :options
Option  has_many :follow_up, :class_name => "Question", :foreign_key => "parent_option_id", :dependent => :destroy, :inverse_of => :option

When I try to deep clone a poll object, poll.deep_clone include: { questions: [ {options: :follow_up} ] } , I get the error NoMethodError: undefined method 'each' for #<Option:0x007fd87d419490>,

with the following trace:

activemodel-3.2.22.2/lib/active_model/attribute_methods.rb:407:in `method_missing'
activerecord-3.2.22.2/lib/active_record/attribute_methods.rb:149:in `method_missing'
activerecord-3.2.22.2/lib/active_record/associations/collection_association.rb:310:in `replace'
activerecord-3.2.22.2/lib/active_record/associations/collection_association.rb:41:in `writer'
activerecord-3.2.22.2/lib/active_record/associations/builder/association.rb:51:in `block in define_writers'
deep_cloneable-2.2.1/lib/deep_cloneable.rb:127:in `block in dup_has_many_association'
activerecord-3.2.22.2/lib/active_record/associations/collection_proxy.rb:91:in `collect'
activerecord-3.2.22.2/lib/active_record/associations/collection_proxy.rb:91:in `method_missing'
deep_cloneable-2.2.1/lib/deep_cloneable.rb:124:in `dup_has_many_association'

It seems to be an issue when the association doesn't use the same name as the class of the object, similar to #58

Any idea how to get around this?

Missing relations in new (cloned) model

Im cloning an event object with nested models like show here:

    copied_object = self.deep_clone include: [
      :alternative_tickettexts,
      :conversion_script,
      :event_email_description,
      :memberships,
      :performances,
      :sponsor_images,
      passe_partouts: [:performances, {categories: :products}],
      categories: :products,
      ], use_dictionary: true, validate: false

The result is a cloned object, but misses the categories: :products on the copy. The use of use_dictionary doesn't seem to make a difference. I tried various alternatives, but never ended up with the required result. Can you give me some advice here?

include an scoped has_many

How can I include a has_many relation but scoped?
For instance:

class Treasure < ActiveRecord::Base
  scope :last_week, lambda { where("created_at >= ?", Time.zone.now - 1.week) }
end    

dup = @jack.dup(:include => :treasures ) # but only from last_week !!!

can't get this to work

I can't get this to work in a rails3 app - have you tried it for sure? i keep getting the ArgumentError (wrong number of arguments(1 for 0)): i.e. it is not accepting the options e.g. :except, :include

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.