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.

Jared Carroll

Pair with one of our expert developers to level up your skills with Coaching by thoughtbot. Save time learning best practices and techniques for reducing technical debt in Ember, Ruby, Haskell, and Go in 1-on-1 sessions tailored to your goals.