I want to patch Rails 6.0 to include part of this PR: https://github.com/rails/rails/commit/4dba136c83cc808282625c0d5b195ce5e0bbaa68
I'm only using direct uploads so I'm only patching create_before_direct_upload! at the moment. Here is what I have tried:
initializers/active_storage.rbmodule BlobOverride
class << self
def create_before_direct_upload!(key: nil, filename:, byte_size:, checksum:, content_type: nil, metadata: nil)
puts "In Blob Override Patch"
byebug
create! key: key, filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata
end
end
end
ActiveStorage::Blob.prepend(BlobOverride)
This returns the undefined methodhas_one_attached'` error, which I tracked down to a github issue here: https://github.com/rails/rails/issues/38876 Which basically says you can't use load the model from the initializer.
2. I then tried loading the module this way:
ActiveSupport.on_load(:active_storage_blob) do
ActiveStorage::Blob.prepend(BlobOverride)
end
And I didn't get an error but my patch wasn't hit.
3. I tried this:
Rails.application.config.to_prepare do
require 'active_storage/blob'
ActiveStorage::Blob.class_eval do
def create_before_direct_upload!(key: nil, filename:, byte_size:, checksum:, content_type: nil, metadata: nil)
puts "in create before direct upload patch"
byebug
create! key: key, filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata
end
end
end
No error, patched method wasn't hit.
How do I patch the blob model on active storage to support a custom key? The standard monkey patching isn't working for some reason.
This applies for Rails 6.1:
ActiveRecord now has a thing called has_secure_token, which ActiveStorage is using on Blob. This has_secure_token is setting a before_create with the following code:
before_create { send("#{attribute}=", self.class.generate_unique_secure_token(length: length)) unless send("#{attribute}?") }
This in turn sets self[:key] to be a secure_token
When it's being called like this, the self[:key] method only sets it if it doesn't exist
ActiveStorage::Blob.class_eval do
def key
self[:key] ||= 'your custom key'
end
end
I fixed this by doing the following in config/application.rb
class Application < Rails::Application
# ...
# ...
# ...
Rails.application.config.to_prepare do
require 'active_storage/blob'
class ActiveStorage::Blob
def self.generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)
gust = SecureRandom.base36(length)
epoch = Time.now.to_i.to_s
checksum = Digest::MD5.hexdigest(gust + epoch)
folder = checksum.chars.first(9).each_slice(3).to_a.map(&:join).join('/')
"#{folder}/#{gust}"
end
end
# Rails 5 doesn't have secure_token for Blob, so this will monkey patch it
ActiveStorage::Blob.class_eval do
def key
self[:key] ||= generate_key_with_folder
end
def generate_key_with_folder
gust = self.class.generate_unique_secure_token
epoch = Time.now.to_i.to_s
checksum = Digest::MD5.hexdigest(gust + epoch)
folder = checksum.chars.first(9).each_slice(3).to_a.map(&:join).join('/')
"#{folder}/#{gust}"
end
end
end
end
Thanks to @Arian's answer, here is my implementation in a Rails 5 app.
I created an initializer config/initializers/active_storage_custom_key.rb with the following code
Rails.application.config.to_prepare do
require 'active_storage/blob'
ActiveStorage::Blob.class_eval do
def key
self[:key] ||= File.join(self.class.generate_unique_secure_token, filename.to_s)
end
end
end
This generates keys with the form SZJLX7fLpsv1dzzj2PhRg7ve/my_filename.jpg
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With