formtastic 1.2.4 → 3.1.5

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 (189) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +46 -0
  5. data/.yardopts +1 -0
  6. data/Appraisals +43 -0
  7. data/CHANGELOG +54 -0
  8. data/DEPRECATIONS +52 -0
  9. data/Gemfile +3 -0
  10. data/README.md +629 -0
  11. data/RELEASE_PROCESS +6 -0
  12. data/Rakefile +35 -0
  13. data/app/assets/stylesheets/formtastic.css +289 -0
  14. data/app/assets/stylesheets/formtastic_ie6.css +33 -0
  15. data/app/assets/stylesheets/formtastic_ie7.css +23 -0
  16. data/formtastic.gemspec +42 -0
  17. data/gemfiles/rails_3.2.gemfile +9 -0
  18. data/gemfiles/rails_4.0.4.gemfile +8 -0
  19. data/gemfiles/rails_4.1.gemfile +8 -0
  20. data/gemfiles/rails_4.2.gemfile +8 -0
  21. data/gemfiles/rails_4.gemfile +8 -0
  22. data/gemfiles/rails_5.0.gemfile +8 -0
  23. data/gemfiles/rails_edge.gemfile +15 -0
  24. data/lib/formtastic.rb +40 -1945
  25. data/lib/formtastic/action_class_finder.rb +18 -0
  26. data/lib/formtastic/actions.rb +11 -0
  27. data/lib/formtastic/actions/base.rb +156 -0
  28. data/lib/formtastic/actions/button_action.rb +67 -0
  29. data/lib/formtastic/actions/buttonish.rb +17 -0
  30. data/lib/formtastic/actions/input_action.rb +70 -0
  31. data/lib/formtastic/actions/link_action.rb +88 -0
  32. data/lib/formtastic/deprecation.rb +42 -0
  33. data/lib/formtastic/engine.rb +11 -0
  34. data/lib/formtastic/form_builder.rb +124 -0
  35. data/lib/formtastic/helpers.rb +16 -0
  36. data/lib/formtastic/helpers/action_helper.rb +162 -0
  37. data/lib/formtastic/helpers/actions_helper.rb +168 -0
  38. data/lib/formtastic/helpers/enum.rb +13 -0
  39. data/lib/formtastic/helpers/errors_helper.rb +81 -0
  40. data/lib/formtastic/helpers/fieldset_wrapper.rb +80 -0
  41. data/lib/formtastic/helpers/file_column_detection.rb +16 -0
  42. data/lib/formtastic/helpers/form_helper.rb +203 -0
  43. data/lib/formtastic/helpers/input_helper.rb +407 -0
  44. data/lib/formtastic/helpers/inputs_helper.rb +411 -0
  45. data/lib/formtastic/helpers/reflection.rb +37 -0
  46. data/lib/formtastic/html_attributes.rb +32 -0
  47. data/lib/formtastic/i18n.rb +4 -2
  48. data/lib/formtastic/input_class_finder.rb +18 -0
  49. data/lib/formtastic/inputs.rb +39 -0
  50. data/lib/formtastic/inputs/base.rb +76 -0
  51. data/lib/formtastic/inputs/base/associations.rb +31 -0
  52. data/lib/formtastic/inputs/base/choices.rb +108 -0
  53. data/lib/formtastic/inputs/base/collections.rb +159 -0
  54. data/lib/formtastic/inputs/base/database.rb +22 -0
  55. data/lib/formtastic/inputs/base/datetime_pickerish.rb +85 -0
  56. data/lib/formtastic/inputs/base/errors.rb +58 -0
  57. data/lib/formtastic/inputs/base/fileish.rb +23 -0
  58. data/lib/formtastic/inputs/base/hints.rb +31 -0
  59. data/lib/formtastic/inputs/base/html.rb +53 -0
  60. data/lib/formtastic/inputs/base/labelling.rb +52 -0
  61. data/lib/formtastic/inputs/base/naming.rb +42 -0
  62. data/lib/formtastic/inputs/base/numeric.rb +50 -0
  63. data/lib/formtastic/inputs/base/options.rb +17 -0
  64. data/lib/formtastic/inputs/base/placeholder.rb +17 -0
  65. data/lib/formtastic/inputs/base/stringish.rb +38 -0
  66. data/lib/formtastic/inputs/base/timeish.rb +241 -0
  67. data/lib/formtastic/inputs/base/validations.rb +215 -0
  68. data/lib/formtastic/inputs/base/wrapping.rb +50 -0
  69. data/lib/formtastic/inputs/boolean_input.rb +118 -0
  70. data/lib/formtastic/inputs/check_boxes_input.rb +197 -0
  71. data/lib/formtastic/inputs/color_input.rb +42 -0
  72. data/lib/formtastic/inputs/country_input.rb +86 -0
  73. data/lib/formtastic/inputs/datalist_input.rb +41 -0
  74. data/lib/formtastic/inputs/date_picker_input.rb +93 -0
  75. data/lib/formtastic/inputs/date_select_input.rb +34 -0
  76. data/lib/formtastic/inputs/datetime_picker_input.rb +103 -0
  77. data/lib/formtastic/inputs/datetime_select_input.rb +12 -0
  78. data/lib/formtastic/inputs/email_input.rb +41 -0
  79. data/lib/formtastic/inputs/file_input.rb +42 -0
  80. data/lib/formtastic/inputs/hidden_input.rb +62 -0
  81. data/lib/formtastic/inputs/number_input.rb +88 -0
  82. data/lib/formtastic/inputs/password_input.rb +41 -0
  83. data/lib/formtastic/inputs/phone_input.rb +42 -0
  84. data/lib/formtastic/inputs/radio_input.rb +163 -0
  85. data/lib/formtastic/inputs/range_input.rb +95 -0
  86. data/lib/formtastic/inputs/search_input.rb +41 -0
  87. data/lib/formtastic/inputs/select_input.rb +235 -0
  88. data/lib/formtastic/inputs/string_input.rb +36 -0
  89. data/lib/formtastic/inputs/text_input.rb +48 -0
  90. data/lib/formtastic/inputs/time_picker_input.rb +99 -0
  91. data/lib/formtastic/inputs/time_select_input.rb +38 -0
  92. data/lib/formtastic/inputs/time_zone_input.rb +58 -0
  93. data/lib/formtastic/inputs/url_input.rb +41 -0
  94. data/lib/formtastic/localized_string.rb +17 -0
  95. data/lib/formtastic/localizer.rb +152 -0
  96. data/lib/formtastic/namespaced_class_finder.rb +99 -0
  97. data/lib/formtastic/util.rb +35 -16
  98. data/lib/formtastic/version.rb +3 -0
  99. data/lib/generators/formtastic/form/form_generator.rb +64 -37
  100. data/lib/generators/formtastic/input/input_generator.rb +46 -0
  101. data/lib/generators/formtastic/install/install_generator.rb +13 -5
  102. data/lib/generators/templates/_form.html.erb +10 -4
  103. data/lib/generators/templates/_form.html.haml +8 -4
  104. data/lib/generators/templates/_form.html.slim +8 -0
  105. data/lib/generators/templates/formtastic.rb +77 -44
  106. data/lib/generators/templates/input.rb +19 -0
  107. data/lib/locale/en.yml +3 -0
  108. data/sample/basic_inputs.html +224 -0
  109. data/sample/config.ru +69 -0
  110. data/sample/index.html +14 -0
  111. data/spec/action_class_finder_spec.rb +12 -0
  112. data/spec/actions/button_action_spec.rb +63 -0
  113. data/spec/actions/generic_action_spec.rb +521 -0
  114. data/spec/actions/input_action_spec.rb +59 -0
  115. data/spec/actions/link_action_spec.rb +92 -0
  116. data/spec/builder/custom_builder_spec.rb +116 -0
  117. data/spec/builder/error_proc_spec.rb +27 -0
  118. data/spec/builder/semantic_fields_for_spec.rb +142 -0
  119. data/spec/fast_spec_helper.rb +12 -0
  120. data/spec/generators/formtastic/form/form_generator_spec.rb +131 -0
  121. data/spec/generators/formtastic/input/input_generator_spec.rb +124 -0
  122. data/spec/generators/formtastic/install/install_generator_spec.rb +47 -0
  123. data/spec/helpers/action_helper_spec.rb +19 -0
  124. data/spec/helpers/actions_helper_spec.rb +143 -0
  125. data/spec/helpers/form_helper_spec.rb +218 -0
  126. data/spec/helpers/input_helper_spec.rb +6 -0
  127. data/spec/helpers/inputs_helper_spec.rb +655 -0
  128. data/spec/helpers/namespaced_action_helper_spec.rb +43 -0
  129. data/spec/helpers/namespaced_input_helper_spec.rb +36 -0
  130. data/spec/helpers/reflection_helper_spec.rb +32 -0
  131. data/spec/helpers/semantic_errors_helper_spec.rb +112 -0
  132. data/spec/i18n_spec.rb +210 -0
  133. data/spec/input_class_finder_spec.rb +10 -0
  134. data/spec/inputs/base/collections_spec.rb +76 -0
  135. data/spec/inputs/base/validations_spec.rb +342 -0
  136. data/spec/inputs/boolean_input_spec.rb +254 -0
  137. data/spec/inputs/check_boxes_input_spec.rb +546 -0
  138. data/spec/inputs/color_input_spec.rb +97 -0
  139. data/spec/inputs/country_input_spec.rb +133 -0
  140. data/spec/inputs/custom_input_spec.rb +55 -0
  141. data/spec/inputs/datalist_input_spec.rb +61 -0
  142. data/spec/inputs/date_picker_input_spec.rb +449 -0
  143. data/spec/inputs/date_select_input_spec.rb +235 -0
  144. data/spec/inputs/datetime_picker_input_spec.rb +490 -0
  145. data/spec/inputs/datetime_select_input_spec.rb +193 -0
  146. data/spec/inputs/email_input_spec.rb +85 -0
  147. data/spec/inputs/file_input_spec.rb +89 -0
  148. data/spec/inputs/hidden_input_spec.rb +135 -0
  149. data/spec/inputs/include_blank_spec.rb +78 -0
  150. data/spec/inputs/label_spec.rb +149 -0
  151. data/spec/inputs/number_input_spec.rb +815 -0
  152. data/spec/inputs/password_input_spec.rb +99 -0
  153. data/spec/inputs/phone_input_spec.rb +85 -0
  154. data/spec/inputs/placeholder_spec.rb +71 -0
  155. data/spec/inputs/radio_input_spec.rb +328 -0
  156. data/spec/inputs/range_input_spec.rb +505 -0
  157. data/spec/inputs/readonly_spec.rb +50 -0
  158. data/spec/inputs/search_input_spec.rb +84 -0
  159. data/spec/inputs/select_input_spec.rb +615 -0
  160. data/spec/inputs/string_input_spec.rb +260 -0
  161. data/spec/inputs/text_input_spec.rb +187 -0
  162. data/spec/inputs/time_picker_input_spec.rb +455 -0
  163. data/spec/inputs/time_select_input_spec.rb +248 -0
  164. data/spec/inputs/time_zone_input_spec.rb +143 -0
  165. data/spec/inputs/url_input_spec.rb +85 -0
  166. data/spec/inputs/with_options_spec.rb +43 -0
  167. data/spec/localizer_spec.rb +130 -0
  168. data/spec/namespaced_class_finder_spec.rb +79 -0
  169. data/spec/spec.opts +2 -0
  170. data/spec/spec_helper.rb +525 -0
  171. data/spec/support/custom_macros.rb +564 -0
  172. data/spec/support/deprecation.rb +6 -0
  173. data/spec/support/shared_examples.rb +1313 -0
  174. data/spec/support/specialized_class_finder_shared_example.rb +27 -0
  175. data/spec/support/test_environment.rb +31 -0
  176. data/spec/util_spec.rb +66 -0
  177. metadata +434 -161
  178. data/README.textile +0 -682
  179. data/generators/form/USAGE +0 -16
  180. data/generators/form/form_generator.rb +0 -111
  181. data/generators/formtastic/formtastic_generator.rb +0 -26
  182. data/init.rb +0 -5
  183. data/lib/formtastic/layout_helper.rb +0 -12
  184. data/lib/formtastic/railtie.rb +0 -14
  185. data/lib/generators/templates/formtastic.css +0 -145
  186. data/lib/generators/templates/formtastic_changes.css +0 -14
  187. data/lib/generators/templates/rails2/_form.html.erb +0 -5
  188. data/lib/generators/templates/rails2/_form.html.haml +0 -4
  189. data/rails/init.rb +0 -2
@@ -0,0 +1,41 @@
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 documentation of all possible options.
28
+ class PasswordInput
29
+ include Base
30
+ include Base::Stringish
31
+ include Base::Placeholder
32
+
33
+ def to_html
34
+ input_wrapping do
35
+ label_html <<
36
+ builder.password_field(method, input_html_options)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,42 @@
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="tel" 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 documentation of all possible options.
28
+ class PhoneInput
29
+ include Base
30
+ include Base::Stringish
31
+ include Base::Placeholder
32
+
33
+ def to_html
34
+ input_wrapping do
35
+ label_html <<
36
+ builder.phone_field(method, input_html_options)
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,163 @@
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_select`, `:time_select` or `:datetime_select` 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
+ # For radio inputs that map to ActiveRecord `enum` attributes, Formtastic will automatically
35
+ # load in your enum options to be used as the radio button choices. This can be overridden with
36
+ # the `:collection` option, or augmented with I18n translations. See examples below.
37
+ #
38
+ # The way on which Formtastic renders the `value` attribute and label for each choice in the `:collection` is
39
+ # customisable (see examples below). When not provided, we fall back to a list of methods to try on each
40
+ # object such as `:to_label`, `:name` and `:to_s`, which are defined in the configurations
41
+ # `collection_label_methods` and `collection_value_methods`.
42
+ #
43
+ # @example Basic `belongs_to` example with full form context
44
+ #
45
+ # <%= semantic_form_for @post do |f| %>
46
+ # <%= f.inputs do %>
47
+ # <%= f.input :author, :as => :radio %>
48
+ # <% end %>
49
+ # <% end %>
50
+ #
51
+ # <form...>
52
+ # <fieldset>
53
+ # <ol>
54
+ # <li class='radio'>
55
+ # <fieldset>
56
+ # <legend class="label"><label>Categories</label></legend>
57
+ # <ol>
58
+ # <li>
59
+ # <label for="post_author_id_1">
60
+ # <input type="radio" id="post_author_id_1" value="1"> Justin
61
+ # </label>
62
+ # </li>
63
+ # <li>
64
+ # <label for="post_author_id_3">
65
+ # <input type="radio" id="post_author_id_3" value="3"> Kate
66
+ # </label>
67
+ # </li>
68
+ # <li>
69
+ # <label for="post_author_id_2">
70
+ # <input type="radio" id="post_author_id_2" value="2"> Amelia
71
+ # </label>
72
+ # </li>
73
+ # </ol>
74
+ # </fieldset>
75
+ # </li>
76
+ # </ol>
77
+ # </fieldset>
78
+ # </form>
79
+ #
80
+ # @example The `:collection` option can be used to customize the choices
81
+ # <%= f.input :author, :as => :radio, :collection => @authors %>
82
+ # <%= f.input :author, :as => :radio, :collection => Author.all %>
83
+ # <%= f.input :author, :as => :radio, :collection => Author.some_named_scope %>
84
+ # <%= f.input :author, :as => :radio, :collection => Author.pluck(:full_name, :id) %>
85
+ # <%= f.input :author, :as => :radio, :collection => Author.pluck(Arel.sql("CONCAT(`first_name`, ' ', `last_name`)"), :id)) %>
86
+ # <%= f.input :author, :as => :radio, :collection => [Author.find_by_login("justin"), Category.find_by_name("kate")] %>
87
+ # <%= f.input :author, :as => :radio, :collection => ["Justin", "Kate"] %>
88
+ # <%= f.input :author, :as => :radio, :collection => [["Justin", "justin"], ["Kate", "kate"]] %>
89
+ # <%= f.input :author, :as => :radio, :collection => [["Justin", "1"], ["Kate", "3"]] %>
90
+ # <%= f.input :author, :as => :radio, :collection => [["Justin", 1], ["Kate", 3]] %>
91
+ # <%= f.input :author, :as => :radio, :collection => [["Justin", :justin], ["Kate", :kate]] %>
92
+ # <%= f.input :author, :as => :radio, :collection => [:justin, :kate] %>
93
+ # <%= f.input :author, :as => :radio, :collection => 1..5 %>
94
+ #
95
+ # @example Set HTML attributes on each `<input type="radio">` tag with `:input_html`
96
+ # <%= f.input :author, :as => :radio, :input_html => { :size => 20, :multiple => true, :class => "special" } %>
97
+ #
98
+ # @example Set HTML attributes on the `<li>` wrapper with `:wrapper_html`
99
+ # <%= f.input :author, :as => :radio, :wrapper_html => { :class => "special" } %>
100
+ #
101
+ # @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
102
+ # <%= f.input :author, :as => :radio, :value_as_class => true %>
103
+ #
104
+ # @example Set HTML options on a specific radio input option with a 3rd element in the array for a collection member
105
+ # <%= f.input :author, :as => :radio, :collection => [["Test", 'test'], ["Try", "try", {:disabled => true}]]
106
+ #
107
+ # @example Using ActiveRecord enum attribute with i18n translation:
108
+ # # post.rb
109
+ # class Post < ActiveRecord::Base
110
+ # enum :status => [ :active, :archived ]
111
+ # end
112
+ # # en.yml
113
+ # en:
114
+ # activerecord:
115
+ # attributes:
116
+ # post:
117
+ # statuses:
118
+ # active: I am active!
119
+ # archived: I am archived!
120
+ # # form
121
+ # <%= f.input :status, :as => :radio %>
122
+ #
123
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
124
+ # @see Formtastic::Inputs::RadioInput as an alternative for `belongs_to` associations
125
+ #
126
+ # @todo :disabled like CheckBoxes?
127
+ class RadioInput
128
+ include Base
129
+ include Base::Collections
130
+ include Base::Choices
131
+
132
+ def to_html
133
+ input_wrapping do
134
+ choices_wrapping do
135
+ legend_html <<
136
+ choices_group_wrapping do
137
+ collection.map { |choice|
138
+ choice_wrapping(choice_wrapping_html_options(choice)) do
139
+ choice_html(choice)
140
+ end
141
+ }.join("\n").html_safe
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ def choice_html(choice)
148
+ template.content_tag(:label,
149
+ builder.radio_button(input_name, choice_value(choice), input_html_options.merge(choice_html_options(choice)).merge(:required => false)) <<
150
+ choice_label(choice),
151
+ label_html_options.merge(:for => choice_input_dom_id(choice), :class => nil)
152
+ )
153
+ end
154
+
155
+ # Override to remove the for attribute since this isn't associated with any element, as it's
156
+ # nested inside the legend.
157
+ def label_html_options
158
+ super.merge(:for => nil)
159
+ end
160
+
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,95 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a HTML5 `<input type="range">` wrapped in the standard
5
+ # `<li>` wrapper. This is an alternative input choice to a number input.
6
+ #
7
+ # Sensible default for the `min`, `max` and `step` attributes are found by reflecting on
8
+ # the model's validations. When validations are not provided, the `min` and `step` default to
9
+ # `1` and the `max` default to `100`. An `IndeterminableMinimumAttributeError` exception
10
+ # will be raised when the following conditions are all true:
11
+ #
12
+ # * you haven't specified a `:min` or `:max` for the input
13
+ # * the model's database column type is a `:float` or `:decimal`
14
+ # * the validation uses `:less_than` or `:greater_than`
15
+ #
16
+ # The solution is to either:
17
+ #
18
+ # * manually specify the `:min` or `:max` for the input
19
+ # * change the database column type to an `:integer` (if appropriate)
20
+ # * change the validations to use `:less_than_or_equal_to` or `:greater_than_or_equal_to`
21
+ #
22
+ # @example Full form context and output
23
+ #
24
+ # <%= semantic_form_for(@user) do |f| %>
25
+ # <%= f.inputs do %>
26
+ # <%= f.input :shoe_size, :as => :range %>
27
+ # <% end %>
28
+ # <% end %>
29
+ #
30
+ # <form...>
31
+ # <fieldset>
32
+ # <ol>
33
+ # <li class="numeric">
34
+ # <label for="user_shoe_size">Shoe size</label>
35
+ # <input type="range" id="user_shoe_size" name="user[shoe_size]" min="1" max="100" step="1">
36
+ # </li>
37
+ # </ol>
38
+ # </fieldset>
39
+ # </form>
40
+ #
41
+ # @example Default HTML5 min/max/step attributes are detected from the numericality validations
42
+ #
43
+ # class Person < ActiveRecord::Base
44
+ # validates_numericality_of :age,
45
+ # :less_than_or_equal_to => 100,
46
+ # :greater_than_or_equal_to => 18,
47
+ # :only_integer => true
48
+ # end
49
+ #
50
+ # <%= f.input :age, :as => :number %>
51
+ #
52
+ # <li class="numeric">
53
+ # <label for="persom_age">Age</label>
54
+ # <input type="range" id="person_age" name="person[age]" min="18" max="100" step="1">
55
+ # </li>
56
+ #
57
+ # @example Pass attributes down to the `<input>` tag with :input_html
58
+ # <%= f.input :shoe_size, :as => :range, :input_html => { :min => 3, :max => 15, :step => 1, :class => "special" } %>
59
+ #
60
+ # @example Min/max/step also work as options
61
+ # <%= f.input :shoe_size, :as => :range, :min => 3, :max => 15, :step => 1, :input_html => { :class => "special" } %>
62
+ #
63
+ # @example Use :in with a Range as a shortcut for :min/:max
64
+ # <%= f.input :shoe_size, :as => :range, :in => 3..15, :step => 1 %>
65
+ # <%= f.input :shoe_size, :as => :range, :input_html => { :in => 3..15, :step => 1 } %>
66
+ #
67
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
68
+ # @see http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_numericality_of Rails' Numericality validation documentation
69
+ #
70
+ class RangeInput
71
+ include Base
72
+ include Base::Numeric
73
+
74
+ def to_html
75
+ input_wrapping do
76
+ label_html <<
77
+ builder.range_field(method, input_html_options)
78
+ end
79
+ end
80
+
81
+ def min_option
82
+ super || 1
83
+ end
84
+
85
+ def max_option
86
+ super || 100
87
+ end
88
+
89
+ def step_option
90
+ super || 1
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,41 @@
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 documentation of all possible options.
27
+ class SearchInput
28
+ include Base
29
+ include Base::Stringish
30
+ include Base::Placeholder
31
+
32
+ def to_html
33
+ input_wrapping do
34
+ label_html <<
35
+ builder.search_field(method, input_html_options)
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,235 @@
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 an `:integer` and there is an enum defined (`enum`)
12
+ # * the database column type is a `:string` and the `:collection` option is used
13
+ # * there an object with an association, but no database column on the object (`has_many`, etc)
14
+ # * there is no object and the `:collection` option is used
15
+ #
16
+ # The flexibility of the `:collection` option (see examples) makes the :select input viable as
17
+ # an alternative for many other input types. For example, instead of...
18
+ #
19
+ # * a `:string` input (where you want to force the user to choose from a few specific strings rather than entering anything)
20
+ # * a `:boolean` checkbox input (where the user could choose yes or no, rather than checking a box)
21
+ # * a `:date_select`, `:time_select` or `:datetime_select` input (where the user could choose from pre-selected dates)
22
+ # * a `:number` input (where the user could choose from a set of pre-defined numbers)
23
+ # * a `:time_zone` input (where you want to provide your own set of choices instead of relying on Rails)
24
+ # * a `:country` input (no need for a plugin really)
25
+ #
26
+ # Within the standard `<li>` wrapper, the output is a `<label>` tag followed by a `<select>`
27
+ # tag containing `<option>` tags.
28
+ #
29
+ # For inputs that map to associations on the object model, Formtastic will automatically load
30
+ # in a collection of objects on the association as options to choose from. This might be an
31
+ # `Author.all` on a `Post` form with an input for a `belongs_to :user` association, or a
32
+ # `Tag.all` for a `Post` form with an input for a `has_and_belongs_to_many :tags` association.
33
+ # You can override or customise this collection and the `<option>` tags it will render through
34
+ # the `:collection` option (see examples).
35
+ #
36
+ # The way on which Formtastic renders the `value` attribute and content of each `<option>` tag
37
+ # is customisable through the `:member_label` and `:member_value` options. When not provided,
38
+ # we fall back to a list of methods to try on each object such as `:to_label`, `:name` and
39
+ # `:to_s`, which are defined in the configurations `collection_label_methods` and
40
+ # `collection_value_methods` (see examples below).
41
+ #
42
+ # For select inputs that map to ActiveRecord `enum` attributes, Formtastic will automatically
43
+ # load in your enum options to be used as the select's options. This can be overridden with
44
+ # the `:collection` option, or augmented with I18n translations. See examples below.
45
+ # An error is raised if you try to render a multi-select with an enum, as ActiveRecord can
46
+ # only store one choice in the database.
47
+ #
48
+ #
49
+ # @example Basic `belongs_to` example with full form context
50
+ #
51
+ # <%= semantic_form_for @post do |f| %>
52
+ # <%= f.inputs do %>
53
+ # <%= f.input :author, :as => :select %>
54
+ # <% end %>
55
+ # <% end %>
56
+ #
57
+ # <form...>
58
+ # <fieldset>
59
+ # <ol>
60
+ # <li class='select'>
61
+ # <label for="post_author_id">Author</label>
62
+ # <select id="post_author_id" name="post[post_author_id]">
63
+ # <option value=""></option>
64
+ # <option value="1">Justin</option>
65
+ # <option value="3">Kate</option>
66
+ # <option value="2">Amelia</option>
67
+ # </select>
68
+ # </li>
69
+ # </ol>
70
+ # </fieldset>
71
+ # </form>
72
+ #
73
+ # @example Basic `has_many` or `has_and_belongs_to_many` example with full form context
74
+ #
75
+ # <%= semantic_form_for @post do |f| %>
76
+ # <%= f.inputs do %>
77
+ # <%= f.input :tags, :as => :select %>
78
+ # <% end %>
79
+ # <% end %>
80
+ #
81
+ # <form...>
82
+ # <fieldset>
83
+ # <ol>
84
+ # <li class='select'>
85
+ # <label for="post_tag_ids">Author</label>
86
+ # <select id="post_tag_ids" name="post[tag_ids]" multiple="true">
87
+ # <option value="1">Ruby</option>
88
+ # <option value="6">Rails</option>
89
+ # <option value="3">Forms</option>
90
+ # <option value="4">Awesome</option>
91
+ # </select>
92
+ # </li>
93
+ # </ol>
94
+ # </fieldset>
95
+ # </form>
96
+ #
97
+ # @example Override Formtastic's assumption on when you need a multi select
98
+ # <%= f.input :authors, :as => :select, :input_html => { :multiple => true } %>
99
+ # <%= f.input :authors, :as => :select, :input_html => { :multiple => false } %>
100
+ #
101
+ # @example The `:collection` option can be used to customize the choices
102
+ # <%= f.input :author, :as => :select, :collection => @authors %>
103
+ # <%= f.input :author, :as => :select, :collection => Author.all %>
104
+ # <%= f.input :author, :as => :select, :collection => Author.some_named_scope %>
105
+ # <%= f.input :author, :as => :select, :collection => Author.pluck(:full_name, :id) %>
106
+ # <%= f.input :author, :as => :select, :collection => Author.pluck(Arel.sql("CONCAT(`first_name`, ' ', `last_name`)"), :id)) %>
107
+ # <%= f.input :author, :as => :select, :collection => [Author.find_by_login("justin"), Category.find_by_name("kate")] %>
108
+ # <%= f.input :author, :as => :select, :collection => ["Justin", "Kate"] %>
109
+ # <%= f.input :author, :as => :select, :collection => [["Justin", "justin"], ["Kate", "kate"]] %>
110
+ # <%= f.input :author, :as => :select, :collection => [["Justin", "1"], ["Kate", "3"]] %>
111
+ # <%= f.input :author, :as => :select, :collection => [["Justin", 1], ["Kate", 3]] %>
112
+ # <%= f.input :author, :as => :select, :collection => 1..5 %>
113
+ # <%= f.input :author, :as => :select, :collection => "<option>your own options HTML string</option>" %>
114
+ # <%= f.input :author, :as => :select, :collection => options_for_select(...) %>
115
+ # <%= f.input :author, :as => :select, :collection => options_from_collection_for_select(...) %>
116
+ # <%= f.input :author, :as => :select, :collection => grouped_options_for_select(...) %>
117
+ # <%= f.input :author, :as => :select, :collection => time_zone_options_for_select(...) %>
118
+ #
119
+ # @example Set HTML attributes on the `<select>` tag with `:input_html`
120
+ # <%= f.input :authors, :as => :select, :input_html => { :size => 20, :multiple => true, :class => "special" } %>
121
+ #
122
+ # @example Set HTML attributes on the `<li>` wrapper with `:wrapper_html`
123
+ # <%= f.input :authors, :as => :select, :wrapper_html => { :class => "special" } %>
124
+ #
125
+ # @example Exclude, include, or customize the blank option at the top of the select. Always shown, even if the field already has a value. Suitable for optional inputs.
126
+ # <%= f.input :author, :as => :select, :include_blank => false %>
127
+ # <%= f.input :author, :as => :select, :include_blank => true %> => <option value=""></option>
128
+ # <%= f.input :author, :as => :select, :include_blank => "No author" %>
129
+ #
130
+ # @example Exclude, include, or customize the prompt at the top of the select. Only shown if the field does not have a value. Suitable for required inputs.
131
+ # <%= f.input :author, :as => :select, :prompt => false %>
132
+ # <%= f.input :author, :as => :select, :prompt => true %> => <option value="">Please select</option>
133
+ # <%= f.input :author, :as => :select, :prompt => "Please select an author" %>
134
+ #
135
+ # @example Using ActiveRecord enum attribute with i18n translation:
136
+ # # post.rb
137
+ # class Post < ActiveRecord::Base
138
+ # enum :status => [ :active, :archived ]
139
+ # end
140
+ # # en.yml
141
+ # en:
142
+ # activerecord:
143
+ # attributes:
144
+ # post:
145
+ # statuses:
146
+ # active: I am active!
147
+ # archived: I am archived!
148
+ # # form
149
+ # <%= f.input :status, :as => :select %>
150
+ #
151
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
152
+ # @see Formtastic::Inputs::CheckBoxesInput CheckBoxesInput as an alternative for `has_many` and `has_and_belongs_to_many` associations
153
+ # @see Formtastic::Inputs::RadioInput RadioInput as an alternative for `belongs_to` associations
154
+ #
155
+ # @todo Do/can we support the per-item HTML options like RadioInput?
156
+ class SelectInput
157
+ include Base
158
+ include Base::Collections
159
+
160
+ def initialize(*args)
161
+ super
162
+ raise Formtastic::UnsupportedEnumCollection if collection_from_enum? && multiple?
163
+ end
164
+
165
+ def to_html
166
+ input_wrapping do
167
+ label_html <<
168
+ select_html
169
+ end
170
+ end
171
+
172
+ def select_html
173
+ builder.select(input_name, collection, input_options, input_html_options)
174
+ end
175
+
176
+ def include_blank
177
+ options.key?(:include_blank) ? options[:include_blank] : (single? && builder.include_blank_for_select_by_default)
178
+ end
179
+
180
+ def prompt?
181
+ !!options[:prompt]
182
+ end
183
+
184
+ def label_html_options
185
+ super.merge(:for => input_html_options[:id])
186
+ end
187
+
188
+ def input_options
189
+ super.merge :include_blank => (include_blank unless prompt?)
190
+ end
191
+
192
+ def input_html_options
193
+ extra_input_html_options.merge(super.reject {|k,v| k==:name && v.nil?} )
194
+ end
195
+
196
+ def extra_input_html_options
197
+ {
198
+ :multiple => multiple?,
199
+ :name => (multiple? && Rails::VERSION::MAJOR >= 3) ? input_html_options_name_multiple : input_html_options_name
200
+ }
201
+
202
+
203
+ end
204
+
205
+ def input_html_options_name
206
+ if builder.options.key?(:index)
207
+ "#{object_name}[#{builder.options[:index]}][#{association_primary_key}]"
208
+ else
209
+ "#{object_name}[#{association_primary_key}]"
210
+ end
211
+ end
212
+
213
+ def input_html_options_name_multiple
214
+ input_html_options_name + "[]"
215
+ end
216
+
217
+ def multiple_by_association?
218
+ reflection && [ :has_many, :has_and_belongs_to_many ].include?(reflection.macro)
219
+ end
220
+
221
+ def multiple_by_options?
222
+ options[:multiple] || (options[:input_html] && options[:input_html][:multiple])
223
+ end
224
+
225
+ def multiple?
226
+ multiple_by_options? || multiple_by_association?
227
+ end
228
+
229
+ def single?
230
+ !multiple?
231
+ end
232
+
233
+ end
234
+ end
235
+ end