formtastic 2.1.0 → 4.0.0

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 (164) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/test.yml +61 -0
  4. data/.gitignore +4 -2
  5. data/CHANGELOG.md +52 -0
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +105 -0
  8. data/MIT-LICENSE +1 -1
  9. data/{README.textile → README.md} +204 -219
  10. data/RELEASE_PROCESS +3 -1
  11. data/Rakefile +27 -29
  12. data/app/assets/stylesheets/formtastic.css +3 -2
  13. data/bin/appraisal +8 -0
  14. data/formtastic.gemspec +11 -14
  15. data/gemfiles/rails_5.2/Gemfile +5 -0
  16. data/gemfiles/rails_6.0/Gemfile +5 -0
  17. data/gemfiles/rails_6.1/Gemfile +5 -0
  18. data/gemfiles/rails_edge/Gemfile +13 -0
  19. data/lib/formtastic/action_class_finder.rb +18 -0
  20. data/lib/formtastic/actions/button_action.rb +55 -60
  21. data/lib/formtastic/actions/input_action.rb +59 -57
  22. data/lib/formtastic/actions/link_action.rb +68 -67
  23. data/lib/formtastic/actions.rb +6 -3
  24. data/lib/formtastic/deprecation.rb +5 -0
  25. data/lib/formtastic/engine.rb +3 -1
  26. data/lib/formtastic/form_builder.rb +35 -16
  27. data/lib/formtastic/helpers/action_helper.rb +34 -28
  28. data/lib/formtastic/helpers/enum.rb +13 -0
  29. data/lib/formtastic/helpers/errors_helper.rb +2 -2
  30. data/lib/formtastic/helpers/fieldset_wrapper.rb +16 -12
  31. data/lib/formtastic/helpers/form_helper.rb +19 -16
  32. data/lib/formtastic/helpers/input_helper.rb +69 -97
  33. data/lib/formtastic/helpers/inputs_helper.rb +35 -25
  34. data/lib/formtastic/helpers/reflection.rb +4 -4
  35. data/lib/formtastic/helpers.rb +1 -2
  36. data/lib/formtastic/html_attributes.rb +12 -1
  37. data/lib/formtastic/i18n.rb +1 -1
  38. data/lib/formtastic/input_class_finder.rb +18 -0
  39. data/lib/formtastic/inputs/base/choices.rb +2 -2
  40. data/lib/formtastic/inputs/base/collections.rb +46 -14
  41. data/lib/formtastic/inputs/base/database.rb +7 -2
  42. data/lib/formtastic/inputs/base/datetime_pickerish.rb +85 -0
  43. data/lib/formtastic/inputs/base/errors.rb +7 -7
  44. data/lib/formtastic/inputs/base/hints.rb +2 -2
  45. data/lib/formtastic/inputs/base/html.rb +10 -9
  46. data/lib/formtastic/inputs/base/labelling.rb +5 -8
  47. data/lib/formtastic/inputs/base/naming.rb +4 -4
  48. data/lib/formtastic/inputs/base/numeric.rb +1 -1
  49. data/lib/formtastic/inputs/base/options.rb +3 -4
  50. data/lib/formtastic/inputs/base/stringish.rb +10 -2
  51. data/lib/formtastic/inputs/base/timeish.rb +34 -22
  52. data/lib/formtastic/inputs/base/validations.rb +41 -13
  53. data/lib/formtastic/inputs/base/wrapping.rb +29 -26
  54. data/lib/formtastic/inputs/base.rb +22 -15
  55. data/lib/formtastic/inputs/boolean_input.rb +26 -12
  56. data/lib/formtastic/inputs/check_boxes_input.rb +39 -31
  57. data/lib/formtastic/inputs/color_input.rb +41 -0
  58. data/lib/formtastic/inputs/country_input.rb +24 -5
  59. data/lib/formtastic/inputs/datalist_input.rb +41 -0
  60. data/lib/formtastic/inputs/date_picker_input.rb +93 -0
  61. data/lib/formtastic/inputs/{date_input.rb → date_select_input.rb} +1 -1
  62. data/lib/formtastic/inputs/datetime_picker_input.rb +103 -0
  63. data/lib/formtastic/inputs/{datetime_input.rb → datetime_select_input.rb} +1 -1
  64. data/lib/formtastic/inputs/file_input.rb +2 -2
  65. data/lib/formtastic/inputs/hidden_input.rb +2 -6
  66. data/lib/formtastic/inputs/radio_input.rb +28 -22
  67. data/lib/formtastic/inputs/select_input.rb +36 -39
  68. data/lib/formtastic/inputs/time_picker_input.rb +99 -0
  69. data/lib/formtastic/inputs/{time_input.rb → time_select_input.rb} +6 -2
  70. data/lib/formtastic/inputs/time_zone_input.rb +16 -6
  71. data/lib/formtastic/inputs.rb +32 -21
  72. data/lib/formtastic/localized_string.rb +1 -1
  73. data/lib/formtastic/localizer.rb +24 -24
  74. data/lib/formtastic/namespaced_class_finder.rb +99 -0
  75. data/lib/formtastic/version.rb +1 -1
  76. data/lib/formtastic.rb +20 -10
  77. data/lib/generators/formtastic/form/form_generator.rb +10 -4
  78. data/lib/generators/formtastic/input/input_generator.rb +46 -0
  79. data/lib/generators/formtastic/install/install_generator.rb +5 -19
  80. data/lib/generators/templates/_form.html.slim +2 -2
  81. data/lib/generators/templates/formtastic.rb +46 -25
  82. data/lib/generators/templates/input.rb +19 -0
  83. data/sample/basic_inputs.html +23 -3
  84. data/script/integration-template.rb +74 -0
  85. data/script/integration.sh +19 -0
  86. data/spec/action_class_finder_spec.rb +12 -0
  87. data/spec/actions/button_action_spec.rb +8 -8
  88. data/spec/actions/generic_action_spec.rb +92 -56
  89. data/spec/actions/input_action_spec.rb +7 -7
  90. data/spec/actions/link_action_spec.rb +10 -10
  91. data/spec/builder/custom_builder_spec.rb +36 -20
  92. data/spec/builder/error_proc_spec.rb +4 -4
  93. data/spec/builder/semantic_fields_for_spec.rb +28 -29
  94. data/spec/fast_spec_helper.rb +12 -0
  95. data/spec/generators/formtastic/form/form_generator_spec.rb +45 -32
  96. data/spec/generators/formtastic/input/input_generator_spec.rb +124 -0
  97. data/spec/generators/formtastic/install/install_generator_spec.rb +9 -9
  98. data/spec/helpers/action_helper_spec.rb +75 -103
  99. data/spec/helpers/actions_helper_spec.rb +17 -17
  100. data/spec/helpers/form_helper_spec.rb +84 -33
  101. data/spec/helpers/input_helper_spec.rb +333 -285
  102. data/spec/helpers/inputs_helper_spec.rb +167 -121
  103. data/spec/helpers/reflection_helper_spec.rb +3 -3
  104. data/spec/helpers/semantic_errors_helper_spec.rb +23 -23
  105. data/spec/i18n_spec.rb +26 -26
  106. data/spec/input_class_finder_spec.rb +10 -0
  107. data/spec/inputs/base/collections_spec.rb +76 -0
  108. data/spec/inputs/base/validations_spec.rb +480 -0
  109. data/spec/inputs/boolean_input_spec.rb +100 -65
  110. data/spec/inputs/check_boxes_input_spec.rb +200 -101
  111. data/spec/inputs/color_input_spec.rb +85 -0
  112. data/spec/inputs/country_input_spec.rb +20 -20
  113. data/spec/inputs/custom_input_spec.rb +3 -4
  114. data/spec/inputs/datalist_input_spec.rb +61 -0
  115. data/spec/inputs/date_picker_input_spec.rb +449 -0
  116. data/spec/inputs/date_select_input_spec.rb +249 -0
  117. data/spec/inputs/datetime_picker_input_spec.rb +490 -0
  118. data/spec/inputs/datetime_select_input_spec.rb +209 -0
  119. data/spec/inputs/email_input_spec.rb +5 -5
  120. data/spec/inputs/file_input_spec.rb +6 -6
  121. data/spec/inputs/hidden_input_spec.rb +22 -35
  122. data/spec/inputs/include_blank_spec.rb +11 -11
  123. data/spec/inputs/label_spec.rb +62 -25
  124. data/spec/inputs/number_input_spec.rb +112 -112
  125. data/spec/inputs/password_input_spec.rb +5 -5
  126. data/spec/inputs/phone_input_spec.rb +5 -5
  127. data/spec/inputs/placeholder_spec.rb +6 -6
  128. data/spec/inputs/radio_input_spec.rb +99 -55
  129. data/spec/inputs/range_input_spec.rb +66 -66
  130. data/spec/inputs/readonly_spec.rb +50 -0
  131. data/spec/inputs/search_input_spec.rb +5 -5
  132. data/spec/inputs/select_input_spec.rb +170 -170
  133. data/spec/inputs/string_input_spec.rb +68 -16
  134. data/spec/inputs/text_input_spec.rb +16 -16
  135. data/spec/inputs/time_picker_input_spec.rb +455 -0
  136. data/spec/inputs/time_select_input_spec.rb +261 -0
  137. data/spec/inputs/time_zone_input_spec.rb +54 -28
  138. data/spec/inputs/url_input_spec.rb +5 -5
  139. data/spec/inputs/with_options_spec.rb +7 -7
  140. data/spec/localizer_spec.rb +39 -17
  141. data/spec/namespaced_class_finder_spec.rb +79 -0
  142. data/spec/schema.rb +21 -0
  143. data/spec/spec_helper.rb +254 -221
  144. data/spec/support/custom_macros.rb +128 -95
  145. data/spec/support/shared_examples.rb +12 -0
  146. data/spec/support/specialized_class_finder_shared_example.rb +27 -0
  147. data/spec/support/test_environment.rb +26 -10
  148. metadata +177 -238
  149. data/.travis.yml +0 -8
  150. data/Appraisals +0 -11
  151. data/CHANGELOG +0 -371
  152. data/gemfiles/rails-3.0.gemfile +0 -7
  153. data/gemfiles/rails-3.1.gemfile +0 -7
  154. data/gemfiles/rails-3.2.gemfile +0 -7
  155. data/lib/formtastic/helpers/buttons_helper.rb +0 -310
  156. data/lib/formtastic/inputs/base/grouped_collections.rb +0 -77
  157. data/lib/formtastic/util.rb +0 -25
  158. data/lib/tasks/verify_rcov.rb +0 -44
  159. data/spec/helpers/buttons_helper_spec.rb +0 -166
  160. data/spec/helpers/commit_button_helper_spec.rb +0 -530
  161. data/spec/inputs/date_input_spec.rb +0 -227
  162. data/spec/inputs/datetime_input_spec.rb +0 -185
  163. data/spec/inputs/time_input_spec.rb +0 -267
  164. data/spec/support/deferred_garbage_collection.rb +0 -21
