Ryan Lyu
Ryan Lyu

Reputation: 5135

Why Rails allows double rendering?

class SomeController < ApplicationController
  before_action :api_limit

  def new
    if user.can_access_foo?
      render 'foo'
    end

    if user.can_access_bar?
      render 'bar'
    end

    if user.can_access_hello?
      render 'hello'
    end
  end

  def api_limit
    render 'exceed_limit_error'
    # code in action will not be executed.
  end
end

When render or redirect_to exists in before_filter, action will not be executed. In short, there is no risk of double rendering.

See the Rails Guide on controllers :

If a "before" filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter, they are also cancelled.

However, why Rails allow double rendering in an action?

Take following code as example. a Double Render Exception will raised by Rails when user can access foo, bar, or hello.

class SomeController < ApplicationController
  before_action :callback

  def new
    if user.can_access_foo?
      render 'foo' 
      # From my understanding, following render should 
      # be ignored if this step is successfully performed.
    end

    if user.can_access_bar?
      render 'bar'
    end

    if user.can_access_hello?
      render 'hello'
    end
  end
end

Why not respond immediately and halt the request cycle when render 'foo' completes? That sounds more reasonable.

Upvotes: 0

Views: 203

Answers (1)

Toby 1 Kenobi
Toby 1 Kenobi

Reputation: 5035

The reason why a render statement in an action doesn't return the code execution is that it is a reasonable use case that there is further (non-rendering) code to be executed after the render in the action.

The reason that a render in the before_action callback doesn't allow code execution to go into the action is because this would assume that you have actions that have code paths that neither render nor redirect (otherwise you would get a double render error). This code path in the action is a much less reasonable use case because it would rely on the "before" filter to have triggered and performed a render already.

The intention of the structure of Rails' actions and filters in the controllers is that they are not so tightly coupled. Generally a filter will not be aware of what action will run after it, and an action is not aware of what filters triggered before it ran. So to make them coordinate as to which is doing the rendering would break that loose coupling. Each action must assume that rendering is an essential part of its role, that is why it doesn't make sense for an action to run if the filter already rendered.

Upvotes: 1

Related Questions