How to get current_tenant for Paperclip :path in model?

In my multi-tenant app I’d like to store each tenant’s uploaded files in a different AWS S3 ‘folder’ according to the tenant’s name.

Using Tenant.current_id set in an around filter in ApplicationController, I can do this in the model:

class Upload < ActiveRecord::Base
  ...
  has_attached_file :upload, styles: { original: '500x500', thumb: '50x50' },
  path: ":class/#{Tenant.find(Tenant.current_id).name}/:id/:style/:filename"
  ...

This ensures that all uploads from tenant with name ‘cornerstone’ are saved in:

/uploads/cornerstone/89/filename.jpg

Is this a reasonable way to get the current tenant inside the model?

Does Upload have a relationship to Tenant that might allow you to accomplish this with a custom interpolation in an initializer?

@reshleman perfect! I already looked at this option but didn’t fully understand it. Taking the time to read it properly, all makes sense now.

Changing the path option to: path: ":class/:tenant_name/:id/:style/:filename" and adding:

class Upload < ActiveRecord::Base
...
  Paperclip.interpolates :tenant_name do |attachment, style|
    attachment.instance.tenant.name
  end
...
end

This works great in the app but blows up on the command line because there is no tenant signed in. So I modified it to:

Paperclip.interpolates :tenant_name do |attachment, style|
  Tenant.current_id ? attachment.instance.tenant.name : 'dev'
end

This will work as normal but won’t blow up in the console and if I do create any test uploads there they will be put in the ‘/dev/’ folder on S3.

Does this look good?

Nice!

Not sure how your app is set up – is Tenant.current_id always empty if you’re accessing via the command line? If you try to view or modify an exiting attachment for a real tenant via the command line, will you get the correct path with this interpolation, or will you get dev?

I might consider basing the conditional on the attachment itself, instead of relying on Tenant.current_id to be set. Maybe something like:

Paperclip.interpolates :tenant_name do |attachment, style|
  attachment.instance.tenant.present? ? attachment.instance.tenant.name : 'dev'
end

You could also consider moving this conditional logic to, e.g., a tenant_name method on Upload, which will probably be easier to test, and simplify the interpolation to just attachment.instance.tenant_name.

I usually put these interpolations in an initializer (usually config/initializers/paperclip.rb), so they don’t bloat the model classes, but that’s up to you.