@@ -4,13 +4,15 @@ module Formtastic
4
4
  # Rails doesn't come with a `country_select` helper by default any more, so you'll need to do
5
5
  # one of the following:
6
6
  #
7
- # * install the [country-select](https://github.com/jamesds/country-select) gem
8
- # * install the no longer maintained [official Rails plugin](http://github.com/rails/iso-3166-country-select)
7
+ # * install the [country_select](https://github.com/stefanpenner/country_select) gem
9
8
  # * install any other country_select plugin that behaves in a similar way
10
9
  # * roll your own `country_select` helper with the same args and options as the Rails one
11
10
  #
11
+ # Formtastic supports both 1.x and 2.x of stefanpenner/country_select, but if you're upgrading
12
+ # from 1.x, they behave quite differently, so please see their [upgrade instructions](https://github.com/stefanpenner/country_select/blob/master/UPGRADING.md).
13
+ #
12
14
  # By default, Formtastic includes a handful of English-speaking countries as "priority
13
- # counties", which can be set in the `priority_countries` configuration array in the
15
+ # countries", which can be set in the `priority_countries` configuration array in the
14
16
  # formtastic.rb initializer to suit your market and user base (see README for more info on
15
17
  # configuration). Additionally, it is possible to set the :priority_countries on a per-input
