When I used to use the MailView
gem to preview ActionMailer
emails, I would access @rack_env
to preview my emails under different conditions, eg delivery to a specific user or in a specific locale, or with specific inputs. With the new(ish) ActionMailer::Preview
that instance variable isn't being set, and params
doesn't exist. Does anyone know how to access the GET params to /rails/mailers/:mailer/:action
with ActionMailer::Preview
?
Ideally I'd like to be able to request /rails/mailers/my_mailer/mymail?user_id=123
and use it like
class MyMailerPreview < ActionMailer::Preview
def mymail
MyMailer.mymail(User.find(params[:user_id]))
end
end
ActionMailer does not have access to the controller params, so pass the params you need as arguments instead:
class MyMailerPreview < ActionMailer::Preview
def mymail(user_id)
MyMailer.mymail(User.find(user_id))
end
end
Then in your controller:
MyMailerPreview.mymail(params[:user_id]).deliver
I came up with a simple solution to this and detailed it with a blog post on extending ActionMailer . It's the easiest solution that I've found, my only concern would be how it may impact performance for other controllers.
# config/initializers/mailer_injection.rb
# This allows `request` to be accessed from ActionMailer Previews
# And @request to be accessed from rendered view templates
# Easy to inject any other variables like current_user here as well
module MailerInjection
def inject(hash)
hash.keys.each do |key|
define_method key.to_sym do
eval " @#{key} = hash[key] "
end
end
end
end
class ActionMailer::Preview
extend MailerInjection
end
class ActionMailer::Base
extend MailerInjection
end
class ActionController::Base
before_filter :inject_request
def inject_request
ActionMailer::Preview.inject({ request: request })
ActionMailer::Base.inject({ request: request })
end
end
Here's a solution I use for a specific parameter (in my case to load a specific order), you could modify this to make it more generic and work for any or all parameters
It uses a class variable in the the XXXMailerPreview class, which shouldn't be a problem if you are running in dev. This is required because at least in Rails 5 the previews are shown in an IFrame and the url params added to the preview url are not passed to the IFrame urls.
Specifically this example code was for Abandoned Cart emails in Spree but it is also used for any order specific email preview
Preview all emails at http://localhost:3000/rails/mailers/abandonded_checkout_mailer
In mailer_controller_decorator.rb
which you can load anyway you want after Rails is loaded
module MailerPreviewOrderNumber
def set_order_number(order_number)
@order_number = order_number
end
def get_order_number
@order_number
end
def order_for_preview
order_number = get_order_number
order = Spree::Order.find_by(:number => order_number) if order_number.present?
order = yield if order.blank?
order
end
end
Rails::MailersController.class_eval do
before_action :save_order_number, only: [:preview]
def save_order_number
if @preview.present? && @preview.respond_to?(:set_order_number)
@preview.set_order_number(params[:order]) if params[:order].present?
end
end
end
And in your XXXMailerPreview file
class AbandondedCheckoutMailerPreview < ActionMailer::Preview
extend MailerPreviewOrderNumber
def abandoned_checkout_email
if Rails.env.development?
order = self.class.order_for_preview do
Spree::Order.order(updated_at: :desc).last
end
AbandonedCheckoutMailer.abandoned_checkout_email(order)
end
end
end
The block passed to order_for_preview
is used in the case that no order param is provided to the preview URL the first time
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.