I like to dry my controllers by placing reoccurring code in before and after filters. In my current project, every action has the same respond_to block:
respond_to do |wants|
wants.html
wants.js { render :layout => "transition" }
end
I placed this in an after_filter like so:
after_filter :respond_to_html_or_transition
But it leads to this 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".
This happens even when there is no explicit respond_to or redirect within the action. I'm assuming this happens because Rails (for lack of an explicit respond_to call) makes an educated guess and creates its own respond_to before the after_filter. If that's the case, is there any way for me to keep Rails from doing that and instead use the block in the after_filter?
What you are trying to accomplish is not possible with an after_filter. The after filter deals with an already rendered action. At that point, the render have already completed (either the explicit or implicit render), thus calling render will result in a double call.
You can extract the code logic in a method and call the method. Another alternative would be to make all the actions alias of a single action, assuming the body of the actions can be handled by a single method.
Last but not least, you can write a custom action responder and replace the default one of the controller setting
self.responder = CustomActionResponder
Related
How does one use the Ruby on Rails method performed? in an if else statement?
I tried to use this StackOverflow anwser in my example below:
if condition
does something
elsif redirect_to(records_path)
performed?
does another thing
else
yet another thing
end
But this only redirects without checking if it is performed.
I want it to check if the redirect to records_path is performed and when true do something (or "does another thing" in my example)
I also tried this:
elseif records_path.performed?
And this:
elseif redirect_to(records_path) performed?()
And all things in between.
Can somebody explain how it's done and how I could've get it from the docs?
In a controller action, when we type render or redirect_to those are not immediately executed, but they are queued and will be executed after the completion of the method. So this allows to have double renders or redirect_to in a controller action, and this will generate an error (because then rails has no idea which to execute). So that is why in rails they have added a method performed? which will indicate if a render or redirect_to has already been called (queued) or not.
In most cases this is not really needed because normally your controller code is pretty simple.
To clarify: performed? does not actually test the redirect_to has been done, it just tests a render or redirect-to was called/queued. Furthermore the redirect_to does not return a boolean indicating whether it was done or not.
So your code should be like this:
if condition
does something
else
redirect_to(records_path)
end
if performed?
# the redirect-to was executed
does another thing # but not a render or redirect
else
yet another thing
# and could be a render or redirect
# if not the normal view will be rendered
end
Please not that in this simple example the performed? is just the negative of condition so you could easily squash those together.
performed? just tests if render or redirect has already happened. That doesn't check what view was sent to the user. That check only "you define path yourself or it must be done automatically".
Try something like this:
if condition
does something
redirect_to(records_path)
end
if performed?
does another thing
else
yet another thing
end
It's meant for your controller action (A) to be able to call other controller methods (M), and then render or redirect in (A) only if none of (M) have performed a render/redirect.
Read the source code, it's pretty simple and explicit: https://apidock.com/rails/ActionController/Metal/performed%3F
For example:
class ArticlesController < ApplicationController
def show
check_identity
render :show unless performed?
end
def check_identity
redirect_to root_path, notice: "You're not allowed to be here" unless user_signed_in?
end
end
I am writing a Ruby on Rails application with a controller called "pages_controller" that is responsible for displaying pages to users. There are 3 different types of pages that can be displayed, and different things have to happen on the back end in each case, so I decided to break the functionality out into 3 methods within the controller. When the user requests a page, the "show" method is called, which figures out whether the page:
1. Belongs to the user
2. Belongs to another user, and can be viewed by the user requesting it
3. Belongs to another user, and cannot be viewed by the user requesting it (unauthorized)
The appropriate method is then called from there to display the page. The code looks something like this:
def show
if (something)
showMine
elsif (something else)
showAnother
else
showUnauthorized
end
end
def showUnauthorized
respond_to do |format|
format.html # showUnauthorized.html.erb
end
end
def showMine
respond_to do |format|
format.html # showMine.html.erb
end
end
def showAnother
respond_to do |format|
format.html # showAnother.html.erb
end
end
I am getting a template missing error because rails wants to render a view when "show" is called, but I do not want any views to be rendered when "show" is called. I simply want "show" to call the correct method from there, and the corresponding view for that method (showMine, showAnother, or showUnauthorized) to be rendered. How can I do this? Or am I going about this the wrong way entirely?
You need to declare these new actions that you have created in the routes file, as they don't belong to the RESTful routes.
I sugest to keep only the show action in your controller and create the IFs in the show view using the render method to include the partials(_showMine.html.erb, showAnother.html.erb, showUnauthorized)
example:
show view:
if (something)
<%= render 'showMine' %>
elsif (something else)
<%= render 'showAnother' %>
else
<%= render 'showUnauthorized' %>
end
I hope it helps...
I basically agree with Samy's comment, but here's some background:
The method that tells Rails what view to use is render. If there's no call to that method in your show method, Rails assumes you have a view called show.xxx.xxx, e.g. show.html.erb, that is supposed to be rendered. Note that it doesn't assume template will be prefixed with show because that's the name of the method. It assumes it will be show because that's the name of the action. The name of the action is passed to the controller as part of the request; it's not simply derived from the name of whatever method has a respond_to block in it.
All the respond_to blocks do is specify different view templates based on the MIME type of the request, but since you never call render, all of those extra methods are still trying to call the show view (show.html.erb in every case), because you never told Rails to render any other view, and the action name is show.
So, instead of the respond_to blocks, just call render [some_view] in each of your other methods.
This might not be the clearest answer, but I'd suggest also reading the following:
http://ryanbigg.com/2009/04/how-rails-works-2-mime-types-respond_to/
It describes what respond_to does, in particular how it keys off the action name to determine what view to render.
Given a rails RESTful controller
class WidgetsController
def new
#widget = Widget.new
end
def create
#widget = Widget.new(params[:widget])
render :new and return unless w.save
redirect_to widget_path(#widget)
end
end
Other than to capture the parameters and render validation messages why is the convention to render :new instead of redirect_to new_widget_path if validation fails?
As URL will alter from /widgets/new to /widgets if validation fails doesn't this break the concept of REST?
Render will not call the controller action, so the instance variables that you have set in the create action will be taken to the new view.
this means that any validation errors persist in the new view...
redirect_to would run the controller action for new, and therefore create a new instance of your model... and you would loose the errors...
hope this helps
Could you do this instead of rendering the new action?
flash[:thing] = params[:thing]
redirect_to new_thing_path
Then in new
#thing = Thing.new(flash[:thing] || params[:thing])
This honestly seems nicer to me because otherwise if you have any logic in your controller that is required to render the new/edit actions, you have to repeat that logic (or create some before_filter or something) in update/create. I realize this involves one more request but aside from that it seems simple and safer to code (apart from the fact that you are breaking rails conventions) and it gives the user a valid url that is always the same for looking at/doing the same thing.
If they refresh they'll lose their values but that is true of any form before being submitted. And that seems to make more sense to me than refreshing resulting in a resubmit warning which is pretty strange. How is a user supposed to clear the form for example?
This rails-ism has always bugged me. Are there are any serious issues with this that I'm not considering? Do other application frameworks all do the same?
The thing with render is that it will not cause another request to be generated. It will go to the corresponding view and display it. With redirect, however, a redirect request to the browser will be generated, which will cause another request to your server.
I've been reading about respond_with. and used it before in a couple of tutorials but dont really think I understand it fully.
The functionality I am trying to implement right now is this: I have a list of items of class Article, and each of them has a link to the create Favorite action. When the user clicks on it it the favorite instance is created and the user is redirected to the home page. I want this to work with AJAX without a page refresh, and execute some JavaScript on the article item to let the user know it's been favourited successfully. I've used the :remote => true attribute in the relevant link, so the action is executing remotely without problem, but I am a bit stuck on how to execute the action's .js.erb file.
I've done the following in the controller:
class FavouritesController < ApplicationController
before_filter :authenticate
respond_to :html, :js
def create
#article = Article.find_by_id(params[:article_id])
current_user.Favourites.create(:article => #article)
redirect_to root_path
end
The problem is, I dont know what parameter should i pass to respond_with. I've tried replacing the redirect with both respond_with #article, and respond_with without parameters, and while both of them work (the create.js.erb is called), I dont understand why...
I dont want to 'respond_with' anything, but only that when the action is executed via javascript, the create.js.erb gets called. Can anyone explain a) why does respond_with work anyway whatever I pass to it, and b) what is the right way to do this?
BTW, I am using Rails 3.0
EDIT: I understand it better if I use respond_to like in the following code, but I would like to understand respond_with better.
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
I ran across this question several times trying to solve a similar problem. It turns out my controller wasn't inferring the format as I assumed it would from the content type or the fact that it's an xhr request.
In any case, it worked when I switched from this:
contacts_path
To this:
contacts_path format: :json
I didn't care for that per se, so I changed my ajax call to include the dataType option like this (per jQuery ajax documentation):
$.ajax({url: contactUrl, type: 'POST', data: data, dataType: 'json' });
Hi Oalo go through the link below, it concisely explains the respond_with and respond_to. It lists all the options that you can pass to respond_with
http://ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with
Hope this answers your query
In my view file, I use a collection_select with an :onchange => remote_function that execute another action from the same controller (the action will eventually update the page content)
I'd like this action to be only accessible when called by the JS but not when the corresponding route is directly entered as an URL in the browser.
Any idea how I could do this ?
You can use request.xhr? to check the request type, either its AJAX request or others (post, get). It returns true or false. If true you can perform the action.
You could use respond_with and respond_to
class MyController
respond_to :js
def index
respond_with(#users = User.all)
end
end