16
18
  # basis through the `:priority_countries` option. These priority countries will be passed down
@@ -31,7 +33,7 @@ module Formtastic
31
33
  # # ...
32
34
  # </li>
33
35
  #
34
- # @example `:priority_countries` set on a specific input
36
+ # @example `:priority_countries` set on a specific input (country_select 1.x)
35
37
  #
36
38
  # <%= semantic_form_for @user do |f| %>
37
39
  # <%= f.inputs do %>
@@ -46,12 +48,29 @@ module Formtastic
46
48
  # # ...
47
49
  # </li>
48
50
  #
51
+ # @example `:priority_countries` set on a specific input (country_select 2.x)
52
+ #
53
+ # <%= semantic_form_for @user do |f| %>
54
+ # <%= f.inputs do %>
55
+ # <%= f.input :nationality, :as => :country, :priority_countries => ["AU", "NZ"] %>
56
+ # <% end %>
57
+ # <% end %>
58
+ #
59
+ # <li class='country'>
60
+ # <label for="user_nationality">Country</label>
61
+ # <select id="user_nationality" name="user[nationality]">
62
+ # <option value="...">...</option>
63
+ # # ...
64
+ # </li>
65
+ #
49
66
  # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
50
67
  class CountryInput
51
68
  include Base
52
69
 
70
+ CountrySelectPluginMissing = Class.new(StandardError)
71
+
53
72
  def to_html
54
- raise "To use the :country input, please install a country_select plugin, like this one: https://github.com/jamesds/country-select" unless builder.respond_to?(:country_select)
73
+ raise CountrySelectPluginMissing, "To use the :country input, please install a country_select plugin, like this one: https://github.com/stefanpenner/country_select" unless builder.respond_to?(:country_select)
55
74
  input_wrapping do
