Why Rails allows double rendering? - ruby-on-rails

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.

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.

Related

Render page more flexible

in my current project, have a assumption that a lot of different UI will be required.
ex> there will be two clients, boogle and yumhoo, these two client want totally different view.
I made a prototype like this,
class HomeController < ApplicationController
after_action :render_ui
def index
end
def render_ui
render "#{self.controller_name}/#{ENV['CLIENT_UI']}/#{self.action_name}"
end
end
my plan was to generate the path to the view file dynamically,
but there is a big problem here,
as you all know rails automatically runs,
render 'there own contoller and action name combination'
and throws an error,
Render and/or redirect were called multiple times in this action
Is there a way to skip the default automatic render feature in rails?
after_action is called when the view has already been rendered. If you want to render a specific file, simply call render in the action itself.
class HomeController < ApplicationController
def index
render "#{self.controller_name}/#{ENV['CLIENT_UI']}/#{self.action_name}"
end
end
You may also want to consider to use the Rails 4.1 template variants feature.

Rails render and return in before_action, DoubleRenderError

In a controller action with a before_action, using render something and return does not actually cause the controller to stop executing the rest of the action. In my testing, only when called in the controller action does and return work as I would expect.
# DoubleRenderError
class SomeController < ApplicationController
before_filter :double_render, only: [:index]
def index
render file: "public/500.html", status: :internal_server_error
end
def double_render
render file: "public/404.html", status: :not_found and return
end
end
# Renders 404 only, no error
class SomeController < ApplicationController
def index
render file: "public/404.html", status: :not_found and return
render file: "public/500.html", status: :internal_server_error
end
end
What's going on here? Can before_actions stop the rest of a controller's execution?
If your intention is to render the 404 page, you shouldn't render it manually in the before_filter. The right way of doing that is raising a routing error, like this:
raise ActionController::RoutingError.new('Not Found')
You could implement a method called "render_404", raise this exception in it and let the routing system do the rest.
EDIT: Actually, your code should work. I really don't know what is going on. I wrote the same thing here and it worked - the action method is never called if the before_filter renders something. What version of Rails are you using?
This is a pretty simple case of flow control.
Yes, when you and return from within the method, it obviously returns and doesn't reach the second render.
Nothing about rendering inside a before_filter is meant to stop the actual invocation of the action itself, and calling and return from a before_filter doesn't affect the execution of a completely separate method invocation.
This is pure Ruby flow-control, no Rails magic involved.

Rails controller methods

I have a controller action that is calling a model method to render some data. The controller looks something like this
class WeekController < ApplicationController
def index
#week = Week.first
end
def render
Week.render
redirect_to root_url
end
end
Currently my Week.render method has a bug in it and is causing errors. Which is fine, however when I am calling my index action the page fails because of the error in my render action.
Is it normal for rails to call actions other than the one being called or is there something weird going on?
It is ok to call other actions as long as you don't call redirect_to or render multiple times in a response.
By the way render is a reserved word, and you should not use it for a custom method.

Conditionally render a view if it exists? How to call render from a second method in a controller?

I am trying to render a custom partial if it exists, because I am running multiple websites with one codebase. The views are generally the same but sometimes they are different enough that it's easier to render a custom view.
Does anyone know how to call render from a method like below? The problem I am running into is that you have to call return after calling render, but I have to call return in the "index" method rather than calling the return in the "render_custom_view_if_exists" method. I can't seem to find a clever way to automatically check if the view exists and render it, since I would have to manually go into every method to call a return to cancel rendering the default view.
class ApplicationController
after_action :render_custom_view_if_exists
def render_custom_view_if_exists
render "#{path}" if lookup_context.template_exists?("#{path}")
return true
end
end
class UsersController < ApplicationController
def index
#users = User.all
end
end
ERROR: 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".
You could use append_view_path

Rails - Break Statement?

Does rails have a break statement?
I'm writing a controller that has some pretty complicated IF statements. In Sum what I'd like to do is something like this:
IF !All these variable exist and are not nil?
BREAK and don't continue bec something went wrong
END
If XXX = 'adsasd
do all this stuff
ELSE IF
ELSE
send out an error email
Is a break possible?
I don't know when all of your variables become available, but when I want to do checks in the controller, I usually use the before_filter callbacks to do that. For example:
class YourController
before_filter :check_if_variables_exist
def show
#prepare for render view
end
private
def check_if_variables_exist
unless #your_variable.nil?
#Do what you want
render :nothing => true
end
end
What this does is that when a request comes to action show in YourController, it will first call the private method check_if_variables_exist. If #your_variable is nil than it will end up at render :nothing => true and the filter_chain will be halted and the action show will never be called. If however your_variable is not nil, then the method will end without doing anything and the controller will then call the action show like usual.
So all the things you want to check beforehand can be placed in different before_filter callbacks. And if you don't want the check for all actions in the Controller, it can be specified like this:
class YourController
before_filter :first_check, :only => [:index, :show]
before_filter :second_check, :only => [:show, :edit]
In my opinion, this is the "rails" way to do it.
Ruby's return statement is what you're looking for. However...
I'm writing a controller that has some pretty complicated IF statements
Without knowing anything else about your application, this is a BIG red flag. Controllers are not the place to have a bunch of complicated logic. If you're using RESTful design patterns (and there's rarely a reason not to), then your controllers should be very lightweight and contain almost no logic. Models are where your business logic belongs. They allow you to isolate logic, simplifying your application and making it easier to test.

Resources