Daily Code Reading #30 – Formtastic basic inputs

Today I’ll be finishing up my code reading session of formtastic by looking at how it creates the input fields.

The Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
module Formtastic #:nodoc:
 
  class SemanticFormBuilder < ActionView::Helpers::FormBuilder
      def basic_input_helper(form_helper_method, type, method, options) #:nodoc:
        html_options = options.delete(:input_html) || {}
        html_options = default_string_options(method, type).merge(html_options) if [:numeric, :string, :password, :text].include?(type)
 
        self.label(method, options_for_label(options)) <<
        self.send(form_helper_method, method, html_options)
      end
 
      # Outputs a label and standard Rails text field inside the wrapper.
      def string_input(method, options)
        basic_input_helper(:text_field, :string, method, options)
      end
 
      # Outputs a label and standard Rails password field inside the wrapper.
      def password_input(method, options)
        basic_input_helper(:password_field, :password, method, options)
      end
 
      # Outputs a label and standard Rails text field inside the wrapper.
      def numeric_input(method, options)
        basic_input_helper(:text_field, :numeric, method, options)
      end
 
      # Ouputs a label and standard Rails text area inside the wrapper.
      def text_input(method, options)
        basic_input_helper(:text_area, :text, method, options)
      end
 
      # Outputs a label and a standard Rails file field inside the wrapper.
      def file_input(method, options)
        basic_input_helper(:file_field, :file, method, options)
      end
 
  end
end

Review

The first thing I noticed is that many of the common inputs all use the same method to create the input just with different parameters, #basic_input_helper. So lets dig into #basic_input_helper deeper.

HTML Options

1
2
html_options = options.delete(:input_html) || {}
html_options = default_string_options(method, type).merge(html_options) if [:numeric, :string, :password, :text].include?(type)

First #basic_input_helper sets up the input_html options. If you remember, this option was used to control the html attributes of the input element in more detail.

Then :numeric, :string, :password, and :text fields will get some extra options from #default_string_options. This includes sizing information that is be pulled directly from the database (e.g. 4 characters max size).

Input generation

1
2
self.label(method, options_for_label(options)) <<
self.send(form_helper_method, method, html_options)

Finally, #basic_input_helper generates a label for the input and then runs the ActionView method to create the input. If you remember, the form_helper_method was passed in from the inputs earlier (:text_field, :password_field, etc).

So that completes my code reading of formtastic. I started with the form generation, figured out how it automatically gets all of the fields on a model, walked though how all of the field options are created, saw how it uses reflection to find the type of input for each field, and how those inputs are passed to ActionView to be rendered into HTML. There is still a lot of other code in formtastic, including some generators for more advanced fields if you’re interested in reading further.