56
75
  label_html <<
57
76
  builder.country_select(method, priority_countries, input_options, input_html_options)
@@ -0,0 +1,41 @@
1
+ module Formtastic
2
+ module Inputs
3
+ # Outputs a label and a text field, along with a datalist tag
4
+ # datalist tag provides a list of options which drives a simple autocomplete
5
+ # on the text field. This is a HTML5 feature, more info can be found at
6
+ # {https://developer.mozilla.org/en/docs/Web/HTML/Element/datalist <datalist> at MDN}
7
+ # This input accepts a :collection option which takes data in all the usual formats accepted by
8
+ # {http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_for_select options_for_select}
9
+ #
10
+ # @example Input is used as follows
11
+ # f.input :fav_book, :as => :datalist, :collection => Book.pluck(:name)
12
+ #
13
+ class DatalistInput
14
+ include Base
15
+ include Base::Stringish
16
+ include Base::Collections
17
+
18
+ def to_html
19
+ @name = input_html_options[:id].gsub(/_id$/, "")
20
+ input_wrapping do
21
+ label_html <<
22
+ builder.text_field(method, input_html_options) << # standard input
23
+ data_list_html # append new datalist element
24
+ end
25
+ end
26
+
27
+ def input_html_options
28
+ super.merge(:list => html_id_of_datalist)
29
+ end
30
+
31
+ def html_id_of_datalist
32
+ "#{@name}_datalist"
33
+ end
34
+
35
+ def data_list_html
36
+ html = builder.template.options_for_select(collection)
37
+ builder.template.content_tag(:datalist,html, { :id => html_id_of_datalist }, false)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,93 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a HTML5 `<input type="date">` wrapped in the standard
5
+ # `<li>` wrapper. This is an alternative to `:date_select` for `:date`, `:time`, `:datetime`
6
+ # database columns. You can use this input with `:as => :date_picker`.
7
+ #
8
+ # *Please note:* Formtastic only provides suitable markup for a date picker, but does not supply
9
+ # any additional CSS or Javascript to render calendar-style date pickers. Browsers that support
10
+ # this input type (such as Mobile Webkit and Opera on the desktop) will render a native widget.
11
+ # Browsers that don't will default to a plain text field`<input type="text">` and can be
12
+ # poly-filled with some Javascript and a UI library of your choice.
13
+ #
14
+ # @example Full form context and output
15
+ #
16
+ # <%= semantic_form_for(@post) do |f| %>
17
+ # <%= f.inputs do %>
18
+ # <%= f.input :publish_at, :as => :date_picker %>
19
+ # <% end %>
20
+ # <% end %>
21
+ #
22
+ # <form...>
23
+ # <fieldset>
24
+ # <ol>
25
+ # <li class="string">
26
+ # <label for="post_publish_at">First name</label>
27
+ # <input type="date" id="post_publish_at" name="post[publish_at]">
28
+ # </li>
29
+ # </ol>
30
+ # </fieldset>
31
+ # </form>
32
+ #
33
+ # @example Setting the size (defaults to 10 for YYYY-MM-DD)
34
+ # <%= f.input :publish_at, :as => :date_picker, :size => 20 %>
35
+ # <%= f.input :publish_at, :as => :date_picker, :input_html => { :size => 20 } %>
36
+ #
37
+ # @example Setting the maxlength (defaults to 10 for YYYY-MM-DD)
38
+ # <%= f.input :publish_at, :as => :date_picker, :maxlength => 20 %>
39
+ # <%= f.input :publish_at, :as => :date_picker, :input_html => { :maxlength => 20 } %>
40
+ #
41
+ # @example Setting the value (defaults to YYYY-MM-DD for Date and Time objects, otherwise renders string)
42
+ # <%= f.input :publish_at, :as => :date_picker, :input_html => { :value => "1970-01-01" } %>
43
+ #
44
+ # @example Setting the step attribute (defaults to 1)
45
+ # <%= f.input :publish_at, :as => :date_picker, :step => 7 %>
46
+ # <%= f.input :publish_at, :as => :date_picker, :input_html => { :step => 7 } %>
47
+ #
48
+ # @example Setting the step attribute with a macro
49
+ # <%= f.input :publish_at, :as => :date_picker, :step => :day %>
50
+ # <%= f.input :publish_at, :as => :date_picker, :step => :week %>
51
+ # <%= f.input :publish_at, :as => :date_picker, :step => :seven_days %>
52
+ # <%= f.input :publish_at, :as => :date_picker, :step => :fortnight %>
53
+ # <%= f.input :publish_at, :as => :date_picker, :step => :two_weeks %>
54
+ # <%= f.input :publish_at, :as => :date_picker, :step => :four_weeks %>
55
+ # <%= f.input :publish_at, :as => :date_picker, :step => :thirty_days %>
56
+ #
57
+ # @example Setting the min attribute
58
+ # <%= f.input :publish_at, :as => :date_picker, :min => "2012-01-01" %>
59
+ # <%= f.input :publish_at, :as => :date_picker, :input_html => { :min => "2012-01-01" } %>
60
+ #
61
+ # @example Setting the max attribute
62
+ # <%= f.input :publish_at, :as => :date_picker, :max => "2012-12-31" %>
63
+ # <%= f.input :publish_at, :as => :date_picker, :input_html => { :max => "2012-12-31" } %>
64
+ #
65
+ # @example Setting the placeholder attribute
66
+ # <%= f.input :publish_at, :as => :date_picker, :placeholder => 20 %>
67
+ # <%= f.input :publish_at, :as => :date_picker, :input_html => { :placeholder => "YYYY-MM-DD" } %>
68
+ #
69
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
70
+ class DatePickerInput
71
+ include Base
72
+ include Base::Stringish
73
+ include Base::DatetimePickerish
74
+
75
+ def html_input_type
76
+ "date"
77
+ end
78
+
79
+ def default_size
80
+ 10
81
+ end
82
+
83
+ def value
84
+ return options[:input_html][:value] if options[:input_html] && options[:input_html].key?(:value)
85
+ val = object.send(method)
86
+ return Date.new(val.year, val.month, val.day).to_s if val.is_a?(Time)
87
+ return val if val.nil?
88
+ val.to_s
89
+ end
90
+
91
+ end
92
+ end
93
+ end
@@ -3,7 +3,7 @@ module Formtastic
3
3
  # Outputs a series of select boxes for the fragments that make up a date (year, month, day).
4
4
  #
5
5
  # @see Formtastic::Inputs::Base::Timeish Timeish module for documentation of date, time and datetime input options.
6
- class DateInput
6
+ class DateSelectInput
7
7
  include Base
8
8
  include Base::Timeish
9
9
 
@@ -0,0 +1,103 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a HTML5 `<input type="datetime-local">` (or
5
+ # `<input type="datetime">`) wrapped in the standard `<li>` wrapper. This is an alternative to
6
+ # `:date_select` for `:date`, `:time`, `:datetime` database columns. You can use this input with
7
+ # `:as => :datetime_picker`.
8
+ #
9
+ # *Please note:* Formtastic only provides suitable markup for a date picker, but does not supply
10
+ # any additional CSS or Javascript to render calendar-style date pickers. Browsers that support
11
+ # this input type (such as Mobile Webkit and Opera on the desktop) will render a native widget.
12
+ # Browsers that don't will default to a plain text field`<input type="text">` and can be
13
+ # poly-filled with some Javascript and a UI library of your choice.
14
+ #
15
+ # @example Full form context and output
16
+ #
17
+ # <%= semantic_form_for(@post) do |f| %>
18
+ # <%= f.inputs do %>
19
+ # <%= f.input :publish_at, :as => :datetime_picker %>
20
+ # <% end %>
21
+ # <% end %>
22
+ #
23
+ # <form...>
24
+ # <fieldset>
25
+ # <ol>
26
+ # <li class="string">
27
+ # <label for="post_publish_at">First name</label>
28
+ # <input type="date" id="post_publish_at" name="post[publish_at]">
29
+ # </li>
30
+ # </ol>
31
+ # </fieldset>
32
+ # </form>
33
+ #
34
+ # @example Setting the size (defaults to 16 for YYYY-MM-DD HH:MM)
35
+ # <%= f.input :publish_at, :as => :datetime_picker, :size => 20 %>
36
+ # <%= f.input :publish_at, :as => :datetime_picker, :input_html => { :size => 20 } %>
37
+ #
38
+ # @example Setting the maxlength (defaults to 16 for YYYY-MM-DD HH:MM)
39
+ # <%= f.input :publish_at, :as => :datetime_picker, :maxlength => 20 %>
40
+ # <%= f.input :publish_at, :as => :datetime_picker, :input_html => { :maxlength => 20 } %>
41
+ #
42
+ # @example Setting the value (defaults to YYYY-MM-DD HH:MM for Date and Time objects, otherwise renders string)
43
+ # <%= f.input :publish_at, :as => :datetime_picker, :input_html => { :value => "1970-01-01 00:00" } %>
44
+ #
45
+ # @example Setting the step attribute (defaults to 1)
46
+ # <%= f.input :publish_at, :as => :datetime_picker, :step => 60 %>
47
+ # <%= f.input :publish_at, :as => :datetime_picker, :input_html => { :step => 60 } %>
48
+ #
49
+ # @example Setting the step attribute with a macro
50
+ # <%= f.input :publish_at, :as => :datetime_picker, :step => :second %>
51
+ # <%= f.input :publish_at, :as => :datetime_picker, :step => :minute %>
52
+ # <%= f.input :publish_at, :as => :datetime_picker, :step => :quarter_hour %>
53
+ # <%= f.input :publish_at, :as => :datetime_picker, :step => :fifteen_minutes %>
54
+ # <%= f.input :publish_at, :as => :datetime_picker, :step => :half_hour %>
55
+ # <%= f.input :publish_at, :as => :datetime_picker, :step => :thirty_minutes %>
56
+ # <%= f.input :publish_at, :as => :datetime_picker, :step => :hour %>
57
+ # <%= f.input :publish_at, :as => :datetime_picker, :step => :sixty_minutes %>
58
+ #
59
+ # @example Setting the min attribute
60
+ # <%= f.input :publish_at, :as => :datetime_picker, :min => "2012-01-01 09:00" %>
61
+ # <%= f.input :publish_at, :as => :datetime_picker, :input_html => { :min => "2012-01-01 09:00" } %>
62
+ #
63
+ # @example Setting the max attribute
64
+ # <%= f.input :publish_at, :as => :datetime_picker, :max => "2012-12-31 16:00" %>
65
+ # <%= f.input :publish_at, :as => :datetime_picker, :input_html => { :max => "2012-12-31 16:00" } %>
66
+ #
67
+ # @example Setting the placeholder attribute
68
+ # <%= f.input :publish_at, :as => :datetime_picker, :placeholder => "YYYY-MM-DD HH:MM" %>
69
+ # <%= f.input :publish_at, :as => :datetime_picker, :input_html => { :placeholder => "YYYY-MM-DD HH:MM" } %>
70
+ #
71
+ # @example Using `type=datetime-local` with `:local` option (this is the default, and recommended for browser support on iOS7 and Chrome)
72
+ # <%= f.input :publish_at, :as => :datetime_picker, :local => true %>
73
+ #
74
+ # @example Using `type=datetime` with `:local` option (not recommended)
75
+ # <%= f.input :publish_at, :as => :datetime_picker, :local => false %>
76
+ #
77
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
78
+ class DatetimePickerInput
79
+ include Base
80
+ include Base::Stringish
81
+ include Base::DatetimePickerish
82
+
83
+ def html_input_type
84
+ options[:local] = true unless options.key?(:local)
85
+ options[:local] ? "datetime-local" : "datetime"
86
+ end
87
+
88
+ def default_size
89
+ 16
90
+ end
91
+
92
+ def value
93
+ return options[:input_html][:value] if options[:input_html] && options[:input_html].key?(:value)
94
+ val = object.send(method)
95
+ return val.strftime("%Y-%m-%dT%H:%M:%S") if val.is_a?(Time)
96
+ return "#{val.year}-#{val.month}-#{val.day}T00:00:00" if val.is_a?(Date)
97
+ return val if val.nil?
98
+ val.to_s
99
+ end
100
+
101
+ end
102
+ end
103
+ end
@@ -4,7 +4,7 @@ module Formtastic
4
4
  # Outputs a series of select boxes for the fragments that make up a date and time (year, month, day, hour, minute, second).
5
5
  #
6
6
  # @see Formtastic::Inputs::Base::Timeish Timeish module for documentation of date, time and datetime input options.
7
- class DatetimeInput
7
+ class DatetimeSelectInput
8
8
  include Base
9
9
  include Base::Timeish
10
10
  end
@@ -20,7 +20,7 @@ module Formtastic
20
20
  # <form...>
21
21
  # <fieldset>
22
22
  # <ol>
23
- # <li class="email">
23
+ # <li class="file">
24
24
  # <label for="user_avatar">Avatar</label>
25
25
  # <input type="file" id="user_avatar" name="user[avatar]">
26
26
  # </li>
@@ -29,7 +29,7 @@ module Formtastic
29
29
  # </form>
30
30
  #
31
31
  # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
32
- class FileInput
32
+ class FileInput
33
33
  include Base
34
34
  def to_html
35
35
  input_wrapping do
@@ -31,12 +31,8 @@ module Formtastic
31
31
  class HiddenInput
32
32
  include Base
33
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
34
  def input_html_options
39
- options.slice(:value).merge(super).merge(:required => nil).merge(:autofocus => nil)
35
+ super.merge(:required => nil).merge(:autofocus => nil)
40
36
  end
41
37
 
42
38
  def to_html
@@ -63,4 +59,4 @@ module Formtastic
63
59
 
64
60
  end
65
61
  end
66
- end
62
+ end
@@ -20,7 +20,7 @@ module Formtastic
20
20
  #
21
21
  # * a `:string` input (where you want to force the user to choose from a few specific strings rather than entering anything)
22
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)
23
+ # * a `:date_select`, `:time_select` or `:datetime_select` input (where the user could choose from a small set of pre-determined dates)
24
24
  # * a `:number` input (where the user could choose from a small set of pre-defined numbers)
