Skip to content

Instantly share code, notes, and snippets.

@seyhunak
Created January 28, 2012 17:58
Show Gist options
  • Save seyhunak/1695275 to your computer and use it in GitHub Desktop.
Save seyhunak/1695275 to your computer and use it in GitHub Desktop.
Makes building forms that follow Twitter Bootstrap 2.0's conventions easier
class BootstrapFormBuilder < ActionView::Helpers::FormBuilder
def control_group(method, *args, &block)
options = args.extract_options!
error_class = object.errors[method].present? ? 'error' : ''
@template.field_set_tag(nil, class: ['control-group', error_class].join(' ')) do
label(method, options[:label], class: 'control-label') +
@template.content_tag(:div, class: 'controls') do
doc = Nokogiri::HTML::DocumentFragment.parse(@template.capture(&block))
if help_block = doc.at_css('.help-block')
help_block.before inline_errors(method)
else
doc.add_child inline_errors(method)
end
doc.to_html.html_safe
end
end
end
private
def inline_errors(method)
if object.errors[method].present?
@template.content_tag :span, object.errors[method].to_sentence, class: 'help-inline'
else
''
end
end
def self.with_custom_field_error_proc(&block)
default_field_error_proc = ::ActionView::Base.field_error_proc
::ActionView::Base.field_error_proc = ->(html_tag, instance) { html_tag }
yield
ensure
::ActionView::Base.field_error_proc = default_field_error_proc
end
module FormHelper
# in ApplicationHelper, include BootstrapFormBuilder::FormHelper
def bootstrap_form_for(object, options = {}, &block)
options[:builder] = BootstrapFormBuilder
BootstrapFormBuilder.with_custom_field_error_proc do
form_for(object, options, &block)
end
end
end
end
require 'spec_helper'
describe 'BootstrapFormBuilder' do
let(:object) do
class BootstrapFormBuilderSpecObject
include ActiveModel::Validations
validates :name, presence: true
validates :age, numericality: true
attr_accessor :name, :age
def initialize; @name, @age = 'John Doe', 30; end
end
BootstrapFormBuilderSpecObject.new
end
let(:builder) { BootstrapFormBuilder.new(:dummy, object, self, {}, nil) }
let(:control_group) do
Capybara.string(builder.control_group(:name) do
builder.text_field(:name) +
content_tag(:p, 'a helpful message', class: 'help-block')
end)
end
describe '#control_group' do
it 'follows the Twitter Bootstrap 2.0 control group structure' do
control_group.find('fieldset.control-group') do |html|
html.should have_selector 'label.control-label:first-of-type'
html.should have_selector 'div.controls:first-of-type'
end
end
describe 'label.control-label' do
it 'contains the humanized attribute name' do
human_attribute_name = builder.object.class.human_attribute_name(:name)
control_group.find('label.control-label').should have_content human_attribute_name
end
it 'can be overridden' do
control_group = Capybara.string(builder.control_group(:name, label: 'Your name'){})
control_group.find('label.control-label').should have_content 'Your name'
end
end
describe 'div.controls' do
it 'contains the content of the passed block' do
control_group.find('div.controls') do |html|
html.find('input[type="text"]#dummy_name:first-of-type').value.should eq object.name
html.find('p.help-block:first-of-type').should have_content 'a helpful message'
end
end
end
context 'with errors' do
before do
object.name = nil; object.valid?
end
let(:builder) { BootstrapFormBuilder.new(:dummy, object, self, {}, nil) }
let(:control_group) do
Capybara.string(builder.control_group(:name) do
builder.text_field(:name) +
content_tag(:p, 'a helpful message', class: 'help-block')
end)
end
describe 'fieldset.control-group' do
it { control_group.should have_selector 'fieldset.control-group.error' }
end
describe 'div.controls' do
it { control_group.should have_selector 'div.controls p.help-block:last-child' }
end
describe 'span.help-inline' do
end
end
end
end
@seyhunak
Copy link
Author

Usage

<%= bootstrap_form_for @user, html: {class: 'form-horizontal'} do |f| %>
  <legend>Create an Account</legend>

    <%= f.control_group :email do %>
     <%= f.email_field :email %>
   <% end %>

   <%= f.control_group :password do %>
     <%= f.text_field :password %>
     <p class="help-block">Must be at least 6 characters</p>
   <% end %>

   <%= f.control_group :full_name do %>
     <%= f.text_field :full_name %>
   <% end %>

   <%= field_set_tag nil, class: 'form-actions' do %>
     <%= button_tag 'Create account', type: 'submit', class: 'btn primary' %>
     <%= link_to 'Cancel', root_path %>
   <% end %>
<% end %>

@seyhunak
Copy link
Author

Custom labels

<%= f.control_group :full_name, label: 'Your first and last name' do %>
    <%= f.text_field :full_name %>
<% end %>

@asavartsov
Copy link

Wow! Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment