Bear with me, I am new to posting and Rails so sorry if I mess up phrasing!!
I am working on a Rails app with many similar models. Each view has a _form.html.haml partial that differs in content but contains similar components, such as buttons to submit, delete, etc. Every model has_many_attached photos, and in the form you can add or delete photos. The haml for deleting photos looks like this, where variable is replaced with whatever view _form.html.haml is in:
.gallery#form
- @VARIABLE.photo.each_with_index do |image, index|
.overlay-container
.img-square{ :style => "background-image: url(#{rails_blob_url(photo(@VARIABLE, index))})", :alt => "Photo of #{image}" }
= link_to("Delete", delete_image_attachment_VARIABLE_url(image), method: :delete, class: 'button delete overlay')
To make the delete work on each photo, this code is in each controller:
def delete_image_attachment
@photo = ActiveStorage::Attachment.find(params[:id])
@photo.purge
redirect_back fallback_location: @VARIABLE
flash[:success] = 'Photo was successfully deleted.'
end
And routes.rb has this chunk of code for each model:
resources :VARIABLE do
member do
delete :delete_image_attachment
end
end
However, I have about a dozen models I need to do this on. My goal is to bring the gallery into a new partial, since it will be used in every _form regardless of the other content. However, the delete function (though the same for every controller) is tied to the controller/routes.rb of each model.
There must be some way of DRYing this functionality into a couple files instead of copy-pasting for each model, but my Google searches have not turned up anything. So, any guidance or better Rails convention is greatly appreciated!
If I'm understanding the structure properly, it sounds like you would want to do something like the following:
id the photo's ID to delete by.redirect where to redirect the user after the action is completed.Now there will only be 1 route to handle the controller above.
For rendering a collection of images with a redirect url that allows you to set the following state for the partial:
photos a collection of images to render.redirect_url this is passed as a query parameter to the centralized delete image controller.This should be able to DRY up your implementation. Currently the only thing I see that couples the view with the deletion is the redirect URL. By abstracting that out and moving that essentially to a parameter for the partial will allow for re-use and flexibility.
You've already identified your coupling via @VARIABLE, here's a quick mock of how I would expect it to end up looking like:
.gallery#form
- @photos.each_with_index do |image, index|
.overlay-container
.img-square{ :style => "background-image: url(#{rails_blob_url(photo(@VARIABLE, index))})", :alt => "Photo of #{image}" }
= link_to("Delete", delete_image_attachment_url(image, redirect: @redirect_url), method: :delete, class: 'button delete overlay')
Ths would require: photos, and redirect_url
So make sure to set @photos and @redirect_url on the consuming controller.
@photos = GET_PHOTOS_HERE
@redirect_url = 'some-redirect-path'
render partial: 'photos_partial'
photos = GET_PHOTOS_HERE
render partial: 'photos_partial', locals: { photos: photos, redirect: 'some-redirect-path' }`
Note: You may need to change how you access the local variables in the template.
https://guides.rubyonrails.org/layouts_and_rendering.html#passing-local-variables
def delete_image_attachment
@photo = ActiveStorage::Attachment.find(params[:id])
@photo.purge
redirect_back fallback_location: params[:redirect]
flash[:success] = 'Photo was successfully deleted.'
end
Here you would only have the single route for deleting any image attachment, and point at the single controller above.
delete 'resources/image_attachment/:id', to: 'resources#delete_image_attachment'
Note: Replace "resources" with whatever your controller name is, or the scoping/naming you would like.
PS: It's been a while since I've done Rails so I'm not completely certain on the accuracy or your environment.