25
25
  # * a `:time_zone` input (where you want to provide your own small set of choices instead of relying on Rails)
26
26
  # * a `:country` input (where you want to provide a small set of choices, no need for a plugin really)
@@ -31,10 +31,13 @@ module Formtastic
31
31
  # `Section.all` for a `Post` form with an input for a `belongs_to :section` association.
32
32
  # You can override or customise this collection through the `:collection` option (see examples).
33
33
  #
34
- # The way on which Formtastic renders the `value` attribute and label for each choice is
35
- # customisable through the `:member_label` and `:member_value` 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
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
38
41
  # `collection_label_methods` and `collection_value_methods`.
39
42
  #
40
43
  # @example Basic `belongs_to` example with full form context
@@ -78,30 +81,17 @@ module Formtastic
78
81
  # <%= f.input :author, :as => :radio, :collection => @authors %>
79
82
  # <%= f.input :author, :as => :radio, :collection => Author.all %>
80
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)) %>
81
86
  # <%= f.input :author, :as => :radio, :collection => [Author.find_by_login("justin"), Category.find_by_name("kate")] %>
82
87
  # <%= f.input :author, :as => :radio, :collection => ["Justin", "Kate"] %>
83
88
  # <%= f.input :author, :as => :radio, :collection => [["Justin", "justin"], ["Kate", "kate"]] %>
