class SimpleForm::FormBuilder

Constants

ACTIONS

When action is create or update, we still should use new and edit

ATTRIBUTE_COMPONENTS

Attributes

object[R]
object_name[R]
template[R]
wrapper[R]

Public Class Methods

discovery_cache() click to toggle source
# File lib/simple_form/form_builder.rb, line 35
def self.discovery_cache
  @discovery_cache ||= {}
end

Public Instance Methods

association(association, options = {}, &block) click to toggle source

Helper for dealing with association selects/radios, generating the collection automatically. It's just a wrapper to input, so all options supported in input are also supported by association. Some extra options can also be given:

Examples

simple_form_for @user do |f|
  f.association :company          # Company.all
end

f.association :company, collection: Company.all(order: 'name')
# Same as using :order option, but overriding collection

Block

When a block is given, association simple behaves as a proxy to simple_fields_for:

f.association :company do |c|
  c.input :name
  c.input :type
end

From the options above, only :collection can also be supplied.

Please note that the association helper is currently only tested with Active Record. Depending on the ORM you are using your mileage may vary.

# File lib/simple_form/form_builder.rb, line 176
def association(association, options = {}, &block)
  options = options.dup

  return simple_fields_for(*[association,
    options.delete(:collection), options].compact, &block) if block_given?

  raise ArgumentError, "Association cannot be used in forms not associated with an object" unless @object

  reflection = find_association_reflection(association)
  raise "Association #{association.inspect} not found" unless reflection

  options[:as] ||= :select
  options[:collection] ||= fetch_association_collection(reflection, options)

  attribute = build_association_attribute(reflection, association, options)

  input(attribute, options.merge(reflection: reflection))
end
attribute(attribute_name, options = {}, &block)
Alias for: input
button(type, *args, &block) click to toggle source
# File lib/simple_form/form_builder.rb, line 206
def button(type, *args, &block)
  options = args.extract_options!.dup
  options[:class] = [SimpleForm.button_class, options[:class]].compact
  args << options
  if respond_to?(:"#{type}_button")
    send(:"#{type}_button", *args, &block)
  else
    send(type, *args, &block)
  end
end
Also aliased as: button_button
button_button(type, *args, &block)

Creates a button:

form_for @user do |f|
  f.button :submit
end

It just acts as a proxy to method name given. We also alias original Rails button implementation (3.2 forward (to delegate to the original when calling `f.button :button`.

Alias for: button
collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block) click to toggle source

Creates a collection of check boxes for each item in the collection, associated with a clickable label. Use value_method and text_method to convert items in the collection for use as text/value in check boxes. You can give a symbol or a proc to both value_method and text_method, that will be evaluated for each item in the collection.

Examples

form_for @user do |f|
  f.collection_check_boxes :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
end

<input name="user[options][]" type="hidden" value="" />
<input id="user_options_true" name="user[options][]" type="checkbox" value="true" />
<label class="collection_check_boxes" for="user_options_true">Yes</label>
<input name="user[options][]" type="hidden" value="" />
<input id="user_options_false" name="user[options][]" type="checkbox" value="false" />
<label class="collection_check_boxes" for="user_options_false">No</label>

It is also possible to give a block that should generate the check box + label. To wrap the check box with the label, for instance:

form_for @user do |f|
  f.collection_check_boxes(
    :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
  ) do |b|
    b.label { b.check_box + b.text }
  end
end

Options

Collection check box accepts some extra options:

* checked  => the value or values that should be checked initially. Accepts
              a single item or an array of items. It overrides existing associations.

* disabled => the value or values that should be disabled. Accepts a single
              item or an array of items.

* collection_wrapper_tag   => the tag to wrap the entire collection.

* collection_wrapper_class => the CSS class to use for collection_wrapper_tag. This option
                              is ignored if the :collection_wrapper_tag option is blank.

* item_wrapper_tag         => the tag to wrap each item in the collection.

* item_wrapper_class       => the CSS class to use for item_wrapper_tag

* a block                  => to generate the label + check box or any other component.
# File lib/simple_form/form_builder.rb, line 420
def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
  SimpleForm::Tags::CollectionCheckBoxes.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)).render(&block)
end
collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block) click to toggle source

Create a collection of radio inputs for the attribute. Basically this helper will create a radio input associated with a label for each text/value option in the collection, using value_method and text_method to convert these text/value. You can give a symbol or a proc to both value_method and text_method, that will be evaluated for each item in the collection.

Examples

form_for @user do |f|
  f.collection_radio_buttons :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
end

<input id="user_options_true" name="user[options]" type="radio" value="true" />
<label class="collection_radio_buttons" for="user_options_true">Yes</label>
<input id="user_options_false" name="user[options]" type="radio" value="false" />
<label class="collection_radio_buttons" for="user_options_false">No</label>

