nuatt-formtastic 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.textile +635 -0
  3. data/lib/formtastic.rb +24 -0
  4. data/lib/formtastic/form_builder.rb +75 -0
  5. data/lib/formtastic/helpers.rb +15 -0
  6. data/lib/formtastic/helpers/buttons_helper.rb +277 -0
  7. data/lib/formtastic/helpers/errors_helper.rb +124 -0
  8. data/lib/formtastic/helpers/fieldset_wrapper.rb +62 -0
  9. data/lib/formtastic/helpers/file_column_detection.rb +16 -0
  10. data/lib/formtastic/helpers/form_helper.rb +221 -0
  11. data/lib/formtastic/helpers/input_helper.rb +357 -0
  12. data/lib/formtastic/helpers/inputs_helper.rb +381 -0
  13. data/lib/formtastic/helpers/reflection.rb +12 -0
  14. data/lib/formtastic/helpers/semantic_form_helper.rb +11 -0
  15. data/lib/formtastic/html_attributes.rb +21 -0
  16. data/lib/formtastic/i18n.rb +32 -0
  17. data/lib/formtastic/inputs.rb +29 -0
  18. data/lib/formtastic/inputs/base.rb +50 -0
  19. data/lib/formtastic/inputs/base/associations.rb +33 -0
  20. data/lib/formtastic/inputs/base/choices.rb +88 -0
  21. data/lib/formtastic/inputs/base/collections.rb +94 -0
  22. data/lib/formtastic/inputs/base/database.rb +17 -0
  23. data/lib/formtastic/inputs/base/errors.rb +58 -0
  24. data/lib/formtastic/inputs/base/fileish.rb +23 -0
  25. data/lib/formtastic/inputs/base/grouped_collections.rb +77 -0
  26. data/lib/formtastic/inputs/base/hints.rb +31 -0
  27. data/lib/formtastic/inputs/base/html.rb +51 -0
  28. data/lib/formtastic/inputs/base/labelling.rb +53 -0
  29. data/lib/formtastic/inputs/base/naming.rb +54 -0
  30. data/lib/formtastic/inputs/base/options.rb +18 -0
  31. data/lib/formtastic/inputs/base/stringish.rb +30 -0
  32. data/lib/formtastic/inputs/base/timeish.rb +125 -0
  33. data/lib/formtastic/inputs/base/validations.rb +125 -0
  34. data/lib/formtastic/inputs/base/wrapping.rb +38 -0
  35. data/lib/formtastic/inputs/boolean_input.rb +87 -0
  36. data/lib/formtastic/inputs/check_boxes_input.rb +169 -0
  37. data/lib/formtastic/inputs/country_input.rb +66 -0
  38. data/lib/formtastic/inputs/date_input.rb +14 -0
  39. data/lib/formtastic/inputs/datetime_input.rb +9 -0
  40. data/lib/formtastic/inputs/email_input.rb +40 -0
  41. data/lib/formtastic/inputs/file_input.rb +42 -0
  42. data/lib/formtastic/inputs/hidden_input.rb +66 -0
  43. data/lib/formtastic/inputs/number_input.rb +72 -0
  44. data/lib/formtastic/inputs/numeric_input.rb +20 -0
  45. data/lib/formtastic/inputs/password_input.rb +40 -0
  46. data/lib/formtastic/inputs/phone_input.rb +41 -0
  47. data/lib/formtastic/inputs/radio_input.rb +146 -0
  48. data/lib/formtastic/inputs/search_input.rb +40 -0
  49. data/lib/formtastic/inputs/select_input.rb +208 -0
  50. data/lib/formtastic/inputs/string_input.rb +34 -0
  51. data/lib/formtastic/inputs/text_input.rb +47 -0
  52. data/lib/formtastic/inputs/time_input.rb +14 -0
  53. data/lib/formtastic/inputs/time_zone_input.rb +48 -0
  54. data/lib/formtastic/inputs/url_input.rb +40 -0
  55. data/lib/formtastic/localized_string.rb +96 -0
  56. data/lib/formtastic/railtie.rb +12 -0
  57. data/lib/formtastic/semantic_form_builder.rb +11 -0
  58. data/lib/formtastic/util.rb +25 -0
  59. data/lib/generators/formtastic/form/form_generator.rb +95 -0
  60. data/lib/generators/formtastic/install/install_generator.rb +23 -0
  61. data/lib/generators/templates/_form.html.erb +7 -0
  62. data/lib/generators/templates/_form.html.haml +5 -0
  63. data/lib/generators/templates/formtastic.css +145 -0
  64. data/lib/generators/templates/formtastic.rb +74 -0
  65. data/lib/generators/templates/formtastic_changes.css +14 -0
  66. data/lib/locale/en.yml +7 -0
  67. data/lib/tasks/verify_rcov.rb +44 -0
  68. metadata +206 -19