84
89
  # <%= f.input :author, :as => :radio, :collection => [["Justin", "1"], ["Kate", "3"]] %>
85
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] %>
86
93
  # <%= f.input :author, :as => :radio, :collection => 1..5 %>
87
94
  #
88
- # @example The `:member_label` 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, :member_label => :name %>
90
- # <%= f.input :author, :as => :radio, :member_label => :name_with_post_count
91
- # <%= f.input :author, :as => :radio, :member_label => Proc.new { |a| "#{c.name} (#{pluralize("post", a.posts.count)})" }
92
- #
93
- # @example `:member_label` can be used with a helper method (both examples have the same result)
94
- # <%= f.input :author, :as => :radio, :member_label => method(:fancy_label)
95
- # <%= f.input :author, :as => :radio, :member_label => Proc.new { |author| fancy_label(author) }
96
- #
97
- # @example The `:member_value` 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)
98
- # <%= f.input :author, :as => :radio, :member_value => :login %>
99
- # <%= f.input :author, :as => :radio, :member_value => Proc.new { |c| c.full_name.downcase.underscore }
100
- #
101
- # @example `:member_value` can be used with a helper method (both examples have the same result)
102
- # <%= f.input :author, :as => :radio, :member_value => method(:some_helper)
103
- # <%= f.input :author, :as => :radio, :member_value => Proc.new { |author| some_helper(author) }
104
- #
105
95
  # @example Set HTML attributes on each `<input type="radio">` tag with `:input_html`
106
96
  # <%= f.input :author, :as => :radio, :input_html => { :size => 20, :multiple => true, :class => "special" } %>
107
97
  #
@@ -114,6 +104,22 @@ module Formtastic
114
104
  # @example Set HTML options on a specific radio input option with a 3rd element in the array for a collection member
115
105
  # <%= f.input :author, :as => :radio, :collection => [["Test", 'test'], ["Try", "try", {:disabled => true}]]
116
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
+ #
117
123
  # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
118
124
  # @see Formtastic::Inputs::RadioInput as an alternative for `belongs_to` associations
119
125
  #
@@ -8,6 +8,7 @@ module Formtastic
8
8
  # This is the default input choice when:
9
9
  #
