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 88 forks source link

Multiple attachments for a nested association #115

Closed vitobotta closed 4 years ago

vitobotta commented 5 years ago

Hi! I have a model with some nested associations, and the last association can have multiple attachments uploaded with ActiveStorage. How do I adapt the snippet in the README to this case? The example seems to be for a parent model with a single attachment, while I have multiple attachments with a nested association/model.

Thanks in advance!

moiristo commented 5 years ago

I'm sorry, I don't know as I don't use ActiveStorage yet :) Please let me know if you find out.

letiesperon commented 4 years ago

Has this been solved? I have the same question for carrierwave

moiristo commented 4 years ago

No, I haven't heard back. Looking at the docs, I can see that multiple attachments are basically attachments via has-many-through, so you might be able to do the same as for the singular version by iterating over the records in the association?

moiristo commented 4 years ago

Oh wait, you were referring to carrierwave :) Seems you need to associate an array of file paths in that case, see: https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-%22Upload%22-from-a-local-file

Roko131 commented 4 years ago

The current active storage example in the readme didn't work for me- rails 5.2. It uses ActiveStorage::Downloader but I received NameError: uninitialized constant ActiveStorage::Downloader

Here's a both has_one_attached and a has_many_attached snippets for rails 5.2 that worked for me (Based on this answer by jethro):

# Rails 5.2, has_one_attached example 1
pirate.deep_clone include: [:parrot, :avatar_attachment, :avatar_blob]

# Rails 5.2, has_one_attached example 2
pirate.deep_clone include: :parrot do |original, kopy|
  if kopy.is_a?(Pirate) && original.avatar.attached?
    attachment = original.avatar
    kopy.avatar.attach \
      :io           => StringIO.new(attachment.download),
      :filename     => attachment.filename,
      :content_type => attachment.content_type
  end
end

# Rails 5.2, has_many_attached example 1 (attach one by one)
pirate.deep_clone include: :parrot do |original, kopy|
  if kopy.is_a?(Pirate) && original.crew_members_images.attached?
    original.crew_members_images.each do |attachment|
      kopy.crew_members_images.attach \
        :io           => StringIO.new(attachment.download),
        :filename     => attachment.filename,
        :content_type => attachment.content_type
    end
  end
end

# Rails 5.2, has_many_attached example 2 (attach bulk)
pirate.deep_clone include: :parrot do |original, kopy|
  if kopy.is_a?(Pirate) && original.crew_members_images.attached?
    all_attachments_arr = original.crew_members_images.map do |attachment|
      {
        :io           => StringIO.new(attachment.download),
        :filename     => attachment.filename,
        :content_type => attachment.content_type
      }
    end
    kopy.crew_members_images.attach(all_attachments_arr) # attach all at once
  end
end
moiristo commented 4 years ago

Thanks for this, I've updated the readme!

febincloudyuga commented 4 years ago

I'm stuck with this problem too, where I have a multiple attachment for a nested association. Any inputs on how to go about it would help.

I have this association -

Without ActiveStorage, I'm able to make a deep clone as below - building.deep_clone include: [floors: [rooms: [:beds]]]

I have an ActiveStorage dependency of bed has_many_attached :sheets The Readme describes the usage of ActiveStorage for parent model, but not for the nested models.

Could you please shed some light on how to go about it?

Thanks.

moiristo commented 4 years ago

Hi,

As the readme states, a passed block will be evaluated for every copy created. That's why it's needed to check the class of the copy before doing stuff. So, in your case, you just need to check that the copied object is in fact a Bed.

building.deep_clone include: { floors: { rooms: :beds } } do |original, kopy|
  if kopy.is_a?(Bed) && original.sheets.attached?
     # ActiveStorage copy strategy for sheets
  end
end
febincloudyuga commented 4 years ago

Hi,

As the readme states, a passed block will be evaluated for every copy created. That's why it's needed to check the class of the copy before doing stuff. So, in your case, you just need to check that the copied object is in fact a Bed.

building.deep_clone include: { floors: { rooms: :beds } } do |original, kopy|
  if kopy.is_a?(Bed) && original.sheets.attached?
     # ActiveStorage copy strategy for sheets
  end
end

Thank you very much. I was able to get it to work. Appreciate the quick response :)