select_or_label with custom form builder

In our web app, we have a common UI pattern: replacing select lists (eg.
drop downs) with a label if there is only one item in the list. For
example, when creating a subscription, the user must choose a plan.
Normally, there is a list of plans to choose from. However, if there is
only one plan that the user can choose, we show a label specifying the
one plan they’re getting instead of showing them a list of one.

We implemented this with a custom form builder and a method called
select_or_label. It takes the same arguments as select, but only
creates the select list if the list of choices has more than one

The view looks like:

<% form_for :subscription, :builder => SelectFormBuilder, :url => { :controller => :subscriptions, :action => :create } do |form| -%>
  <%= form.label :plan %>
  <%= form.select_or_label :plan_id, Plan.all.collect {|p| [,]} %>
<% end -%>

The form builder creates a span and a hidden_field if the size of the
choices is 1:

class SelectFormBuilder < ActionView::Helpers::FormBuilder
  include ActionView::Helpers::TagHelper

  def select_or_label(method, choices, options = {})
    if choices.size == 1
      content_tag(:span, choices.first.first) +
        hidden_field(method, :value => choices.first.last)
      select(method, choices, options)

And the spec:

require File.dirname(__FILE__) + '/../spec_helper'

describe SelectFormBuilder, :type => :helper do
  describe "select_or_label" do
    before do
      helper = { include ActionView::Helpers }.new
      @builder =,, helper, {}, nil)

    it "returns a span and hidden field if the size of the choices array is only one" do
      html = @builder.select_or_label(:plan_id, [["name", "id"]])
      html.should_not have_tag("select")

      html.should have_tag("span", "name")
      html.should have_tag("input[type=hidden][name=?][value=?]", "subscription[plan_id]", "id")

    it "returns a select if the size of the choices array is greater than one" do
      html = @builder.select_or_label(:plan_id, [["name", "id"], ["other name", "other_id"]])
      html.should_not have_tag("input")

      html.should have_tag("select[name=?]", "subscription[plan_id]") do
        with_tag("option[value=?]", "id", "name")
        with_tag("option[value=?]", "other_id", "other name")
Paul Gross

Paul Gross

I'm a lead software developer in Seattle working for Braintree Payments.

Read More