[ActiveStorage] ActiveStorage custom storage path configuration per Model attribute

Hello Rails Team! First of all I want to thank you for all job that you make here!!! Rails is a wonderful framework to work with. I am creating an issue because I am not sure I have understood all functionalities of ActiveStorage. My pull request is not quite ready… Here is my issue: ActiveStorage custom storage path configuration per Model attribute Here is our business issue: We use the apartment gem to split our datas on a client basis and we want to make the same with uploaded files and store them in separate folders. thoughtbot has made a wonderfull paperclip gem, but since it is deprecated we are in the process of switching to ActiveStorage all our attachments management. In Paperclip there is a fine feature where you can choose a specific storage path for each of the model attachable attribute and interpolate values in it as follows:

# app/models/user.rb
# in Paperclip
has_attached_file :avatar,
                  path: ':tenant/users/:user_id/:hash/:filename'

Expected behaviour

Being able to configure a custom path. The idea is to make this possible in ActiveStorage too and have something like that:

# app/models/user.rb
# in future ActiveStorage
has_one_attached :avatar,
                 path: ':tenant/users/:id/:unique_secure_token'

### Our current solution For the moment we have made a monkey_patching to implement those storage path by changing the ActiveStorage::Blob#key.

# app/models/user.rb

def document=(attachable)
  document.attach(create_blob(attachable))
end

def attachable_storage_path
  [
    Apartment::Tenant.current.parameterize,
    'users',
    id,
    ActiveStorage::Blob.generate_unique_secure_token
  ].compact.join('/')
end
# app/models/application_record.rb

def attachable_storage_path
  raise NotImplementedError
end

def create_blob(attachable)
  return if attachable.nil?

  ActiveStorage::Blob.new.tap do |blob|
    blob.filename = attachable.original_filename
    blob.key = attachable_storage_path
    blob.upload(attachable)
    blob.save!
  end
end

Future solution

I have began to make a PR on ActiveStorage but want to have your feedback on this feature before digging deeper in this problematic and validate the right direction!

Here it is: https://github.com/frantisekrokusekpa/rails/pull/1/files

Thank you for your feedback and advices!

1 Like

We absolutely would need this. There is a previous pull request that was closed [ActiveStorage] Allow the usage of custom key format in Attached::Macros by LikitSaeLee · Pull Request #33285 · rails/rails · GitHub and an issue closed too Folder with active storage · Issue #32790 · rails/rails · GitHub

Instead of a template that might raise issues like this, it could be better to allow for a Proc to be passed that returns a string formatted like the user wants

On the flip side, interpolating user-provided values into storage keys is begging for trouble and we’re unlikely to support it.

My particular use case for this : I have a multi tenant application where I need to share attachments between different rails instances, all stored on S3. I need to ensure via an S3 policy that the shared attachments can only be modified or removed from the instance they originate from and not in the instance they are shared in.

You might think this is too complex a use case for ActiveStorage, yet this simple feature allowing to prefix the S3 object keys with a static string is sufficient. Sharing is easily performed by copying the active_storage_blobs table rows, and permissions is ensured by S3 using prefix based access control. We just need Rails to allow global prefix for S3 objects.

It’s possible now to customize the key for each object that is attached, but we need a solution for every attachment not and in the future. Defining a custom key for every attachment is not only unconvenient but a risk for future code that might forget to do this.