she's a beauty

Jared Carroll

Recently I was writing an action in one of my controllers that needed to do a POST to a remote API.

One of the parameters in that POST was a block of HTML.

The first pass was similar to this (ignoring execption handling for the remote call for now):

class UsersController < ApplicationController

  def create
    @user = User.new params[:user]
    if @user.save
      markup =<<-EOS
        <h2>John Pepper</h2>
        Makes amazing [burritos.](http://www.boloco.com)
        Is in touch with the blogosphere.
      EOS
      RemoteApi.update_profile :markup => markup
      redirect_to user_path(@user)
    else
      render :action => :new
    end
  end

end

I don’t like that HTML in my controller. Plus that <a> tag is terrible, I want to use #link_to but that’s not available in controllers (and it shouldnt’ be).

Second pass:

class UsersController < ApplicationController

  def create
    @user = User.new params[:user]
    if @user.save
      RemoteApi.update_profile
        :markup => render(:partial => 'user')
      redirect_to user_path(@user)
    else
      render :action => :new
    end
  end

end

In user.rhtml:

<h2>John Pepper</h2>
Makes amazing <%= link_to 'burritos', 'http://www.boloco.com' %>.
Is in touch with the blogosphere.

That’s better. I got the HTML out of the controller and I can use #link_to instead of an <a> tag.

But wait: ActionController::DoubleRenderError. You can’t call render and/or redirect multiple times in an action (as a side, whoever wrote ActionController::DoubleRenderError is awesome - that is the best error message of any Rails exception).

After looking through the docs i found ActionController::Base#render_to_string. This beauty is the same as #render but does not actually send the result as the response body i.e. it doesn’t count as a #render call in an action, allowing you to avoid the ActionController::DoubleRenderError.

Third pass:

class UsersController < ApplicationController

  def create
    @user = User.new params[:user]
    if @user.save
      RemoteApi.update_profile
        :markup => render_to_string(:partial => 'user')
      redirect_to user_path(@user)
    else
      render :action => :new
    end
  end

In user.rhtml:

<h2>John Pepper</h2>
Makes amazing <%= link_to 'burritos', 'http://www.boloco.com' %>.
Is in touch with the blogosphere.

Sweet. Exactly what I wanted.

Someone else here at t-bot recommended using this strategy for flash messages. Since flash messages are just text, whenever they get more complicated than 1 sentence it might be good to put them in a partial to keep all that view code out of your controller, #render_to_string would allow you to do that.