amoeba-rb / amoeba

A ruby gem to allow the copying of ActiveRecord objects and their associated children, configurable with a DSL on the model
Other
811 stars 68 forks source link

has_many dupes do not set belongs_to in memory #93

Open adsteel opened 3 years ago

adsteel commented 3 years ago

Given the following models:

class User
  has_many :posts

  amoeba do
    enable
    include_association :posts
  end
end

class Post
  belongs_to :user, optional: false
end

When I attempt to amoeba dupe a user, it will fail

> user.amoeba_dup.save!
ActiveRecord::RecordInvalid: Validation failed: User must exist

This is because amoeba sets only user.posts in memory, but not post.user and the presence validator is run before anything is saved.

The following fixes the issue:

class User
  has_many :posts

  amoeba do
    enable
    include_association :posts

    customize(lambda { |_, new_user|
      new_user.posts.each do |post|
        post.user = new_user
      end
    })
  end
end
kirichkov commented 2 years ago

I'm also seeing a similar error, which has, I believe, the same root cause.

I'm using delegation, i.e.:

class User
  has_many :posts
end

class Post
  belongs_to :user, optional: false
  has_many :attachments

  amoeba do
    enable
  end
end

class Attachment
  belongs_to :attachable, polymorphic: true

  delegate :user, to: :attachable
end

When I try to duplicate a Post I get a Module::DelegationError pointing me at the delegate line in Attachment and an error message Attachment#user delegated to attachable.user, but attachable is nil: #<Attachment id: nil, attachable_id: nil, attachable_type: "Post", file: "file.pdf", description: "", created_at: nil, updated_at: nil>

Using @adsteel 's proposed solution has resolved it for me!

The funny thing is that this has been working correctly on rails 5.2 with Ruby 2.5. The issue has appeared after upgrading to Ruby 2.7 and Rails 6.1

kirichkov commented 2 years ago

After some digging I've found a similar issue that appears in code that is not related to amoeba at all. It looks like a bug in rails, which doesn't set the inverse association when the new record is created via accepts_nested_attributes_for.

ukolovda commented 1 month ago

I use inverse_of to fix the error:

class User
  has_many :posts, inverse_of: :user
  ...
end

Readme have this information, see https://github.com/amoeba-rb/amoeba#validating-nested-attributes