lusketeer
lusketeer

Reputation: 1930

Rails 4 ActionMailer around_action | access action information (action_name & parameters)

I'm building an around_action for my customer_mailer class so that I don't have to wrap begin and rescue around every time I call deliver_now

class CustomerMailer < ApplicationMailer
  around_action :rescue_error

  def send_email(customer)
    ...
  end

  def invite_friend(customer, invitee_email)
    ...
  end

  private
    def rescue_error
      yield
    rescue => e
      msg = "Caught exception! #{e} | #{action_name}"
      puts msg
      raise
     end
end

So in the rescue, I want to log the message with information such as which action was called, I managed to find the method action_name to show which action was called, but I couldn't find a way to retrieve the parameters that were passed into the action, any ideas?

Thanks!

Upvotes: 7

Views: 1326

Answers (3)

Ivan A.
Ivan A.

Reputation: 399

Just store the parameters with which the action has been called to an instance variable, say @params. Then these parameters will be accessible in rescue_error via @params. As per your example:

class CustomerMailer < ApplicationMailer
  around_action :rescue_error

  def send_email(customer)
    @params = { customer: customer }
    ...
  end

  def invite_friend(customer, invitee_email)
    @params = { customer: customer, invitee_email: invitee_email }
    ...
  end

  private

  def rescue_error
    begin
      yield
    rescue => e
      msg = "Caught exception! #{e} | #{action_name} | #{@params.inspect}"
      puts msg
      raise
    end
  end
end

You can make the assignment to @params a bit cleaner by using hash parameters in your actions, e.g.

def invite_friend(options = {})
  @params = params
  ...
end

Of course, this requires accessing the parameters via options, such as options[:customer] to access customer, and options[:invitee_email] to access invitee_email.

Upvotes: 2

Greg Navis
Greg Navis

Reputation: 2934

Before I answer your question: would using Bugsnag or something similar work in your case? Alternatively would rescue_from Exception, with: :exception_handler work for you? (it won't allow you to reraise the exception though)


I dug into Rails source code and it seems that parameters are not stored anywhere. They are just passed as a splat to an instance method defined in your mailer class. However, there is a way to store them (without monkey-patching).

Mailers inherit from AbstractController::Base. Looking at the snippet below:

  # Call the action. Override this in a subclass to modify the
  # behavior around processing an action. This, and not #process,
  # is the intended way to override action dispatching.
  #
  # Notice that the first argument is the method to be dispatched
  # which is *not* necessarily the same as the action name.
  def process_action(method_name, *args)
    send_action(method_name, *args)
  end

  # Actually call the method associated with the action. Override
  # this method if you wish to change how action methods are called,
  # not to add additional behavior around it. For example, you would
  # override #send_action if you want to inject arguments into the
  # method.
  alias send_action send

we can see that we can override #send_action and make it store the arguments. Add the following to your ApplicationMailer:

class ApplicationMailer < ActionMailer::Base
  def send_action(method_name, *args)
    @action_args = args
    super
  end
end

The arguments will be available as @action_args in all your mailers.

Upvotes: 4

plombix
plombix

Reputation: 406

The action name have to be yielded , it depends on the way you use your rescue_error .

Define a variable in the block that will be yielded

or raise specifics errors (maybe your custom exception class ) this way you'll retrieve invormation via "e"

post an exemple use case of rescue_error.

Upvotes: 0

Related Questions