根据官方github描述:

By default, Simple Form will look at the column type in the database and use an appropriate input for the column. For example, a column created with type :text in the database will use a textarea input by default. See the section Available input types and defaults for each column type for a complete list of defaults


那么这个匹配是如何进行的呢?跟踪试试:先在view中的simple_form的block内加binding.pry断点,然后

[7] pry(#<#>)> binding.trace_tree(htmp: 'rails/simple_form_input'){f.input :name}
=> "<div class="\"input" string="" optional="" student_name\"=""><label class="\"string" optional\"="" for="\"student_name\"">Name</label><input class="\"string" optional\"="" type="\"text\"" name="\"student[name]\"" id="\"student_name\""/></div>"


便可知其基本流程如下


完整调用栈如下

20170709_222546_071_simple_form_input.html

其关键在于default_input_type

# simple_form-3.5.0/lib/simple_form/form_builder.rb
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


大概就是这样了,至于想看各种input type的源码?可从这里入手:discovery_cache其实只是个缓存,真正保存所有input type的,是mappings,显然它应该是个hash

# simple_form-3.5.0/lib/simple_form/map_type.rb
require 'active_support/core_ext/class/attribute'

module SimpleForm
  module MapType
    def self.extended(base)
      base.class_attribute :mappings
      base.mappings = {}
    end

    def map_type(*types)
      map_to = types.extract_options![:to]
      raise ArgumentError, "You need to give :to as option to map_type" unless map_to
      self.mappings = mappings.merge types.each_with_object({}) { |t, m| m[t] = map_to }
    end
  end
end


然后,定义各种匹配关系,这里还包括了check_boxes等等似乎不是schema的东西

# simple_form-3.5.0/lib/simple_form/form_builder.rb
module SimpleForm
  class FormBuilder < ActionView::Helpers::FormBuilder
    attr_reader :template, :object_name, :object, :wrapper
    # When action is create or update, we still should use new and edit

    ACTIONS = {
      'create' => 'new',
      'update' => 'edit'
    }

    ATTRIBUTE_COMPONENTS = [:html5, :min_max, :maxlength, :minlength, :placeholder, :pattern, :readonly]

    extend MapType
    include SimpleForm::Inputs

    map_type :text,                                       to: SimpleForm::Inputs::TextInput
    map_type :file,                                       to: SimpleForm::Inputs::FileInput
    map_type :string, :email, :search, :tel, :url, :uuid, to: SimpleForm::Inputs::StringInput
    map_type :password,                                   to: SimpleForm::Inputs::PasswordInput
    map_type :integer, :decimal, :float,                  to: SimpleForm::Inputs::NumericInput
    map_type :range,                                      to: SimpleForm::Inputs::RangeInput
    map_type :check_boxes,                                to: SimpleForm::Inputs::CollectionCheckBoxesInput
    map_type :radio_buttons,                              to: SimpleForm::Inputs::CollectionRadioButtonsInput
    map_type :select,                                     to: SimpleForm::Inputs::CollectionSelectInput
    map_type :grouped_select,                             to: SimpleForm::Inputs::GroupedCollectionSelectInput
    map_type :date, :time, :datetime,                     to: SimpleForm::Inputs::DateTimeInput
    map_type :country, :time_zone,                        to: SimpleForm::Inputs::PriorityInput
    map_type :boolean,                                    to: SimpleForm::Inputs::BooleanInput
    map_type :hidden,                                     to: SimpleForm::Inputs::HiddenInput

    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


完整的匹配关系也可这样查看

[3] pry(#<#>)> f.mappings
=> {:text=>SimpleForm::Inputs::TextInput,
 :file=>SimpleForm::Inputs::FileInput,
 :string=>SimpleForm::Inputs::StringInput,
 :email=>SimpleForm::Inputs::StringInput,
 :search=>SimpleForm::Inputs::StringInput,
 :tel=>SimpleForm::Inputs::StringInput,
 :url=>SimpleForm::Inputs::StringInput,
 :uuid=>SimpleForm::Inputs::StringInput,
 :password=>SimpleForm::Inputs::PasswordInput,
 :integer=>SimpleForm::Inputs::NumericInput,
 :decimal=>SimpleForm::Inputs::NumericInput,
 :float=>SimpleForm::Inputs::NumericInput,
 :range=>SimpleForm::Inputs::RangeInput,
 :check_boxes=>SimpleForm::Inputs::CollectionCheckBoxesInput,
 :radio_buttons=>SimpleForm::Inputs::CollectionRadioButtonsInput,
 :select=>SimpleForm::Inputs::CollectionSelectInput,
 :grouped_select=>SimpleForm::Inputs::GroupedCollectionSelectInput,
 :date=>SimpleForm::Inputs::DateTimeInput,
 :time=>SimpleForm::Inputs::DateTimeInput,
 :datetime=>SimpleForm::Inputs::DateTimeInput,
 :country=>SimpleForm::Inputs::PriorityInput,
 :time_zone=>SimpleForm::Inputs::PriorityInput,
 :boolean=>SimpleForm::Inputs::BooleanInput,
 :hidden=>SimpleForm::Inputs::HiddenInput}