@@ -0,0 +1,66 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<input type="hidden">` wrapped in the standard `<li>` wrapper. This is
5
+ # provided for situations where a hidden field needs to be rendered in the flow of a form with
6
+ # many inputs that form an `<ol>`. Wrapping the hidden input inside the `<li>` maintains the
7
+ # HTML validity. The `<li>` is marked with a `class` of `hidden` so that stylesheet authors can
8
+ # hide these list items with CSS (formtastic.css does this out of the box).
9
+ #
10
+ # @example Full form context, output and CSS
11
+ #
12
+ # <%= semantic_form_for(@something) do |f| %>
13
+ # <%= f.inputs do %>
14
+ # <%= f.input :secret, :as => :hidden %>
15
+ # <% end %>
16
+ # <% end %>
17
+ #
18
+ # <form...>
19
+ # <fieldset>
20
+ # <ol>
21
+ # <li class="hidden">
22
+ # <input type="hidden" id="something_secret" name="something[secret]">
23
+ # </li>
24
+ # </ol>
25
+ # </fieldset>
26
+ # </form>
27
+ #
28
+ # form.formtastic li.hidden { display:none; }
29
+ #
30
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
31
+ class HiddenInput
32
+ include Base
33
+
34
+ # Override to include :value set directly from options hash. The :value set in :input_html
35
+ # hash will be preferred over :value set directly in the options.
36
+ #
37
+ # @todo this is inconsistent with all other inputs, deprecate and remove
38
+ def input_html_options
39
+ {:value => options[:value]}.merge(super).merge(:required => nil)
40
+ end
41
+
42
+ def to_html
43
+ input_wrapping do
44
+ builder.hidden_field(method, input_html_options)
45
+ end
46
+ end
47
+
48
+ def error_html
49
+ ""
50
+ end
51
+
52
+ def errors?
53
+ false
54
+ end
55
+
56
+ def hint_html
57
+ ""
58
+ end
59
+
60
+ def hint?
61
+ false
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,72 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a HTML5 `<input type="number">` wrapped in the standard
5
+ # `<li>` wrapper. This is the default input choice for all database columns of the type `:float`
6
+ # and `:decimal`, as well as `:integer` columns that aren't used for `belongs_to` associations,
7
+ # but can be applied to any text-like input with `:as => :number`.
8
+ #
9
+ # @example Full form context and output
10
+ #
11
+ # <%= semantic_form_for(@user) do |f| %>
12
+ # <%= f.inputs do %>
13
+ # <%= f.input :shoe_size, :as => :number %>
14
+ # <% end %>
15
+ # <% end %>
16
+ #
17
+ # <form...>
18
+ # <fieldset>
19
+ # <ol>
20
+ # <li class="numeric">
21
+ # <label for="user_shoe_size">Shoe size</label>
22
+ # <input type="number" id="user_shoe_size" name="user[shoe_size]">
23
+ # </li>
24
+ # </ol>
25
+ # </fieldset>
26
+ # </form>
27
+ #
28
+ # @example Default HTML5 min/max/step attributes are detected from the numericality validations
29
+ #
30
+ # class Person < ActiveRecord::Base
31
+ # validates_numericality_of :age,
32
+ # :less_than => 100,
33
+ # :greater_than => 17,
34
+ # :only_integer => true
35
+ # end
36
+ #
37
+ # <%= f.input :age, :as => :number %>
38
+ #
39
+ # <li class="numeric">
40
+ # <label for="persom_age">Age</label>
41
+ # <input type="number" id="person_age" name="person[age]" min="18" max="99" step="1">
42
+ # </li>
43
+ #
44
+ # @example Pass attributes down to the `<input>` tag
45
+ # <%= f.input :shoe_size, :as => :number, :input_html => { :min => 3, :max => 15, :step => 1, :class => "special" } %>
46
+ #
47
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
48
+ # @see http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_numericality_of Rails' Numericality validation documentation
49
+ #
50
+ # @todo Rename/Alias to NumberInput
51
+ class NumberInput
52
+ include Base
53
+ include Base::Stringish
54
+
55
+ def to_html
56
+ input_wrapping do
57
+ label_html <<
58
+ builder.number_field(method, input_html_options)
59
+ end
60
+ end
61
+
62
+ def input_html_options
63
+ {
64
+ :min => validation_min,
65
+ :max => validation_max,
66
+ :step => validation_integer_only? ? 1 : nil
67
+ }.merge(super)
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,20 @@
1
+ module Formtastic
2
+ module Inputs
3
+ # Alias for NumberInput for backwards compatibility with 1.x.
4
+ #
5
+ # @example:
6
+ # f.input :age, :as => :numeric
7
+ #
8
+ # @deprecated Use :as => :number instead
9
+ #
10
+ # @todo Remove on or after 2.1
11
+ class NumericInput < NumberInput
12
+
13
+ def initialize(builder, template, object, object_name, method, options)
14
+ ActiveSupport::Deprecation.warn(':as => :numeric has been deprecated in favor of :as => :number and will be removed on or after version 2.1', caller)
15
+ super
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a `<input type="password">` wrapped in the standard
5
+ # `<li>` wrapper. This is the default input choice for all attributes matching `/password/`, but
6
+ # can be applied to any text-like input with `:as => :password`.
7
+ #
8
+ # @example Full form context and output
9
+ #
10
+ # <%= semantic_form_for(@user) do |f| %>
11
+ # <%= f.inputs do %>
12
+ # <%= f.input :password, :as => :password %>
13
+ # <% end %>
14
+ # <% end %>
15
+ #
16
+ # <form...>
17
+ # <fieldset>
18
+ # <ol>
19
+ # <li class="password">
20
+ # <label for="user_password">Password</label>
21
+ # <input type="password" id="user_password" name="user[password]">
22
+ # </li>
23
+ # </ol>
24
+ # </fieldset>
25
+ # </form>
26
+ #
27
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
28
+ class PasswordInput
29
+ include Base
30
+ include Base::Stringish
31
+
32
+ def to_html
33
+ input_wrapping do
34
+ label_html <<
35
+ builder.password_field(method, input_html_options)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a HTML5 `<input type="phone">` wrapped in the standard
5
+ # `<li>` wrapper. This is the default input choice for attributes with a name matching
6
+ # `/(phone|fax)/`, but can be applied to any text-like input with `:as => :phone`.
7
+ #
8
+ # @example Full form context and output
9
+ #
10
+ # <%= semantic_form_for(@user) do |f| %>
11
+ # <%= f.inputs do %>
12
+ # <%= f.input :mobile, :as => :phone %>
13
+ # <% end %>
14
+ # <% end %>
15
+ #
16
+ # <form...>
17
+ # <fieldset>
18
+ # <ol>
19
+ # <li class="phone">
20
+ # <label for="user_mobile">Mobile</label>
21
+ # <input type="phone" id="user_mobile" name="user[mobile]">
22
+ # </li>
23
+ # </ol>
24
+ # </fieldset>
25
+ # </form>
26
+ #
27
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
28
+ class PhoneInput
29
+ include Base
30
+ include Base::Stringish
31
+
32
+ def to_html
33
+ input_wrapping do
34
+ label_html <<
35
+ builder.phone_field(method, input_html_options)
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,146 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # A radio input is used to render a series of radio inputs. This is an alternative input choice
5
+ # for `belongs_to` associations like a `Post` belonging to a `Section` or an `Author`, or any
6
+ # case where the user needs to make a single selection from a pre-defined collectioon of choices.
7
+ #
8
+ # Within the standard `<li>` wrapper, the output is a `<fieldset>` with a `<legend>` to
9
+ # represent the "label" for the input, and an `<ol>` containing `<li>`s for each choice in
10
+ # the association. Each `<li>` choice has a `<label>` containing an `<input type="radio">` and
11
+ # the label text to describe each choice.
12
+ #
13
+ # Radio inputs can be considered as an alternative where a (non-multi) select input is used,
14
+ # especially in cases where there are only a few choices, however they are not used by default
15
+ # for any type of association or model attribute. You can choose to use a radio input instead of
16
+ # a select with `:as => :radio`.
17
+ #
18
+ # Like a select input, the flexibility of the `:collection` option (see examples) makes the
19
+ # :radio input viable as an alternative for many other input types. For example, instead of...
20
+ #
21
+ # * a `:string` input (where you want to force the user to choose from a few specific strings rather than entering anything)
22
+ # * a `:boolean` checkbox input (where the user could choose yes or no, rather than checking a box)
23
+ # * a `:date`, `:time` or `:datetime` input (where the user could choose from a small set of pre-determined dates)
24
+ # * a `:number` input (where the user could choose from a small set of pre-defined numbers)
25
+ # * a `:time_zone` input (where you want to provide your own small set of choices instead of relying on Rails)
26
+ # * a `:country` input (where you want to provide a small set of choices, no need for a plugin really)
27
+ #
28
+ # For radio inputs that map to associations on the object model, Formtastic will automatically
29
+ # load in a collection of objects on the association as options to choose from. This might be an
30
+ # `Author.all` on a `Post` form with an input for a `belongs_to :user` association, or a
31
+ # `Section.all` for a `Post` form with an input for a `belongs_to :section` association.
32
+ # You can override or customise this collection through the `:collection` option (see examples).
33
+ #
34
+ # The way on which Formtastic renders the `value` attribute and label for each choice is
35
+ # customisable through the `:label_method` and `:value_method` options (see examples below).
36
+ # When not provided, we fall back to a list of methods to try on each object such as
37
+ # `:to_label`, `:name` and `:to_s`, which are defined in the configurations
38
+ # `collection_label_methods` and `collection_value_methods`.
39
+ #
40
+ # @example Basic `belongs_to` example with full form context
41
+ #
42
+ # <%= semantic_form_for @post do |f| %>
43
+ # <%= f.inputs do %>
44
+ # <%= f.input :author, :as => :radio %>
45
+ # <% end %>
46
+ # <% end %>
47
+ #
48
+ # <form...>
49
+ # <fieldset>
50
+ # <ol>
51
+ # <li class='radio'>
52
+ # <fieldset>
53
+ # <legend class="label"><label>Categories</label></legend>
54
+ # <ol>
55
+ # <li>
56
+ # <label for="post_author_id_1">
57
+ # <input type="radio" id="post_author_id_1" value="1"> Justin
58
+ # </label>
59
+ # </li>
60
+ # <li>
61
+ # <label for="post_author_id_3">
62
+ # <input type="radio" id="post_author_id_3" value="3"> Kate
63
+ # </label>
64
+ # </li>
65
+ # <li>
66
+ # <label for="post_author_id_2">
67
+ # <input type="radio" id="post_author_id_2" value="2"> Amelia
68
+ # </label>
69
+ # </li>
70
+ # </ol>
71
+ # </fieldset>
72
+ # </li>
73
+ # </ol>
74
+ # </fieldset>
75
+ # </form>
76
+ #
77
+ # @example The `:collection` option can be used to customize the choices
78
+ # <%= f.input :author, :as => :radio, :collection => @authors %>
79
+ # <%= f.input :author, :as => :radio, :collection => Author.all %>
80
+ # <%= f.input :author, :as => :radio, :collection => Author.some_named_scope %>
81
+ # <%= f.input :author, :as => :radio, :collection => [Author.find_by_login("justin"), Category.find_by_name("kate")] %>
82
+ # <%= f.input :author, :as => :radio, :collection => ["Justin", "Kate"] %>
83
+ # <%= f.input :author, :as => :radio, :collection => [["Justin", "justin"], ["Kate", "kate"]] %>
84
+ # <%= f.input :author, :as => :radio, :collection => [["Justin", "1"], ["Kate", "3"]] %>
85
+ # <%= f.input :author, :as => :radio, :collection => [["Justin", 1], ["Kate", 3]] %>
86
+ # <%= f.input :author, :as => :radio, :collection => 1..5 %>
87
+ #
88
+ # @example The `:label_method` can be used to call a different method (or a Proc) on each object in the collection for rendering the label text (it'll try the methods like `to_s` in `collection_label_methods` config by default)
89
+ # <%= f.input :author, :as => :radio, :label_method => :name %>
90
+ # <%= f.input :author, :as => :radio, :label_method => :name_with_post_count
91
+ # <%= f.input :author, :as => :radio, :label_method => Proc.new { |a| "#{c.name} (#{pluralize("post", a.posts.count)})" }
92
+ #
93
+ # @example The `:value_method` can be used to call a different method (or a Proc) on each object in the collection for rendering the value for each checkbox (it'll try the methods like `id` in `collection_value_methods` config by default)
94
+ # <%= f.input :author, :as => :radio, :value_method => :login %>
95
+ # <%= f.input :author, :as => :radio, :value_method => Proc.new { |c| c.full_name.downcase.underscore }
96
+ #
97
+ # @example Set HTML attributes on each `<input type="radio">` tag with `:input_html`
98
+ # <%= f.input :author, :as => :radio, :input_html => { :size => 20, :multiple => true, :class => "special" } %>
99
+ #
100
+ # @example Set HTML attributes on the `<li>` wrapper with `:wrapper_html`
101
+ # <%= f.input :author, :as => :radio, :wrapper_html => { :class => "special" } %>
102
+ #
103
+ # @example `:value_as_class` can be used to add a class to the `<li>` wrapped around each choice using the radio value for custom styling of each choice
104
+ # <%= f.input :author, :as => :radio, :value_as_class => true %>
105
+ #
106
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
107
+ # @see Formtastic::Inputs::RadioInput as an alternative for `belongs_to` associations
108
+ #
109
+ # @todo :disabled like CheckBoxes?
110
+ class RadioInput
111
+ include Base
112
+ include Base::Collections
113
+ include Base::Choices
114
+
115
+ def to_html
116
+ input_wrapping do
117
+ choices_wrapping do
118
+ legend_html <<
119
+ choices_group_wrapping do
120
+ collection.map { |choice|
121
+ choice_wrapping(choice_wrapping_html_options(choice)) do
122
+ choice_html(choice)
123
+ end
124
+ }.join("\n").html_safe
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ def choice_html(choice)
131
+ template.content_tag(:label,
132
+ builder.radio_button(input_name, choice_value(choice), input_html_options.merge(:id => choice_input_dom_id(choice))) <<
133
+ choice_label(choice),
134
+ label_html_options.merge(:for => choice_input_dom_id(choice))
135
+ )
136
+ end
137
+
138
+ # Override to remove the for attribute since this isn't associated with any element, as it's
139
+ # nested inside the legend.
140
+ def label_html_options
141
+ super.merge(:for => nil)
142
+ end
143
+
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,40 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a HTML5 `<input type="search">` wrapped in the standard
5
+ # `<li>` wrapper. This is the default input choice for attributes with a name matching
6
+ # `/^search$/`, but can be applied to any text-like input with `:as => :search`.
7
+ #
8
+ # @example Full form context and output
9
+ #
10
+ # <%= semantic_form_for(@search, :html => { :method => :get }) do |f| %>
11
+ # <%= f.inputs do %>
12
+ # <%= f.input :q, :as => :search, :label => false, :input_html => { :name => "q" } %>
13
+ # <% end %>
14
+ # <% end %>
15
+ #
16
+ # <form...>
17
+ # <fieldset>
18
+ # <ol>
19
+ # <li class="search">
20
+ # <input type="search" id="search_q" name="q">
21
+ # </li>
22
+ # </ol>
23
+ # </fieldset>
24
+ # </form>
25
+ #
26
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
27
+ class SearchInput
28
+ include Base
29
+ include Base::Stringish
30
+
31
+ def to_html
32
+ input_wrapping do
33
+ label_html <<
34
+ builder.search_field(method, input_html_options)
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,208 @@
1
+ module Formtastic
2
+ module Inputs
3
+ # A select input is used to render a `<select>` tag with a series of options to choose from.
4
+ # It works for both single selections (like a `belongs_to` relationship, or "yes/no" boolean),
5
+ # as well as multiple selections (like a `has_and_belongs_to_many`/`has_many` relationship,
6
+ # for assigning many genres to a song, for example).
7
+ #
8
+ # This is the default input choice when:
9
+ #
10
+ # * the database column type is an `:integer` and there is an association (`belongs_to`)
11
+ # * the database column type is a `:string` and the `:collection` option is used
12
+ # * there an object with an association, but no database column on the object (`has_many`, etc)
13
+ # * there is no object and the `:collection` option is used
14
+ #
15
+ # The flexibility of the `:collection` option (see examples) makes the :select input viable as
16
+ # an alternative for many other input types. For example, instead of...
17
+ #
18
+ # * a `:string` input (where you want to force the user to choose from a few specific strings rather than entering anything)
19
+ # * a `:boolean` checkbox input (where the user could choose yes or no, rather than checking a box)
20
+ # * a `:date`, `:time` or `:datetime` input (where the user could choose from pre-selected dates)
21
+ # * a `:number` input (where the user could choose from a set of pre-defined numbers)
22
+ # * a `:time_zone` input (where you want to provide your own set of choices instead of relying on Rails)
23
+ # * a `:country` input (no need for a plugin really)
24
+ #
25
+ # Within the standard `<li>` wrapper, the output is a `<label>` tag followed by a `<select>`
26
+ # tag containing `<option>` tags.
27
+ #
28
+ # For inputs that map to associations on the object model, Formtastic will automatically load
29
+ # in a collection of objects on the association as options to choose from. This might be an
30
+ # `Author.all` on a `Post` form with an input for a `belongs_to :user` association, or a
31
+ # `Tag.all` for a `Post` form with an input for a `has_and_belongs_to_many :tags` association.
32
+ # You can override or customise this collection and the `<option>` tags it will render through
33
+ # the `:collection` option (see examples).
34
+ #
35
+ # The way on which Formtastic renders the `value` attribute and content of each `<option>` tag
36
+ # is customisable through the `:label_method` and `:value_method` options. When not provided,
37
+ # we fall back to a list of methods to try on each object such as `:to_label`, `:name` and
38
+ # `:to_s`, which are defined in the configurations `collection_label_methods` and
39
+ # `collection_value_methods` (see examples below).
40
+ #
41
+ # @example Basic `belongs_to` example with full form context
42
+ #
43
+ # <%= semantic_form_for @post do |f| %>
44
+ # <%= f.inputs do %>
45
+ # <%= f.input :author, :as => :select %>
46
+ # <% end %>
47
+ # <% end %>
48
+ #
49
+ # <form...>
50
+ # <fieldset>
51
+ # <ol>
52
+ # <li class='select'>
53
+ # <label for="post_author_id">Author</label>
54
+ # <select id="post_author_id" name="post[post_author_id]">
55
+ # <option value=""></option>
56
+ # <option value="1">Justin</option>
57
+ # <option value="3">Kate</option>
58
+ # <option value="2">Amelia</option>
59
+ # </select>
60
+ # </li>
61
+ # </ol>
62
+ # </fieldset>
63
+ # </form>
64
+ #
65
+ # @example Basic `has_many` or `has_and_belongs_to_many` example with full form context
66
+ #
67
+ # <%= semantic_form_for @post do |f| %>
68
+ # <%= f.inputs do %>
69
+ # <%= f.input :tags, :as => :select %>
70
+ # <% end %>
71
+ # <% end %>
72
+ #
73
+ # <form...>
74
+ # <fieldset>
75
+ # <ol>
76
+ # <li class='select'>
77
+ # <label for="post_tag_ids">Author</label>
78
+ # <select id="post_tag_ids" name="post[tag_ids]" multiple="true">
79
+ # <option value="1">Ruby</option>
80
+ # <option value="6">Rails</option>
81
+ # <option value="3">Forms</option>
82
+ # <option value="4">Awesome</option>
83
+ # </select>
84
+ # </li>
85
+ # </ol>
86
+ # </fieldset>
87
+ # </form>
88
+ #
89
+ # @example Override Formtastic's assumption on when you need a multi select
90
+ # <%= f.input :authors, :as => :select, :input_html => { :multiple => true } %>
91
+ # <%= f.input :authors, :as => :select, :input_html => { :multiple => false } %>
92
+ #
93
+ # @example The `:collection` option can be used to customize the choices
94
+ # <%= f.input :author, :as => :select, :collection => @authors %>
95
+ # <%= f.input :author, :as => :select, :collection => Author.all %>
96
+ # <%= f.input :author, :as => :select, :collection => Author.some_named_scope %>
97
+ # <%= f.input :author, :as => :select, :collection => [Author.find_by_login("justin"), Category.find_by_name("kate")] %>
98
+ # <%= f.input :author, :as => :select, :collection => ["Justin", "Kate"] %>
99
+ # <%= f.input :author, :as => :select, :collection => [["Justin", "justin"], ["Kate", "kate"]] %>
100
+ # <%= f.input :author, :as => :select, :collection => [["Justin", "1"], ["Kate", "3"]] %>
101
+ # <%= f.input :author, :as => :select, :collection => [["Justin", 1], ["Kate", 3]] %>
102
+ # <%= f.input :author, :as => :select, :collection => 1..5 %>
103
+ # <%= f.input :author, :as => :select, :collection => "<option>your own options HTML string</option>" %>
104
+ # <%= f.input :author, :as => :select, :collection => options_for_select(...) %>
105
+ # <%= f.input :author, :as => :select, :collection => options_from_collection_for_select(...) %>
106
+ # <%= f.input :author, :as => :select, :collection => grouped_options_for_select(...) %>
107
+ # <%= f.input :author, :as => :select, :collection => time_zone_options_for_select(...) %>
108
+ #
109
+ # @example The `:label_method` can be used to call a different method (or a Proc) on each object in the collection for rendering the label text (it'll try the methods like `to_s` in `collection_label_methods` config by default)
110
+ # <%= f.input :author, :as => :select, :label_method => :name %>
111
+ # <%= f.input :author, :as => :select, :label_method => :name_with_post_count
112
+ # <%= f.input :author, :as => :select, :label_method => Proc.new { |a| "#{c.name} (#{pluralize("post", a.posts.count)})" }
113
+ #
114
+ # @example The `:value_method` can be used to call a different method (or a Proc) on each object in the collection for rendering the value for each checkbox (it'll try the methods like `id` in `collection_value_methods` config by default)
115
+ # <%= f.input :author, :as => :select, :value_method => :login %>
116
+ # <%= f.input :author, :as => :select, :value_method => Proc.new { |c| c.full_name.downcase.underscore }
117
+ #
118
+ # @example Set HTML attributes on the `<select>` tag with `:input_html`
119
+ # <%= f.input :authors, :as => :select, :input_html => { :size => 20, :multiple => true, :class => "special" } %>
120
+ #
121
+ # @example Set HTML attributes on the `<li>` wrapper with `:wrapper_html`
122
+ # <%= f.input :authors, :as => :select, :wrapper_html => { :class => "special" } %>
123
+ #
124
+ # @example Exclude or include the blank option at the top of the select, or change the prompt
125
+ # <%= f.input :author, :as => :select, :input_html => { :include_blank => false } %>
126
+ # <%= f.input :author, :as => :select, :input_html => { :include_blank => true } %>
127
+ # <%= f.input :author, :as => :select, :input_html => { :prompt => "Please select an Author..." } %>
128
+ #
129
+ # @example Group options an `<optgroup>` with the `:group_by` and `:group_label_method` options (`belongs_to` associations only)
130
+ # <%= f.input :author, :as => :select, :group_by => :continent %>
131
+ #
132
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
133
+ # @see Formtastic::Inputs::CheckBoxesInput CheckBoxesInput as an alternative for `has_many` and `has_and_belongs_to_many` associations
134
+ # @see Formtastic::Inputs::RadioInput RadioInput as an alternative for `belongs_to` associations
135
+ class SelectInput
136
+ include Base
137
+ include Base::Collections
138
+ include Base::GroupedCollections
139
+
140
+ def to_html
141
+ input_wrapping do
142
+ label_html <<
143
+ (options[:group_by] ? grouped_select_html : select_html)
144
+ end
145
+ end
146
+
147
+ def select_html
148
+ builder.select(input_name, collection, input_options, input_html_options)
149
+ end
150
+
151
+ def grouped_select_html
152
+ builder.grouped_collection_select(
153
+ input_name,
154
+ grouped_collection,
155
+ group_association,
156
+ group_label_method,
157
+ value_method,
158
+ label_method,
159
+ input_options,
160
+ input_html_options
161
+ )
162
+ end
163
+
164
+ def include_blank?
165
+ return options[:prompt] if options.key?(:prompt)
166
+ return options[:include_blank] if options.key?(:include_blank)
167
+ return true if (single? && builder.include_blank_for_select_by_default)
168
+ false
169
+ end
170
+
171
+ def label_html_options
172
+ super.merge(:for => input_html_options[:id])
173
+ end
174
+
175
+ def input_options
176
+ {:include_blank => include_blank?}.merge(super)
177
+ end
178
+
179
+ def input_html_options
180
+ extra_input_html_options.merge(super)
181
+ end
182
+
183
+ def extra_input_html_options
184
+ {
185
+ :multiple => multiple_by_association?,
186
+ :name => "#{object_name}[#{association_primary_key}]"
187
+ }
188
+ end
189
+
190
+ def multiple_by_association?
191
+ reflection && [ :has_many, :has_and_belongs_to_many ].include?(reflection.macro)
192
+ end
193
+
194
+ def multiple_by_options?
195
+ options[:multiple]
196
+ end
197
+
198
+ def multiple?
199
+ multiple_by_options? || multiple_by_association?
200
+ end
201
+
202
+ def single?
203
+ !multiple?
204
+ end
205
+
206
+ end
207
+ end
208
+ end