How to overcome the DoubleRender problem?

Hi all

I ran into this problem more than once.

Let’s say, a user logs into my site and clicks a link that only logged
in members can see. Because he likes the content of this specific site,
he places a link into his link list.
Some days later he clicks the link from his link list. Unfortunately his
session has expired so my code forwards him to the login page, but keeps
the link he tried to open in the session so after successful login the
user is redirected straight away to the wished site.
So the user logs successfully in. But during the days that have passed
the site has been moved to another URL, but this shouldn’t be a problem
because we just forward him to the new URL - and that’s where problems
strike! You get a DoubleRenderException or something like that!

Let’s recapitulate: The redirection was from (1) the login site to (2)
the link the user entered before to (3) the new location of the site.

I guess this is quite correct semantical code, but Rails does not let me
do it.

Did I miss anything? Maybe I mis-use the redirect_to method? Or how can
I accomplish the described behavior in Rails?

Thanks
Josh

This should not create a problem at all as I do it all the time. My
guess is that you are assuming that render and redirect_to immediately
return from a method, but they don’t. All redirect_to does is add a 302
header to the response object that is sent back to the browser. Without
seeing your code, all I can do is assume, but try changing the
redirect_to line to something like the following (notice the && return):

redirect_to(:controller => ‘foo’, :action => ‘bar’) && return

-Bill

On 10/28/07, William P. [email protected] wrote:

redirect_to(:controller => ‘foo’, :action => ‘bar’) && return

While this is perfectly valid syntax, I would recommend:

redirect_to(…)
return

because it is more clear what is going on. The && trick works –
unless redirect_to suddenly starts returning true.

–Michael

On 10/28/07, Michael G. [email protected] wrote:

because it is more clear what is going on. The && trick works –
unless redirect_to suddenly starts returning true.

Actually the return will happen as long as the redirect_to returns
anything but nil or false.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Gah, brain fart. :slight_smile:

The point is, && is “short-circuit” in that the first in the path that
fails causes the rest to not even run.

–Michael

You can also try out:

return redirect_to(…)

You can avoid the “short-circuit”.

  • Herry

On 29 Oct 2007, at 15:19, Joshua M. wrote:

else

As far as I can see you then trickle down to

model_obj.new_record? ?
render(:action => :new) :
render(:action => :edit)

Which renders a second time, hence the error. I can also see quite a
few other paths by which this could happen.

Fred

Herryanto S. wrote:

You can also try out:

return redirect_to(…)

You can avoid the “short-circuit”.

  • Herry

Hrm then I guess I’m having something substantially wrong, because I’m
always getting this error. So let’s track down what’s wrong…

The user types in the URL http://localhost:3000/admin/links/new

This routes to the controller Admin::LinksController and the action
“new” which is handled by a module I wrote once:

class Admin::LinksController < Admin::ApplicationController
include ActsAsManager
end

module ActsAsManager

def new
manage
end


end

This calls to the quite complex method “manage” (private one) - this
handles all the create/edit/update stuff that the normal scaffolds
would:

def manage
params[:id].nil? ?
self.model_obj = model_class.new :
self.model_obj = model_class.find(params[:id])

if request.get?
  member_requests('CREATE', model_class) do
    model_obj.attributes = params[model_obj_name]
  end
elsif request.post?
  model_obj.attributes = params[model_obj_name]

  if model_obj.new_record?
    member_requests!('CREATE', model_class) do
      model_obj.creator = logged_in_member if 

model_obj.respond_to?(‘creator=’)
model_obj.owner = logged_in_member if
model_obj.respond_to?(‘owner=’)
end
else
member_requests!(‘EDIT’, model_class)
end

  if model_obj.save
    if model_obj.new_record?
      flash[:notice] = model_obj_name.capitalize + ' was 

successfully created.’
redirect_to :action => :list and return
else
flash[:notice] = model_obj_name.capitalize + ’ was
successfully updated.’
redirect_to :action => :show, :id => model_obj and return
end
end
else
raise “Another method than GET or POST?! Woah!”
end
model_obj.new_record? ?
render(:action => :new) :
render(:action => :edit)

If the form contains a hidden field +lock_version+ then ActiveRecord

throws a StaleObjectError if its value has changed since the form was
called (positive locking).

TODO: Does this work anyhow anymore?!

rescue ActiveRecord::StaleObjectError
# TODO: Evaluate differences and add related errors
self.model_obj.errors.add(:base, “Object is stale!
#{self.model_obj.lock_version}:#{model_class.find_by_id(self.model_obj.id).lock_version}”)
render :action => :edit
end

So far, we’re getting to line where the member requests permission to
CREATE an item:


if request.get?
member_requests(‘CREATE’, model_class) do

which calls the method member_requests():

def member_requests(right_name, obj, options = {}, &block)
if
logged_in_member.has_system_right?(SystemRight.find_by_model_class_and_name(obj.to_s,
right_name))
yield if block_given?
else
render :file => “#{RAILS_ROOT}/public/404.html”, :layout => true,
:status => 404
end
end

And that’s where the DoubleRenderError is thrown! Anyone can see where
the problem is? I just want to render the 404.html file, nothing else…
No double rendering or stuff! :stuck_out_tongue:

Thanks a lot
Josh

On 29 Oct 2007, at 18:31, Joshua M. wrote:

Which renders a second time, hence the error.
Thanks!

I can also see quite a
few other paths by which this could happen.
What do you mean with that?

that there are other cases. eg member_requests rendering and you
later redirecting.

Frederick C. wrote:

On 29 Oct 2007, at 18:31, Joshua M. wrote:

Which renders a second time, hence the error.
Thanks!

I can also see quite a
few other paths by which this could happen.
What do you mean with that?

that there are other cases. eg member_requests rendering and you
later redirecting.

Hrm OK, so could you please tell me the key Do’s and Don’ts how to use
redirect_to and render correctly? Thanks :slight_smile:

It’s dead simple. If you’ve called render or redirect_to once in an
action, make sure you don’t do either of them again.

Frederick C. wrote:

On 29 Oct 2007, at 15:19, Joshua M. wrote:

else

As far as I can see you then trickle down to

model_obj.new_record? ?
render(:action => :new) :
render(:action => :edit)

Which renders a second time, hence the error.
Thanks!

I can also see quite a
few other paths by which this could happen.
What do you mean with that?

Frederick C. wrote:

It’s dead simple. If you’ve called render or redirect_to once in an
action, make sure you don’t do either of them again.

I like dead simple things. Thanks a lot :slight_smile: