Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

render_to_string :json (or :xml or :update) before render results in DoubleRenderError

#5875

In Rails3 render_to_string :json => ... (and :xml and :udpate) alters the response_body and thus makes subsequent render calls raise a DoubleRenderError

The following example works in Rails 2.3.8 but doesn't in Rails 3.0.1

class TestController < ApplicationController
  def test
    render :text => render_to_string(:json => "[]")
  end
end

Reported by aduffeck · October 28th, 2010 @ 01:22 PM

State: resolved
Milestone: 3.x
Assigned to: José Valim José Valim
Importance: Low

Activity

  1. Jeff Kreeftmeijer
    Jeff Kreeftmeijer
    • Importance changed from to Low

    Automatic cleanup of spam.

    November 1st, 2010 @ 05:03 PM

  2. Bob Lail
    Bob Lail
    • Tag set to actioncontroller workaround doublerendererror render bug

    Although abstract_controller/rendering.rb:91 would lead you to believe that response_body is only set when you call render (and not when you call render_to_string), that isn't the case in Rails 3.0.3. If you try an experiment along the following lines, you'll see that response_body has been written:

    def index
      test = render_to_string(:template => "main/index")
      Rails.logger.info response_body
      # ...
    end
    

    Because response_body is not nil, action_controller/metal/rendering.rb:16 raises AbstractController::DoubleRenderError. You'd think that you can refactor your example like this:

    class TestController < ApplicationController
      def test
        json = render_to_string(:json => "[]")
        response_body = nil
        render :text => json
      end
    end
    

    but there's a catch. There's a second response_body accessor! It's in abstract_controller/base.rb:14. So what you'd actually write is:

    class TestController < ApplicationController
      def test
        json = render_to_string(:json => "[]")
        @_response_body = nil
        render :text => json
      end
    end
    

    That should serve as a workaround for now.

    December 30th, 2010 @ 08:31 PM

  3. Neeraj Singh
    Neeraj Singh
    • State changed from new to open
    • Milestone set to 3.x
    • Tag changed from actioncontroller workaround doublerendererror render bug to actioncontroller workaround doublerendererror render bug, patched
    • Assigned user set to José Valim

    This is an edge case. You will run into this error only if you are using

    render_to_string(:json => '')
    render_to_string(:xml => '')
    render_to_string(:js => '')
    

    That is because of following code

        add :json do |json, options|
          json = json.to_json(options) unless json.kind_of?(String)
          json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
          self.content_type ||= Mime::JSON
          self.response_body  = json
        end
    
        add :js do |js, options|
          self.content_type ||= Mime::JS
          self.response_body  = js.respond_to?(:to_js) ? js.to_js(options) : js
        end
    
        add :xml do |xml, options|
          self.content_type ||= Mime::XML
          self.response_body  = xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
        end
    

    I would say that render_to_string should always ensure that response_body is set to nil. I have attached a patch and a test.

    January 21st, 2011 @ 07:21 PM

  4. Neeraj Singh
    Neeraj Singh

    LH did not attache my patch last time.

    January 21st, 2011 @ 07:23 PM

  5. José Valim
    José Valim

    Patch seems to be empty. Neeraj, just tell me the commit hash in your fork please. :)

    January 21st, 2011 @ 07:24 PM

  6. José Valim
    José Valim

    @BobLail, your first response_body does not work because you are creating a local variable instead of calling a method. "self.response_body = nil" should do the trick.

    January 21st, 2011 @ 07:25 PM

  7. Neeraj Singh
  8. José Valim
    José Valim

    Neeraj, do we need to manipulate the instance variable directly? Couldn't we simply call self.response_body = nil?

    January 21st, 2011 @ 07:34 PM

  9. Neeraj Singh
    Neeraj Singh

    My tests were failing with setting self.response_body = nil

    def render_to_string
    .....
    self.response_body = nil
    end
    
    #/metal/rendering.rb
    puts response_body #=> [nil]
    raise ::AbstractController::DoubleRenderError if response_body
    

    I did not dig into why it is this way. It worked with @_response_body = nil and I left it. Guess I was being lazy.

    Will look into it later.

    January 21st, 2011 @ 08:05 PM

  10. Neeraj Singh
    Neeraj Singh

    Setting self.response_body = nil become [ nil ] because of this line of code

       # action_controller/metal.rb
       def response_body=(val)
          body = val.respond_to?(:each) ? val : [val]
          super body
        end
    

    January 21st, 2011 @ 08:49 PM

  11. José Valim
    José Valim

    We could also change this file to properly handle nil. Wdyt?

    January 21st, 2011 @ 09:46 PM

  12. Neeraj Singh
  13. Bob Lail
    Bob Lail

    (oops. Good call, José.)

    January 22nd, 2011 @ 06:52 PM

  14. Bob Lail
    Bob Lail

    (oops. Good call, José.)

    January 22nd, 2011 @ 06:52 PM

  15. José Valim
    José Valim
    • State changed from open to resolved

    Applied, thanks Neeraj!

    January 25th, 2011 @ 07:14 PM

  16. bingbing