AbstractController::DoubleRenderError in rails 3 - ruby-on-rails-3

I always get the following 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".):
The error happens, when it id is nil the first, but not the second time...
def calc_next
id = next()
if id.nil?
id = next_next()
if id.nil?
render :layout => false, :format => :js
else
redirect_to :action => "view", :id => id, :format => :js
end
else
redirect_to :action => "view", :id => id, :format => :js
end
end
I don't see the problem in this redirection, as the outer one is fine. Even with the debugger there are not two redirections at the same time...
Any help is appreciated...
Markus

This looks like some kind of helper function rather than a Controller action. In which case you're probably calling calc_next twice in one action, or render / redirect_to from somewhere else in the same action. Remember that render and redirect_to don't immediately cause the Controller to return.
Check whether your control path can both call calc_next and call render or redirect_to from somewhere else (or from a second call into calc_next).
If you post the controller action you're going through, we may be able to help better.

Are there any before_filters that might be rendering or redirecting?

You have two options also:
redirect ... and return
render ... and return

Related

Rails action caching refresh after expire

I'm looking to expire and then refresh the cache for a controller action using a publicly accessible endpoint.
In my app currently, /all returns cached json, and /update expires the cache.
You can see the existing relevant code below.
What I'd like to do is not only expire the cache but force a refresh.
So, my question is:
Is there is a way to initiate the refresh of an action cache after expiring it, without hitting the action?
If the answer to that is no (as I'm beginning to suspect), then what would be the best way to do this? I require the update action to return an HTTP 200 status, not a 301 redirect so just redirecting to /all isn't an option.
VendorsController
caches_action :all, :expires_in=>2.weeks
....
def all
respond_to do |format|
format.json { render :json => Vendor.all }
format.html { render :json => Vendor.all }
end
end
....
def update
render :nothing => true
expire_action :action => :all
end
You should use write_fragment
def update
render :nothing => true
expire_action :action => :all
cache_path = ActionCachePath.new(self, {:action => :all}, false).path
write_fragment(cache_path, render_to_string(:json => Vendor.all))
end
Source that may help:
ActionCacheFilter
expire_action

How to create a thank you page?

I'm trying to create a thank you page, my route for this works fine since I test it the url and works just fine, however when I try to redirect in the create action I get:
Routing Error
No route matches {:action=>"thank_you", :locale=>:en, :controller=>"appointments"}
Controller
def create
#appointment = Appointment.new(params[:appointment])
if #appointment.save
#send email
AppointmentMailer.appointment_confirmation(#appointment).deliver
AppointmentMailer.new_appointment(#appointment).deliver
redirect_to :action => "thank_you"
else
render :action => 'new', :alert => #appointment.errors.full_messages.split(', ')
end
end
def thank_you
#appointment = Appointment.find(params[:id])
end
Route
resources :appointments, :except => :new do
member do
get :thank_you
end
end
You need to add it as a RESTful action (or assume a default matching route).
Nutshell:
resources :appointments do
member do
get 'thank_you'
end
end
Or:
resources :appointments do
get 'thank_you', :on => :member
end
For getting a new page, you have to do more than just editing the controller.
Edit config/routes.rb and include
match "/appointments/thank_you" => "appointments#thank_you"
You might want to insert render command in the controller, that brings you to a thank you view you have to create...

How to render validation errors on the page that submitted the form?

I've got a comments form in the article/show page. In this page, it displays the article and has a comments form.
When I submit a comment that has validation errors, I need it to go back to the article/show page, and display the errors there.
Should I change render :action => 'new' to something else?
In the Comment controller, I tried:
def create
...
if #comment.save?
redirect_to article_path(#comment.article), :notice => "Posted comment!"
else
# render :action => 'new'
render 'articles/show"
end
end
But this will complain, since the app won't know which article to show based on the ID.
EDIT: I found this solution. The approach would be to use a session to pass the errors instead. Is this the right way to go with this?
Try this
def create
...
if #comment.save?
redirect_to article_path(#comment.article), :notice => "Posted comment!"
else
# render :action => 'new'
#article = #comment.article
render 'articles/show"
end
end`
So fix your routing so the app does know what article to show based on the ID.

problem with RESTful forms in Rails 3

okay, so basically, I have a normal form for my model:
= form_for #operator do |f|
blah blah blah
In my operators controller, i have this:
def new
#operator = Operator.new
#operator.build_user
respond_to do |format|
format.html {}
end
end
def create
#user = User.create(params[:operator].delete(:user))
#user.update_attributes(:login => #user.email)
#operator = Operator.new(params[:operator].merge(:user => #user))
respond_to do |format|
if #operator.save
format.html {redirect_to new_operator_aircraft_path(#operator)}
else
format.html { render :action => "new", :error => #operator.errors }
end
end
end
very basic stuff. I have some validates_presence_of stuff in my model so naturally when I submit my form, it should show me that I have errors(and keep the fields I have filled up)
Right so far? yeah. The problem is, it seems I am posting to /operators and that's what renders. I seem to have forgotten about what happens in Rails2.3+ but shouldn't I be redirected to /operators/new again? or was that the intended behavior all along?
Here's what I think you are asking:
After I submit a form with errors, why does the URL
read "/operators" rather than
"/operators/new".
Thanks to resourceful routing, when submitting a form via POST to "/operators" the create action is called on the OperatorsController. If you encounter errors when saving your operator, you've instructed the controller to render the new action within the same request.
render :action => "new", :error => #operator.errors
This means a redirect is not occurring and therefore the URL remains "/operators".
If a redirect were to occur, you would lose all the state information of the #operator object in the current request, including the errors you encountered as well as the form values you just submitted.
In other words, working as intended.

Rails3 and Respond_with problem

I have an application, on which I have two user interfaces.
The first one is for normal users and the second one is for iphone users.
Everything was working fine until i refactored my code within controller to use the respond_with declarative instead of respond_to.
The application is still working for the html interface(:format => :html) but not on the iphone interface(:format => :iphone).
On the iphone, when I do the following action (:index, :new, :edit, :show) it works.
But when i do (:create, :update, :destroy), I get errors saying the template is not found(create.iphone.haml for example).
On my controller I have
respond_to :html, :iphone
And then for example, the edit and the update action
def edit
#refund = Refund.find(params[:id])
respond_with(#refund)
end
def update
#refund = Refund.find(params[:id])
if #refund.update_attributes(params[:refund])
flash[:notice] = 'Refund was successfully updated.'
end
respond_with(#refund, :location => project_refunds_path(#project))
end
In fact, I would like the :iphone format is handle as :html is ... and not by calling the to_format method as it is specified into the doc.
Solved it by myself.
Just need to add this to an initializer file :
ActionController::Responder.class_eval do
alias :to_iphone :to_html
end
What if you do:
respond_with(#refund, :location => project_refunds_path(#project)) do |format|
format.iphone { whatever you had here before refactoring }
end