Callback for Active Storage file upload
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
Do action after upload file with Active Storage
There is the dirty? method that you can use
class Post < ApplicationRecord
has_many_attached :photos
before_update :do_whatever, if: -> { photos.dirty? }
def do_whatever
# doing something
end
end
You might also be able to try before_update :do_whatever, if: -> { photos_changed? }
How to test if a new file is sent with Active Storage in the model?
You can use new_record? to check if file
is new ie:
def set_filename
if file.attached? && file.new_record?
self.file.blob.update(filename: "#{new_file_name()}.#{self.file.blob.content_type.split('/')[1]}")
end
end
Alternatively, use before_create
instead of before_save
so that set_name
only runs when uploading new file.
Updated
Interestingly, ActiveStorage handles blob change outside model hooks. Apparently, it doesn't even support validation right now. There's no way to verify blob has changed as its state is not persisted anywhere. If you peek into rails log, notice rails purge old blob as soon as a new one is added.
Few options I can think of:
1.Update filename in controller eg:
original_name = params[:file].original_name
params[:file].original_name = # your logic goes here
2.Store blob file name in parent model and compare in before_save
.
def set_filename
if file.attached? && file.blob.filename != self.old_filename
self.file.blob.update(filename: "#{new_file_name()}.#{self.file.blob.content_type.split('/')[1]}")
end
end
None of these solutions are ideal but hope they give you some ideas.
Related Topics
Require Ruby File Without .Rb Extension
Ruby - Naming Convention - Letter Case for Acronyms in Class/Module Names
Do I Need to Indent My Code in Ruby
Autoload Paths and Nested Services Classes Crash in Ruby
Upgrade Ruby on Elastic Beanstalk
What Could Be Causing This Rails Ioerror Closed Stream
Flash Message with HTML_Safe from the Controller in Rails 4 (Safe Version)
Is There an Elegant Way to Exclude the First Value of a Range
Unexpected Value of _Callee_ When Including a Module - Is This a Ruby Bug
Can You 'Require' Ruby File in Irb Session, Automatically, on Every Command
Can't Run Ruby 2.2.3 with Rvm on Osx
Detecting Regional Settings (List Separator) from Web
How to Call Methods Defined in Applicationcontroller in Models
How to "Extract" Values from a Multidimensional Array in a Smart Way
Pass Command Line Argument to Vagrant Shell Script Provisioner