DoubleRenderError when delivering message with mail_form

595 views Asked by At

The following code sample is part of a Rails 3.2.16 app running on Ruby 1.9.3p484.
Whenever a new location is created or one is updated a message should be sent as defined in the after_filter.

class LocationController < InheritedResources::Base

  respond_to :json

  after_filter :notify_location_contact, only: [:create, :update]

  def create
    @location.user = current_user if current_user
    create!
  end

  def update
    update!
  end


  private

  def notify_location_contact
    message = MailForm.new
    deliver_location_message(message)
  end

  def deliver_location_message(location_message)
    begin
      if location_message.deliver
        render json: { message: "Successfully delivered" }, status: 201
      else
        render json: { error: "Delivery failure" }, status: 500
      end
    rescue => e
      if e.is_a?(ArgumentError)
        render json: { error: "Invalid Recipient" }, status: 422
      else
        render json: { error: e.message }, status: 500
      end
    end
  end

end

The message itself is sent. Though, deliver_location_message first renders the "Successfully delivered" block and after the last block rendering the error message. This causes an internal server error:

Completed 500 Internal Server Error

AbstractController::DoubleRenderError - Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".

For sending the message the mail_form gem ~> 1.5.0 is used.

The DoubleRenderError seems to happen because create and update both render the JSON response when they finished there work. After, .deliver renders its JSON response to inform about success or failure.

1

There are 1 answers

1
vee On

As the error points out you need to return after calling render because you have multiple calls to render in your deliver_location_message(message) method. The reason for the error is because Rails continues execution until the end of the method regardless of render or redirect.

Please try the following. Note the return on each render line.

  def deliver_location_message(message)
    begin
      if message.deliver
        # Here
        return render json: { message: "Successfully delivered" }, status: 201
      else
        # Here
        return render json: { error: "Delivery failure" }, status: 500
      end
    rescue => e
      if e.is_a?(ArgumentError)
        # Here
        return render json: { error: "Invalid Recipient" }, status: 422
      else
        # Here
        return render json: { error: e.message }, status: 500
      end
    end
  end

Alternative syntax to:

return render json: { message: "Successfully delivered" }, status: 201

is:

render json: { message: "Successfully delivered" }, status: 201 and return