10
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`)
11
12
  # * the database column type is a `:string` and the `:collection` option is used
12
13
  # * there an object with an association, but no database column on the object (`has_many`, etc)
13
14
  # * there is no object and the `:collection` option is used
@@ -17,7 +18,7 @@ module Formtastic
17
18
  #
18
19
  # * a `:string` input (where you want to force the user to choose from a few specific strings rather than entering anything)
19
20
  # * 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 `:date_select`, `:time_select` or `:datetime_select` input (where the user could choose from pre-selected dates)
21
22
  # * a `:number` input (where the user could choose from a set of pre-defined numbers)
22
23
  # * a `:time_zone` input (where you want to provide your own set of choices instead of relying on Rails)
23
24
  # * a `:country` input (no need for a plugin really)
@@ -38,6 +39,13 @@ module Formtastic
38
39
  # `:to_s`, which are defined in the configurations `collection_label_methods` and
39
40
  # `collection_value_methods` (see examples below).
40
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
+ #
41
49
  # @example Basic `belongs_to` example with full form context
42
50
  #
43
51
  # <%= semantic_form_for @post do |f| %>
@@ -94,6 +102,8 @@ module Formtastic
94
102
  # <%= f.input :author, :as => :select, :collection => @authors %>
95
103
  # <%= f.input :author, :as => :select, :collection => Author.all %>
96
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)) %>
97
107
  # <%= f.input :author, :as => :select, :collection => [Author.find_by_login("justin"), Category.find_by_name("kate")] %>
98
108
  # <%= f.input :author, :as => :select, :collection => ["Justin", "Kate"] %>
99
109
  # <%= f.input :author, :as => :select, :collection => [["Justin", "justin"], ["Kate", "kate"]] %>
@@ -106,15 +116,6 @@ module Formtastic
106
116
  # <%= f.input :author, :as => :select, :collection => grouped_options_for_select(...) %>
107
117
  # <%= f.input :author, :as => :select, :collection => time_zone_options_for_select(...) %>
108
118
  #
109
- # @example The `:member_label` 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, :member_label => :name %>
111
- # <%= f.input :author, :as => :select, :member_label => :name_with_post_count %>
112
- # <%= f.input :author, :as => :select, :member_label => Proc.new { |a| "#{c.name} (#{pluralize("post", a.posts.count)})" } %>
113
- #
114
- # @example The `:member_value` 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, :member_value => :login %>
116
- # <%= f.input :author, :as => :select, :member_value => Proc.new { |c| c.full_name.downcase.underscore } %>
117
- #
118
119
  # @example Set HTML attributes on the `<select>` tag with `:input_html`
119
120
  # <%= f.input :authors, :as => :select, :input_html => { :size => 20, :multiple => true, :class => "special" } %>
120
121
  #
@@ -131,9 +132,21 @@ module Formtastic
131
132
  # <%= f.input :author, :as => :select, :prompt => true %> => <option value="">Please select</option>
132
133
  # <%= f.input :author, :as => :select, :prompt => "Please select an author" %>
133
134
  #
134
- #
135
- # @example Group options an `<optgroup>` with the `:group_by` and `:group_label` options (`belongs_to` associations only)
136
- # <%= f.input :author, :as => :select, :group_by => :continent %>
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 %>
137
150
  #
138
151
  # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
139
152
  # @see Formtastic::Inputs::CheckBoxesInput CheckBoxesInput as an alternative for `has_many` and `has_and_belongs_to_many` associations
@@ -143,13 +156,16 @@ module Formtastic
143
156
  class SelectInput
144
157
  include Base
145
158
  include Base::Collections
146
- include Base::GroupedCollections
159
+
160
+ def initialize(*args)
161
+ super
162
+ raise Formtastic::UnsupportedEnumCollection if collection_from_enum? && multiple?
163
+ end
147
164
 
148
165
  def to_html
149
166
  input_wrapping do
150
- hidden_input <<
151
167
  label_html <<
152
- (options[:group_by] ? grouped_select_html : select_html)
168
+ select_html
153
169
  end
154
170
  end
155
171
 
@@ -157,31 +173,10 @@ module Formtastic
157
173
  builder.select(input_name, collection, input_options, input_html_options)
158
174
  end
159
175
 
160
- def grouped_select_html
161
- builder.grouped_collection_select(
162
- input_name,
163
- grouped_collection,
164
- group_association,
165
- group_label_method,
166
- value_method,
167
- label_method,
168
- input_options,
169
- input_html_options
170
- )
171
- end
172
-
173
176
  def include_blank
174
177
  options.key?(:include_blank) ? options[:include_blank] : (single? && builder.include_blank_for_select_by_default)
175
178
  end
176
179
 
177
- def hidden_input
178
- if multiple?
179
- template.hidden_field_tag(input_html_options_name_multiple, '', :id => nil)
180
- else
181
- "".html_safe
182
- end
183
- end
184
-
185
180
  def prompt?
186
181
  !!options[:prompt]
187
182
  end
@@ -195,7 +190,7 @@ module Formtastic
195
190
  end
196
191
 
197
192
  def input_html_options
198
- extra_input_html_options.merge(super)
193
+ extra_input_html_options.merge(super.reject {|k,v| k==:name && v.nil?} )
199
194
  end
200
195
 
201
196
  def extra_input_html_options
@@ -203,6 +198,8 @@ module Formtastic
203
198
  :multiple => multiple?,
204
199
  :name => multiple? ? input_html_options_name_multiple : input_html_options_name
205
200
  }
201
+
202
+
206
203
  end
207
204
 
208
205
  def input_html_options_name
@@ -235,4 +232,4 @@ module Formtastic
235
232
 
236
233
  end
237
234
  end
238
- end
235
+ end