It is also possible to give a block that should generate the radio + label. To wrap the radio with the label, for instance:

form_for @user do |f|
  f.collection_radio_buttons(
    :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
  ) do |b|
    b.label { b.radio_button + b.text }
  end
end

Options

Collection radio accepts some extra options:

* checked  => the value that should be checked initially.

* disabled => the value or values that should be disabled. Accepts a single
              item or an array of items.

* collection_wrapper_tag   => the tag to wrap the entire collection.

* collection_wrapper_class => the CSS class to use for collection_wrapper_tag

* item_wrapper_tag         => the tag to wrap each item in the collection.

* item_wrapper_class       => the CSS class to use for item_wrapper_tag

* a block                  => to generate the label + radio or any other component.
# File lib/simple_form/form_builder.rb, line 366
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
  SimpleForm::Tags::CollectionRadioButtons.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)).render(&block)
end
error(attribute_name, options = {}) click to toggle source

Creates an error tag based on the given attribute, only when the attribute contains errors. All the given options are sent as :error_html.

Examples

f.error :name
f.error :name, id: "cool_error"
# File lib/simple_form/form_builder.rb, line 225
def error(attribute_name, options = {})
  options = options.dup

  options[:error_html] = options.except(:error_tag, :error_prefix, :error_method)
  column      = find_attribute_column(attribute_name)
  input_type  = default_input_type(attribute_name, column, options)
  wrapper.find(:error).
    render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
end
error_notification(options = {}) click to toggle source

Creates an error notification message that only appears when the form object has some error. You can give a specific message with the :message option, otherwise it will look for a message using I18n. All other options given are passed straight as html options to the html tag.

Examples

f.error_notification
f.error_notification message: 'Something went wrong'
f.error_notification id: 'user_error_message', class: 'form_error'
# File lib/simple_form/form_builder.rb, line 315
def error_notification(options = {})
  SimpleForm::ErrorNotification.new(self, options).render
end
full_error(attribute_name, options = {}) click to toggle source

Return the error but also considering its name. This is used when errors for a hidden field need to be shown.

Examples

f.full_error :token #=> <span class="error">Token is invalid</span>
# File lib/simple_form/form_builder.rb, line 242
def full_error(attribute_name, options = {})
  options = options.dup

  options[:error_prefix] ||= if object.class.respond_to?(:human_attribute_name)
    object.class.human_attribute_name(attribute_name.to_s)
  else
    attribute_name.to_s.humanize
  end

  error(attribute_name, options)
end
hint(attribute_name, options = {}) click to toggle source

Creates a hint tag for the given attribute. Accepts a symbol indicating an attribute for I18n lookup or a string. All the given options are sent as :hint_html.

Examples

f.hint :name # Do I18n lookup
f.hint :name, id: "cool_hint"
f.hint "Don't forget to accept this"
# File lib/simple_form/form_builder.rb, line 264
def hint(attribute_name, options = {})
  options = options.dup

  options[:hint_html] = options.except(:hint_tag, :hint)
  if attribute_name.is_a?(String)
    options[:hint] = attribute_name
    attribute_name, column, input_type = nil, nil, nil
  else
    column      = find_attribute_column(attribute_name)
    input_type  = default_input_type(attribute_name, column, options)
  end

  wrapper.find(:hint).
    render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
end
input(attribute_name, options = {}, &block) click to toggle source

Basic input helper, combines all components in the stack to generate input html based on options the user define and some guesses through database column information. By default a call to input will generate label + input + hint (when defined) + errors (when exists), and all can be configured inside a wrapper html.

Examples

# Imagine @user has error "can't be blank" on name
simple_form_for @user do |f|
  f.input :name, hint: 'My hint'
end

This is the output html (only the input portion, not the form):

<label class="string required" for="user_name">
  <abbr title="required">*</abbr> Super User Name!
</label>
<input class="string required" id="user_name" maxlength="100"
   name="user[name]" type="text" value="Carlos" />
<span class="hint">My hint</span>
<span class="error">can't be blank</span>

Each database type will render a default input, based on some mappings and heuristic to determine which is the best option.

You have some options for the input to enable/disable some functions:

as: allows you to define the input type you want, for instance you
       can use it to generate a text field for a date column.

required: defines whether this attribute is required or not. True
            by default.

The fact SimpleForm is built in components allow the interface to be unified. So, for instance, if you need to disable :hint for a given input, you can pass hint: false. The same works for :error, :label and :wrapper.

Besides the html for any component can be changed. So, if you want to change the label html you just need to give a hash to :label_html. To configure the input html, supply :input_html instead and so on.

Options

Some inputs, as datetime, time and select allow you to give extra options, like prompt and/or include blank. Such options are given in plainly:

f.input :created_at, include_blank: true

Collection

When playing with collections (:radio_buttons, :check_boxes and :select inputs), you have three extra options:

collection: use to determine the collection to generate the radio or select

label_method: the method to apply on the array collection to get the label

value_method: the method to apply on the array collection to get the value

Priority

Some inputs, as :time_zone and :country accepts a :priority option. If none is given SimpleForm.time_zone_priority and SimpleForm.country_priority are used respectively.

# File lib/simple_form/form_builder.rb, line 110
def input(attribute_name, options = {}, &block)
  options = @defaults.deep_dup.deep_merge(options) if @defaults

  input   = find_input(attribute_name, options, &block)
  wrapper = find_wrapper(input.input_type, options)

  wrapper.render input
end
Also aliased as: attribute
input_field(attribute_name, options = {}) click to toggle source

Creates a input tag for the given attribute. All the given options are sent as :input_html.

Examples

simple_form_for @user do |f|
  f.input_field :name
end

This is the output html (only the input portion, not the form):

<input class="string required" id="user_name" maxlength="100"
   name="user[name]" type="text" value="Carlos" />
# File lib/simple_form/form_builder.rb, line 134
def input_field(attribute_name, options = {})
  components = (wrapper.components.map(&:namespace) & ATTRIBUTE_COMPONENTS)

  options = options.dup
  options[:input_html] = options.except(:as, :boolean_style, :collection, :label_method, :value_method, *components)
  options = @defaults.deep_dup.deep_merge(options) if @defaults

  input      = find_input(attribute_name, options)
  wrapper    = find_wrapper(input.input_type, options)
  components = components.concat([:input]).map { |component| SimpleForm::Wrappers::Leaf.new(component) }

  SimpleForm::Wrappers::Root.new(components, wrapper.options.merge(wrapper: false)).render input
end
label(attribute_name, *args) click to toggle source

Creates a default label tag for the given attribute. You can give a label through the :label option or using i18n. All the given options are sent as :label_html.

Examples

f.label :name                     # Do I18n lookup
f.label :name, "Name"             # Same behavior as Rails, do not add required tag
f.label :name, label: "Name"   # Same as above, but adds required tag

f.label :name, required: false
f.label :name, id: "cool_label"
Calls superclass method
# File lib/simple_form/form_builder.rb, line 293
def label(attribute_name, *args)
  return super if args.first.is_a?(String) || block_given?

  options = args.extract_options!.dup
  options[:label_html] = options.except(:label, :required, :as)

  column      = find_attribute_column(attribute_name)
  input_type  = default_input_type(attribute_name, column, options)
  SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options).label
end

Private Instance Methods

attempt_mapping(mapping, at) click to toggle source
# File lib/simple_form/form_builder.rb, line 617
def attempt_mapping(mapping, at)
  return if SimpleForm.inputs_discovery == false && at == Object

  begin
    at.const_get(mapping)
  rescue NameError => e
    raise if e.message !~ /#{mapping}$/
  end
end
attempt_mapping_with_custom_namespace(input_name) click to toggle source
# File lib/simple_form/form_builder.rb, line 627
def attempt_mapping_with_custom_namespace(input_name)
  SimpleForm.custom_inputs_namespaces.each do |namespace|
    if (mapping = attempt_mapping(input_name, namespace.constantize))
      return mapping
    end
  end

  nil
