formtastic 2.1.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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