ErwinM/acts_as_tenant

Gotcha & workaround when using require_tenant = true with ActionMailer::Preview's

tvongaza opened this issue · 5 comments

There is a small gotcha when using required_tenant = true alongside ActionMailer::Preview's. As per the Rails guide (https://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails) a typical action mailer preview looks something like this, where the welcome email will contain calls to a model with a tenant set:

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    UserMailer.with(user: User.first).welcome_email
  end
end

With acts as tenant we are using this as follows in our dev workflow, which you would expect to properly generate the preview email:

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    ActsAsTenant.with_tenant(Account.first) do
      UserMailer.with(user: User.first).welcome_email
    end
  end
end

The call to welcome_email email will return an ActionMailer::MessageDelivery object, which is a Delegator and won't actually process the message until the getobj method is called. So what happens in the mail preview is the mail isn't generated within the ActsAsTenant.with_tenant(Account.first), but at a later point in time, where the tenant is no longer set, throwing an error - or if for some reason used where another tenant is set - using the wrong tenant.

There is a relatively simple fix, just call .message in your preview within the ActsAsTenant.with_tenant(Account.first):

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    ActsAsTenant.with_tenant(Account.first) do
      UserMailer.with(user: User.first).welcome_email.message
    end
  end
end

I've poked through the rails & acts as tenant code a bunch, and unsure of a simple way to fix this. For us it only really impacts us in development so far and the fix is simple enough. It is something to be aware of if using mail previews with the gem.

excid3 commented

How come you're not calling with_tenant in the method?

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    ActsAsTenant.with_tenant(Account.first) do
      UserMailer.with(user: User.first).welcome_email
    end
  end
end

We are, see the last two code blocks. The first code block is the example from the mail preview docs (https://guides.rubyonrails.org/action_mailer_basics.html#previewing-emails). The next two code blocks are applied with acts as tenant.

excid3 commented

You're wrapping ActsAsTenant around the method definitions in your code examples.

🤦 Good catch! Typo, on my end, thanks for the catch (code updated). The issue still exists, as it is still returning a ActionMailer::MessageDelivery object and not actually performing the mail generation until the __getobj__ call, which in the rail mail preview code will happen later, out of scope of the tenent block.

I can't seem to reproduce this. The following works fine for me in the spec/dummy app.

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    ActsAsTenant.with_tenant(Account.first) do
      UserMailer.with(user: User.first).welcome_email
    end
  end
end

The with_tenant block returns the ActionMailer::Parameterized::MessageDelivery object returned by welcome_email and Rails 7 handles that fine.

Added the example in 572469d

Gonna close this, but let me know if you figure out what's different. Maybe a different Rails version or something.