end
build_association_attribute(reflection, association, options) click to toggle source
# File lib/simple_form/form_builder.rb, line 473
def build_association_attribute(reflection, association, options)
  case reflection.macro
  when :belongs_to
    (reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
  when :has_one
    raise ArgumentError, ":has_one associations are not supported by f.association"
  else
    if options[:as] == :select
      html_options = options[:input_html] ||= {}
      html_options[:multiple] = true unless html_options.key?(:multiple)
    end

    # Force the association to be preloaded for performance.
    if options[:preload] != false && object.respond_to?(association)
      target = object.send(association)
      target.to_a if target.respond_to?(:to_a)
    end

    :"#{reflection.name.to_s.singularize}_ids"
  end
end
default_input_type(attribute_name, column, options) click to toggle source

Attempt to guess the better input type given the defined options. By default alwayls fallback to the user :as option, or to a :select when a collection is given.

# File lib/simple_form/form_builder.rb, line 510
def default_input_type(attribute_name, column, options)
  return options[:as].to_sym if options[:as]
  custom_type = find_custom_type(attribute_name.to_s) and return custom_type
  return :select             if options[:collection]

  input_type = column.try(:type)
  case input_type
  when :timestamp
    :datetime
  when :string, nil
    case attribute_name.to_s
    when /password/  then :password
    when /time_zone/ then :time_zone
    when /country/   then :country
    when /email/     then :email
    when /phone/     then :tel
    when /url/       then :url
    else
      file_method?(attribute_name) ? :file : (input_type || :string)
    end
  else
    input_type
  end
end
discovery_cache() click to toggle source

If cache_discovery is enabled, use the class level cache that persists between requests, otherwise use the instance one.

# File lib/simple_form/form_builder.rb, line 600
def discovery_cache
  if SimpleForm.cache_discovery
    self.class.discovery_cache
  else
    @discovery_cache ||= {}
  end
end
fetch_association_collection(reflection, options) click to toggle source
# File lib/simple_form/form_builder.rb, line 454
def fetch_association_collection(reflection, options)
  options.fetch(:collection) do
    relation = reflection.klass.all

    if reflection.respond_to?(:scope) && reflection.scope
      relation = reflection.klass.instance_exec(&reflection.scope)
    else
      order = reflection.options[:order]
      conditions = reflection.options[:conditions]
      conditions = object.instance_exec(&conditions) if conditions.respond_to?(:call)

      relation = relation.where(conditions)
      relation = relation.order(order) if relation.respond_to?(:order)
    end

    relation
  end
end
file_method?(attribute_name) click to toggle source
# File lib/simple_form/form_builder.rb, line 541
def file_method?(attribute_name)
  file = @object.send(attribute_name) if @object.respond_to?(attribute_name)
  file && SimpleForm.file_methods.any? { |m| file.respond_to?(m) }
end
find_association_reflection(association) click to toggle source
# File lib/simple_form/form_builder.rb, line 552
def find_association_reflection(association)
  if @object.class.respond_to?(:reflect_on_association)
    @object.class.reflect_on_association(association)
  end
end
find_attribute_column(attribute_name) click to toggle source
# File lib/simple_form/form_builder.rb, line 546
def find_attribute_column(attribute_name)
  if @object.respond_to?(:column_for_attribute) && @object.has_attribute?(attribute_name)
    @object.column_for_attribute(attribute_name)
  end
end
find_custom_type(attribute_name) click to toggle source
# File lib/simple_form/form_builder.rb, line 535
def find_custom_type(attribute_name)
  SimpleForm.input_mappings.find { |match, type|
    attribute_name =~ match
  }.try(:last) if SimpleForm.input_mappings
end
find_input(attribute_name, options = {}, &block) click to toggle source

Find an input based on the attribute name.

# File lib/simple_form/form_builder.rb, line 496
def find_input(attribute_name, options = {}, &block)
  column     = find_attribute_column(attribute_name)
  input_type = default_input_type(attribute_name, column, options)

  if block_given?
    SimpleForm::Inputs::BlockInput.new(self, attribute_name, column, input_type, options, &block)
  else
    find_mapping(input_type).new(self, attribute_name, column, input_type, options)
  end
end
find_mapping(input_type) click to toggle source

Attempts to find a mapping. It follows the following rules:

1) It tries to find a registered mapping, if succeeds:

a) Try to find an alternative with the same name in the Object scope
b) Or use the found mapping

2) If not, fallbacks to #{input_type}Input 3) If not, fallbacks to SimpleForm::Inputs::#{input_type}Input

# File lib/simple_form/form_builder.rb, line 565
def find_mapping(input_type)
  discovery_cache[input_type] ||=
    if mapping = self.class.mappings[input_type]
      mapping_override(mapping) || mapping
    else
      camelized = "#{input_type.to_s.camelize}Input"
      attempt_mapping_with_custom_namespace(camelized) ||
        attempt_mapping(camelized, Object) ||
        attempt_mapping(camelized, self.class) ||
        raise("No input found for #{input_type}")
    end
end
find_wrapper(input_type, options) click to toggle source
# File lib/simple_form/form_builder.rb, line 590
def find_wrapper(input_type, options)
  if name = options[:wrapper] || find_wrapper_mapping(input_type)
    name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
  else
    wrapper
  end
end
find_wrapper_mapping(input_type) click to toggle source

Attempts to find a wrapper mapping. It follows the following rules:

1) It tries to find a wrapper for the current form 2) If not, it tries to find a config

# File lib/simple_form/form_builder.rb, line 582
def find_wrapper_mapping(input_type)
  if options[:wrapper_mappings] && options[:wrapper_mappings][input_type]
    options[:wrapper_mappings][input_type]
  else
    SimpleForm.wrapper_mappings && SimpleForm.wrapper_mappings[input_type]
  end
end
mapping_override(klass) click to toggle source
# File lib/simple_form/form_builder.rb, line 608
def mapping_override(klass)
  name = klass.name
  if name =~ /^SimpleForm::Inputs/
    input_name = name.split("::").last
    attempt_mapping_with_custom_namespace(input_name) ||
      attempt_mapping(input_name, Object)
  end
end