Search code examples
ruby-on-railsrubyrails-activestorage

Callback for Active Storage file upload


Is there a callback for active storage files on a model

after_update or after_save is getting called when a field on the model is changed. However when you update (or rather upload a new file) no callback seems to be called?

context:

class Person < ApplicationRecord
  #name :string
  has_one_attached :id_document

  after_update :call_some_service

  def call_some_service
    #do something
  end
end

When a new id_document is uploaded after_update is not called however when the name of the person is changed the after_update callback is executed


Solution

  • The answer from @Uleb got me 90% of the way, but for completion sake I will post my final solution.

    The issue I had was that I was not able to monkey patch the class (not sure why, even requiring the class as per @user10692737 did not help)

    So I copied the source code (https://github.com/rails/rails/blob/fc5dd0b85189811062c85520fd70de8389b55aeb/activestorage/app/models/active_storage/attachment.rb#L20)

    and modified it to include the callback

    require "active_support/core_ext/module/delegation"
    
    # Attachments associate records with blobs. Usually that's a one record-many blobs relationship,
    # but it is possible to associate many different records with the same blob. If you're doing that,
    # you'll want to declare with <tt>has_one/many_attached :thingy, dependent: false</tt>, so that destroying
    # any one record won't destroy the blob as well. (Then you'll need to do your own garbage collecting, though).
    class ActiveStorage::Attachment < ActiveRecord::Base
      self.table_name = "active_storage_attachments"
    
      belongs_to :record, polymorphic: true, touch: true
      belongs_to :blob, class_name: "ActiveStorage::Blob"
    
      delegate_missing_to :blob
    
      #CUSTOMIZED AT THE END:
      after_create_commit :analyze_blob_later, :identify_blob, :do_something
    
      # Synchronously purges the blob (deletes it from the configured service) and destroys the attachment.
      def purge
        blob.purge
        destroy
      end
    
      # Destroys the attachment and asynchronously purges the blob (deletes it from the configured service).
      def purge_later
        blob.purge_later
        destroy
      end
    
    
      private
        def identify_blob
          blob.identify
        end
    
        def analyze_blob_later
          blob.analyze_later unless blob.analyzed?
        end
    
        #CUSTOMIZED:
        def do_something
    
        end
    end
    

    Not sure its the best method, and will update if I find a better solution