moiristo / deep_cloneable

This gem gives every ActiveRecord::Base object the possibility to do a deep clone that includes user specified associations.
MIT License
786 stars 89 forks source link

Possible unexpected has_and_belongs_to_many behaviour. #137

Open somazx opened 3 years ago

somazx commented 3 years ago

When calling deep_clone with include and referencing a has_and_belongs_to_many association - it doesn't actually make new instances of the object(s) like it does for every other association (has_many, etc).

Using the test suite methods:


def test_single_include_association
    deep_clone = @jack.deep_clone(:include => :mateys)
    assert deep_clone.new_record?
    assert deep_clone.save
    assert_equal 1, deep_clone.mateys.size
    puts "\n\nDEBUG DEBUG single include association"
    puts "@jack.matey_ids => #{@jack.matey_ids}"
    puts "deep_clone.matey_ids => #{deep_clone.matey_ids}"
end
 ...
def test_should_deep_clone_many_to_many_associations
    @human = Animal::Human.create :name => 'Michael'
    @human2 = Animal::Human.create :name => 'Jack'
    @chicken1 = Animal::Chicken.create :name => 'Chick1'
    @chicken2 = Animal::Chicken.create :name => 'Chick2'
    @human.chickens << [@chicken1, @chicken2]
    @human2.chickens << [@chicken1, @chicken2]

    deep_clone_human = @human.deep_clone(:include => :ownerships)
    assert deep_clone_human.new_record?
    assert deep_clone_human.save
    assert_equal 2, deep_clone_human.chickens.count
    puts "\n\nDEBUG DEBUG many_to_many"
    puts "deep_clone_human.chicken_ids => #{deep_clone_human.chicken_ids}"
    puts "@human.chicken_ids => #{@human.chicken_ids}"
end

/*
> bundle exec rake test
...

DEBUG DEBUG single include association
@jack.matey_ids => [22]
deep_clone.matey_ids => [23]            --> NEW ID, NEW INSTANCE

 ....
DEBUG DEBUG many_to_many
deep_clone_human.chicken_ids => [1, 2]
@human.chicken_ids => [1, 2]           --> SAME INSTANCES, SAME IDS - NOT NEW INSTANCES
*/

Is this an issue?

moiristo commented 3 years ago

Hi Andy,

Thank you for your interest! The example you specified is actually a hm-through, not habtm. I think it also exemplifies why it works like this: some would like that only new join records to the records (chickens) are created, some would like the whole association to be duplicated. When you define your habtm as a hm-through, I think you can choose between both:

# Only duplicates ownerships
@human.deep_clone(include: :ownerships)
# Duplicates ownerships AND chickens
@human.deep_clone(include: { ownerships: :chicken })
mrr4cc00n commented 2 years ago

Having this issue as well. Any solutions or workarounds @somazx ?