Search code examples
ruby-on-railsruby-on-rails-5rails-activestorage

Can't purge active storage attachments


I have a model called Resource configured with:

class Resource < ActiveRecord::Base    
has_many_attached :assets
end

I created an action in my resources_controller.rb as follows:

  def delete_asset_attachment
    @asset = ActiveStorage::Attachment.find_by(params[:id])
    logger.debug "The value of @asset is #{@asset}"
    @asset.purge
    redirect_to @resource
  end

I have a form that shows the resource and loops through the attached assets. Below is the snippet of code doing the loop through the assets:

<% @resource.assets.each do |asset| %>
<%= link_to 'Remove Attachment', delete_asset_attachment_resource_url(@resource, asset.id), method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>

The /resources page properly shows the resource along with the attached assets. However, when I try to click the link to delete one of the assets, I receive an error: "undefined method `purge' for nil:NilClass". However, in console I see the attachment exists.

Here is the output from the server console:

    Started DELETE "/resources/10/delete_asset_attachment.18" for ::1 at 2019-03-09 17:27:28 -0500
Processing by ResourcesController#delete_asset_attachment as 
  Parameters: {"authenticity_token"=>"EFZO5V9Bii3dId0I6hn5DajFR5WJYZBc8qPAAi5ppQOFW3cws5I4FjyVP9IlvA+2a2kKUJhobnqd8atG4L3k+g==", "id"=>"10"}
  Resource Load (0.1ms)  SELECT  "resources".* FROM "resources" WHERE "resources"."id" = ? LIMIT ?  [["id", 10], ["LIMIT", 1]]
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ActiveStorage::Attachment Load (0.1ms)  SELECT  "active_storage_attachments".* FROM "active_storage_attachments" WHERE (10) LIMIT ?  [["LIMIT", 1]]
The value of @asset is #<ActiveStorage::Attachment:0x00007f8d7bd4df68>
  ActiveStorage::Blob Load (0.2ms)  SELECT  "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ? LIMIT ?  [["id", 27], ["LIMIT", 1]]
Completed 500 Internal Server Error in 5ms (ActiveRecord: 0.6ms)

NoMethodError - undefined method `purge' for nil:NilClass:

::1 - - [09/Mar/2019:17:27:28 EST] "POST /resources/10/delete_asset_attachment.18 HTTP/1.1" 500 76939
http://localhost:3000/resources/10/edit -> /resources/10/delete_asset_attachment.18
Started POST "/__better_errors/70da0e976a425fce/variables" for ::1 at 2019-03-09 17:27:28 -0500
  ActiveStorage::Blob Load (0.2ms)  SELECT  "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = ? LIMIT ?  [["id", 27], ["LIMIT", 11]]
::1 - - [09/Mar/2019:17:27:28 EST] "POST /__better_errors/70da0e976a425fce/variables HTTP/1.1" 200 36499
http://localhost:3000/resources/10/delete_asset_attachment.18 -> /__better_errors/70da0e976a425fce/variables

I've searched for solutions everywhere. The couple that exist on stackoverflow didn't address my issue. There is an incredible lack of specific details and examples in the Rails guide or anywhere else on the web for specifically handling deleting attachments. Would appreciate any help.

UPDATE: Here are my routes.rb:

resources :resources do get 'listing', :on => :collection put :sort, on: :collection member do delete :delete_asset_attachment end end

UPDATE 2: rails routes output

resources GET    /resources(.:format)                                                                     resources#index
                             POST   /resources(.:format)                                                                     resources#create
                new_resource GET    /resources/new(.:format)                                                                 resources#new
               edit_resource GET    /resources/:id/edit(.:format)                                                            resources#edit
                    resource GET    /resources/:id(.:format)                                                                 resources#show
                             PATCH  /resources/:id(.:format)                                                                 resources#update
                             PUT    /resources/:id(.:format)                                                                 resources#update
                             DELETE /resources/:id(.:format)                                                                 resources#destroy

Solution

  • I've been able to make this work. Ahhhh. The rush, after hours of frustration.

    Piecing things together after reading this article Deleting ActiveStorage Attachments From the Controller, 3 Ways

    I changed my controller code to be this:

      def delete_asset_attachment
        @resource.assets.find_by(params[:attachment_id]).purge
        redirect_to @resource
      end
    

    and my form to be this:

       <% @resource.assets.each do |asset| %>
          <%= asset.filename %>
          <%= link_to 'Remove Attachment', delete_asset_attachment_resource_url(@resource, asset.id), method: :delete, data: { confirm: 'Are you sure?' } %>
       <% end %>
    

    I believe the issue was that the line in my old code:

    @asset = ActiveStorage::Attachment.find_by(params[:id])
    

    ...was only passing the @resource id and the attachment was not being found. The key was changing this line:

    @resource.assets.find_by(params[:attachment_id]).purge
    

    ...which more properly points to the correct resource and then the specific asset (attachment) to be purged.