formtastic 3.0.0 → 5.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 (175) hide show
  1. checksums.yaml +5 -13
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/test.yml +61 -0
  4. data/.gitignore +3 -2
  5. data/CHANGELOG.md +61 -0
  6. data/Gemfile.lock +140 -0
  7. data/MIT-LICENSE +1 -1
  8. data/{README.textile → README.md} +191 -168
  9. data/RELEASE_PROCESS +3 -1
  10. data/Rakefile +24 -8
  11. data/app/assets/stylesheets/formtastic.css +1 -1
  12. data/bin/appraisal +8 -0
  13. data/formtastic.gemspec +13 -17
  14. data/gemfiles/rails_6.0/Gemfile +5 -0
  15. data/gemfiles/rails_6.1/Gemfile +5 -0
  16. data/gemfiles/rails_7.0/Gemfile +5 -0
  17. data/gemfiles/rails_7.1/Gemfile +5 -0
  18. data/gemfiles/rails_edge/Gemfile +13 -0
  19. data/lib/formtastic/action_class_finder.rb +19 -0
  20. data/lib/formtastic/actions/base.rb +1 -0
  21. data/lib/formtastic/actions/button_action.rb +56 -53
  22. data/lib/formtastic/actions/buttonish.rb +1 -0
  23. data/lib/formtastic/actions/input_action.rb +60 -57
  24. data/lib/formtastic/actions/link_action.rb +69 -67
  25. data/lib/formtastic/actions.rb +7 -3
  26. data/lib/formtastic/deprecation.rb +6 -0
  27. data/lib/formtastic/engine.rb +4 -1
  28. data/lib/formtastic/form_builder.rb +32 -25
  29. data/lib/formtastic/helpers/action_helper.rb +22 -31
  30. data/lib/formtastic/helpers/actions_helper.rb +1 -0
  31. data/lib/formtastic/helpers/enum.rb +14 -0
  32. data/lib/formtastic/helpers/errors_helper.rb +3 -2
  33. data/lib/formtastic/helpers/fieldset_wrapper.rb +16 -11
  34. data/lib/formtastic/helpers/file_column_detection.rb +1 -0
  35. data/lib/formtastic/helpers/form_helper.rb +4 -3
  36. data/lib/formtastic/helpers/input_helper.rb +59 -80
  37. data/lib/formtastic/helpers/inputs_helper.rb +33 -27
  38. data/lib/formtastic/helpers/reflection.rb +5 -4
  39. data/lib/formtastic/helpers.rb +2 -2
  40. data/lib/formtastic/html_attributes.rb +13 -1
  41. data/lib/formtastic/i18n.rb +2 -1
  42. data/lib/formtastic/input_class_finder.rb +19 -0
  43. data/lib/formtastic/inputs/base/associations.rb +1 -0
  44. data/lib/formtastic/inputs/base/choices.rb +4 -3
  45. data/lib/formtastic/inputs/base/collections.rb +47 -11
  46. data/lib/formtastic/inputs/base/database.rb +8 -5
  47. data/lib/formtastic/inputs/base/datetime_pickerish.rb +1 -0
  48. data/lib/formtastic/inputs/base/errors.rb +7 -6
  49. data/lib/formtastic/inputs/base/fileish.rb +1 -0
  50. data/lib/formtastic/inputs/base/hints.rb +2 -1
  51. data/lib/formtastic/inputs/base/html.rb +12 -10
  52. data/lib/formtastic/inputs/base/labelling.rb +3 -2
  53. data/lib/formtastic/inputs/base/naming.rb +5 -4
  54. data/lib/formtastic/inputs/base/numeric.rb +1 -0
  55. data/lib/formtastic/inputs/base/options.rb +3 -3
  56. data/lib/formtastic/inputs/base/placeholder.rb +1 -0
  57. data/lib/formtastic/inputs/base/stringish.rb +1 -0
  58. data/lib/formtastic/inputs/base/timeish.rb +9 -4
  59. data/lib/formtastic/inputs/base/validations.rb +39 -12
  60. data/lib/formtastic/inputs/base/wrapping.rb +2 -3
  61. data/lib/formtastic/inputs/base.rb +17 -12
  62. data/lib/formtastic/inputs/boolean_input.rb +2 -1
  63. data/lib/formtastic/inputs/check_boxes_input.rb +16 -24
  64. data/lib/formtastic/inputs/color_input.rb +1 -1
  65. data/lib/formtastic/inputs/country_input.rb +4 -1
  66. data/lib/formtastic/inputs/datalist_input.rb +42 -0
  67. data/lib/formtastic/inputs/date_picker_input.rb +1 -0
  68. data/lib/formtastic/inputs/date_select_input.rb +1 -0
  69. data/lib/formtastic/inputs/datetime_picker_input.rb +1 -0
  70. data/lib/formtastic/inputs/datetime_select_input.rb +1 -0
  71. data/lib/formtastic/inputs/email_input.rb +1 -0
  72. data/lib/formtastic/inputs/file_input.rb +3 -2
  73. data/lib/formtastic/inputs/hidden_input.rb +3 -2
  74. data/lib/formtastic/inputs/number_input.rb +1 -0
  75. data/lib/formtastic/inputs/password_input.rb +1 -0
  76. data/lib/formtastic/inputs/phone_input.rb +1 -0
  77. data/lib/formtastic/inputs/radio_input.rb +26 -21
  78. data/lib/formtastic/inputs/range_input.rb +1 -0
  79. data/lib/formtastic/inputs/search_input.rb +1 -0
  80. data/lib/formtastic/inputs/select_input.rb +32 -10
  81. data/lib/formtastic/inputs/string_input.rb +1 -0
  82. data/lib/formtastic/inputs/text_input.rb +1 -0
  83. data/lib/formtastic/inputs/time_picker_input.rb +1 -0
  84. data/lib/formtastic/inputs/time_select_input.rb +1 -0
  85. data/lib/formtastic/inputs/time_zone_input.rb +17 -6
  86. data/lib/formtastic/inputs/url_input.rb +1 -0
  87. data/lib/formtastic/inputs.rb +33 -28
  88. data/lib/formtastic/localized_string.rb +2 -1
  89. data/lib/formtastic/localizer.rb +23 -24
  90. data/lib/formtastic/namespaced_class_finder.rb +98 -0
  91. data/lib/formtastic/version.rb +2 -1
  92. data/lib/formtastic.rb +19 -14
  93. data/lib/generators/formtastic/form/form_generator.rb +8 -2
  94. data/lib/generators/formtastic/input/input_generator.rb +47 -0
  95. data/lib/generators/formtastic/install/install_generator.rb +2 -0
  96. data/lib/generators/templates/formtastic.rb +29 -7
  97. data/lib/generators/templates/input.rb +19 -0
  98. data/sample/basic_inputs.html +1 -1
  99. data/script/integration-template.rb +73 -0
  100. data/script/integration.sh +19 -0
  101. data/spec/action_class_finder_spec.rb +13 -0
  102. data/spec/actions/button_action_spec.rb +21 -20
  103. data/spec/actions/generic_action_spec.rb +134 -133
  104. data/spec/actions/input_action_spec.rb +20 -19
  105. data/spec/actions/link_action_spec.rb +30 -29
  106. data/spec/builder/custom_builder_spec.rb +39 -22
  107. data/spec/builder/error_proc_spec.rb +6 -5
  108. data/spec/builder/semantic_fields_for_spec.rb +46 -45
  109. data/spec/fast_spec_helper.rb +13 -0
  110. data/spec/generators/formtastic/form/form_generator_spec.rb +33 -32
  111. data/spec/generators/formtastic/input/input_generator_spec.rb +125 -0
  112. data/spec/generators/formtastic/install/install_generator_spec.rb +10 -9
  113. data/spec/helpers/action_helper_spec.rb +70 -97
  114. data/spec/helpers/actions_helper_spec.rb +43 -42
  115. data/spec/helpers/form_helper_spec.rb +56 -39
  116. data/spec/helpers/input_helper_spec.rb +314 -255
  117. data/spec/helpers/inputs_helper_spec.rb +217 -202
  118. data/spec/helpers/reflection_helper_spec.rb +7 -6
  119. data/spec/helpers/semantic_errors_helper_spec.rb +26 -25
  120. data/spec/i18n_spec.rb +30 -29
  121. data/spec/input_class_finder_spec.rb +11 -0
  122. data/spec/inputs/base/collections_spec.rb +78 -0
  123. data/spec/inputs/base/validations_spec.rb +481 -0
  124. data/spec/inputs/boolean_input_spec.rb +73 -72
  125. data/spec/inputs/check_boxes_input_spec.rb +174 -123
  126. data/spec/inputs/color_input_spec.rb +53 -64
  127. data/spec/inputs/country_input_spec.rb +23 -22
  128. data/spec/inputs/custom_input_spec.rb +3 -6
  129. data/spec/inputs/datalist_input_spec.rb +62 -0
  130. data/spec/inputs/date_picker_input_spec.rb +114 -113
  131. data/spec/inputs/date_select_input_spec.rb +76 -61
  132. data/spec/inputs/datetime_picker_input_spec.rb +123 -122
  133. data/spec/inputs/datetime_select_input_spec.rb +85 -68
  134. data/spec/inputs/email_input_spec.rb +17 -16
  135. data/spec/inputs/file_input_spec.rb +18 -17
  136. data/spec/inputs/hidden_input_spec.rb +32 -31
  137. data/spec/inputs/include_blank_spec.rb +10 -9
  138. data/spec/inputs/label_spec.rb +36 -31
  139. data/spec/inputs/number_input_spec.rb +212 -211
  140. data/spec/inputs/password_input_spec.rb +17 -16
  141. data/spec/inputs/phone_input_spec.rb +17 -16
  142. data/spec/inputs/placeholder_spec.rb +18 -17
  143. data/spec/inputs/radio_input_spec.rb +92 -65
  144. data/spec/inputs/range_input_spec.rb +136 -135
  145. data/spec/inputs/readonly_spec.rb +51 -0
  146. data/spec/inputs/search_input_spec.rb +16 -15
  147. data/spec/inputs/select_input_spec.rb +209 -102
  148. data/spec/inputs/string_input_spec.rb +51 -50
  149. data/spec/inputs/text_input_spec.rb +34 -33
  150. data/spec/inputs/time_picker_input_spec.rb +115 -114
  151. data/spec/inputs/time_select_input_spec.rb +84 -70
  152. data/spec/inputs/time_zone_input_spec.rb +58 -31
  153. data/spec/inputs/url_input_spec.rb +17 -16
  154. data/spec/inputs/with_options_spec.rb +9 -8
  155. data/spec/localizer_spec.rb +18 -17
  156. data/spec/namespaced_class_finder_spec.rb +91 -0
  157. data/spec/schema.rb +22 -0
  158. data/spec/spec_helper.rb +180 -249
  159. data/spec/support/custom_macros.rb +128 -98
  160. data/spec/support/deprecation.rb +2 -1
  161. data/spec/support/shared_examples.rb +13 -0
  162. data/spec/support/specialized_class_finder_shared_example.rb +28 -0
  163. data/spec/support/test_environment.rb +25 -10
  164. metadata +95 -136
  165. data/.travis.yml +0 -28
  166. data/Appraisals +0 -25
  167. data/CHANGELOG +0 -27
  168. data/gemfiles/rails_3.2.gemfile +0 -7
  169. data/gemfiles/rails_4.0.4.gemfile +0 -7
  170. data/gemfiles/rails_4.1.gemfile +0 -7
  171. data/gemfiles/rails_4.gemfile +0 -7
  172. data/gemfiles/rails_edge.gemfile +0 -10
  173. data/lib/formtastic/util.rb +0 -53
  174. data/spec/support/deferred_garbage_collection.rb +0 -21
  175. data/spec/util_spec.rb +0 -52
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -85,6 +86,9 @@ module Formtastic
85
86
  # <%= f.input :publish_at, :as => :time_select, :include_blank => true %>
86
87
  # <%= f.input :publish_at, :as => :time_select, :include_blank => false %>
87
88
  #
89
+ # @example Provide a value for the field via selected
90
+ # <%= f.input :publish_at, :as => :datetime_select, :selected => DateTime.new(2018, 10, 4, 12, 00)
91
+ #
88
92
  # @todo Document i18n
89
93
  # @todo Check what other Rails options are supported (`start_year`, `end_year`, `use_month_numbers`, `use_short_month`, `add_month_numbers`, `prompt`), write tests for them, and otherwise support them
90
94
  # @todo Could we take the rendering from Rails' helpers and inject better HTML in and around it rather than re-inventing the whee?
@@ -153,10 +157,11 @@ module Formtastic
153
157
 
154
158
  def fragment_label_html(fragment)
155
159
  text = fragment_label(fragment)
156
- text.blank? ? "".html_safe : template.content_tag(:label, text, :for => fragment_id(fragment))
160
+ text.blank? ? +"".html_safe : template.content_tag(:label, text, :for => fragment_id(fragment))
157
161
  end
158
162
 
159
163
  def value
164
+ return input_options[:selected] if options.key?(:selected)
160
165
  object.send(method) if object && object.respond_to?(method)
161
166
  end
162
167
 
@@ -213,7 +218,7 @@ module Formtastic
213
218
  :class => "label"
214
219
  )
215
220
  else
216
- "".html_safe
221
+ +"".html_safe
217
222
  end
218
223
  end
219
224
 
@@ -224,7 +229,7 @@ module Formtastic
224
229
  end
225
230
 
226
231
  def hidden_fragments
227
- "".html_safe
232
+ +"".html_safe
228
233
  end
229
234
 
230
235
  def hidden_field_name(fragment)
@@ -238,4 +243,4 @@ module Formtastic
238
243
  end
239
244
  end
240
245
  end
241
- end
246
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -73,13 +74,11 @@ module Formtastic
73
74
  raise IndeterminableMinimumAttributeError if validation.options[:greater_than] && column? && [:float, :decimal].include?(column.type)
74
75
 
75
76
  if validation.options[:greater_than_or_equal_to]
76
- return (validation.options[:greater_than_or_equal_to].call(object)) if validation.options[:greater_than_or_equal_to].kind_of?(Proc)
77
- return (validation.options[:greater_than_or_equal_to])
77
+ return option_value(validation.options[:greater_than_or_equal_to], object)
78
78
  end
79
79
 
80
80
  if validation.options[:greater_than]
81
- return (validation.options[:greater_than].call(object) + 1) if validation.options[:greater_than].kind_of?(Proc)
82
- return (validation.options[:greater_than] + 1)
81
+ return option_value(validation.options[:greater_than], object) + 1
83
82
  end
84
83
  end
85
84
  end
@@ -94,13 +93,11 @@ module Formtastic
94
93
  raise IndeterminableMaximumAttributeError if validation.options[:less_than] && column? && [:float, :decimal].include?(column.type)
95
94
 
96
95
  if validation.options[:less_than_or_equal_to]
97
- return (validation.options[:less_than_or_equal_to].call(object)) if validation.options[:less_than_or_equal_to].kind_of?(Proc)
98
- return (validation.options[:less_than_or_equal_to])
96
+ return option_value(validation.options[:less_than_or_equal_to], object)
99
97
  end
100
98
 
101
99
  if validation.options[:less_than]
102
- return ((validation.options[:less_than].call(object)) - 1) if validation.options[:less_than].kind_of?(Proc)
103
- return (validation.options[:less_than] - 1)
100
+ return option_value(validation.options[:less_than], object) - 1
104
101
  end
105
102
  end
106
103
  end
@@ -136,9 +133,10 @@ module Formtastic
136
133
  return true if options[:required] == true
137
134
  return false if not_required_through_negated_validation?
138
135
  if validations?
139
- validations.select { |validator|
136
+ validations.any? { |validator|
140
137
  if validator.options.key?(:on)
141
- return false if (validator.options[:on] != :save) && ((object.new_record? && validator.options[:on] != :create) || (!object.new_record? && validator.options[:on] != :update))
138
+ validator_on = Array(validator.options[:on])
139
+ next false if (validator_on.exclude?(:save)) && ((object.new_record? && validator_on.exclude?(:create)) || (!object.new_record? && validator_on.exclude?(:update)))
142
140
  end
143
141
  case validator.kind
144
142
  when :presence
@@ -152,7 +150,7 @@ module Formtastic
152
150
  else
153
151
  false
154
152
  end
155
- }.any?
153
+ }
156
154
  else
157
155
  return responds_to_global_required? && !!builder.all_fields_required_by_default
158
156
  end
@@ -192,8 +190,37 @@ module Formtastic
192
190
  validation_limit || column_limit
193
191
  end
194
192
 
193
+ def readonly?
194
+ readonly_from_options? || readonly_attribute?
195
+ end
196
+
197
+ def readonly_attribute?
198
+ object_class = self.object.class
199
+ object_class.respond_to?(:readonly_attributes) &&
200
+ self.object.persisted? &&
201
+ column.respond_to?(:name) &&
202
+ object_class.readonly_attributes.include?(column.name.to_s)
203
+ end
204
+
205
+ def readonly_from_options?
206
+ options[:input_html] && options[:input_html][:readonly]
207
+ end
208
+
209
+ private
210
+
211
+ # Loosely based on
212
+ # https://github.com/rails/rails/blob/459e7cf62252558bbf65f582a230562ab1a76c5e/activemodel/lib/active_model/validations/numericality.rb#L65-L70
213
+ def option_value(option, object)
214
+ case option
215
+ when Symbol
216
+ object.send(option)
217
+ when Proc
218
+ option.call(object)
219
+ else
220
+ option
221
+ end
222
+ end
195
223
  end
196
224
  end
197
225
  end
198
226
  end
199
-
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -25,9 +26,7 @@ module Formtastic
25
26
  end
26
27
 
27
28
  def wrapper_classes_raw
28
- classes = wrapper_html_options_raw[:class] || []
29
- return classes.dup if classes.is_a?(Array)
30
- return [classes]
29
+ [*wrapper_html_options_raw[:class]]
31
30
  end
32
31
 
33
32
  def wrapper_classes
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
4
-
5
+
5
6
  attr_accessor :builder, :template, :object, :object_name, :method, :options
6
-
7
+
7
8
  def initialize(builder, template, object, object_name, method, options)
8
9
  @builder = builder
9
10
  @template = template
@@ -11,30 +12,35 @@ module Formtastic
11
12
  @object_name = object_name
12
13
  @method = method
13
14
  @options = options.dup
15
+
16
+ # Deprecate :member_label and :member_value, remove v4.0
17
+ member_deprecation_message = "passing an Array of label/value pairs like [['Justin', 2], ['Kate', 3]] into :collection directly (consider building the array in your model using Model.pluck)"
18
+ warn_deprecated_option!(:member_label, member_deprecation_message)
19
+ warn_deprecated_option!(:member_value, member_deprecation_message)
14
20
  end
15
-
21
+
16
22
  # Usefull for deprecating options.
17
23
  def warn_and_correct_option!(old_option_name, new_option_name)
18
24
  if options.key?(old_option_name)
19
- ::ActiveSupport::Deprecation.warn("The :#{old_option_name} option is deprecated in favour of :#{new_option_name} and will be removed from Formtastic in the next version")
25
+ Deprecation.warn("The :#{old_option_name} option is deprecated in favour of :#{new_option_name} and will be removed from Formtastic in the next version", caller(6))
20
26
  options[new_option_name] = options.delete(old_option_name)
21
27
  end
22
28
  end
23
-
29
+
24
30
  # Usefull for deprecating options.
25
31
  def warn_deprecated_option!(old_option_name, instructions)
26
32
  if options.key?(old_option_name)
27
- ::ActiveSupport::Deprecation.warn("The :#{old_option_name} option is deprecated in favour of `#{instructions}` and will be removed in the next version")
33
+ Deprecation.warn("The :#{old_option_name} option is deprecated in favour of `#{instructions}`. :#{old_option_name} will be removed in the next version", caller(6))
28
34
  end
29
35
  end
30
-
36
+
31
37
  # Usefull for raising an error on previously supported option.
32
38
  def removed_option!(old_option_name)
33
39
  raise ArgumentError, ":#{old_option_name} is no longer available" if options.key?(old_option_name)
34
40
  end
35
-
41
+
36
42
  extend ActiveSupport::Autoload
37
-
43
+
38
44
  autoload :DatetimePickerish
39
45
  autoload :Associations
40
46
  autoload :Collections
@@ -53,7 +59,7 @@ module Formtastic
53
59
  autoload :Timeish
54
60
  autoload :Validations
55
61
  autoload :Wrapping
56
-
62
+
57
63
  include Html
58
64
  include Options
59
65
  include Database
@@ -65,8 +71,7 @@ module Formtastic
65
71
  include Associations
66
72
  include Labelling
67
73
  include Wrapping
68
-
74
+
69
75
  end
70
76
  end
71
77
  end
72
-
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  # Boolean inputs are used to render an input for a single checkbox, typically for attributes
@@ -59,7 +60,7 @@ module Formtastic
59
60
  end
60
61
 
61
62
  def label_text_with_embedded_checkbox
62
- check_box_html << "" << label_text
63
+ check_box_html << +"" << label_text
63
64
  end
64
65
 
65
66
  def check_box_html
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -39,6 +40,7 @@ module Formtastic
39
40
  # <%= f.input :categories, :as => :check_boxes, :collection => @categories %>
40
41
  # <%= f.input :categories, :as => :check_boxes, :collection => Category.all %>
41
42
  # <%= f.input :categories, :as => :check_boxes, :collection => Category.some_named_scope %>
43
+ # <%= f.input :categories, :as => :check_boxes, :collection => Category.pluck(:label, :id) %>
42
44
  # <%= f.input :categories, :as => :check_boxes, :collection => [Category.find_by_name("Ruby"), Category.find_by_name("Rails")] %>
43
45
  # <%= f.input :categories, :as => :check_boxes, :collection => ["Ruby", "Rails"] %>
44
46
  # <%= f.input :categories, :as => :check_boxes, :collection => [["Ruby", "ruby"], ["Rails", "rails"]] %>
@@ -56,24 +58,6 @@ module Formtastic
56
58
  # @example `:disabled` can be used to disable any checkboxes with a value found in the given Array
57
59
  # <%= f.input :categories, :as => :check_boxes, :collection => ["a", "b"], :disabled => ["a"] %>
58
60
  #
59
- # @example `: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)
60
- # <%= f.input :categories, :as => :check_boxes, :member_label => :name %>
61
- # <%= f.input :categories, :as => :check_boxes, :member_label => :name_with_post_count
62
- # <%= f.input :categories, :as => :check_boxes, :member_label => { |c| "#{c.name} (#{pluralize("post", c.posts.count)})" }
63
- #
64
- # @example `:member_label` can be used with a helper method (both examples have the same result)
65
- # <%= f.input :categories, :as => :check_boxes, :member_label => method(:fancy_label)
66
- # <%= f.input :categories, :as => :check_boxes, :member_label => Proc.new { |category| fancy_label(category) }
67
- #
68
- # @example `: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)
69
- # <%= f.input :categories, :as => :check_boxes, :member_value => :code %>
70
- # <%= f.input :categories, :as => :check_boxes, :member_value => :isbn
71
- # <%= f.input :categories, :as => :check_boxes, :member_value => Proc.new { |c| c.name.downcase.underscore }
72
- #
73
- # @example `:member_value` can be used with a helper method (both examples have the same result)
74
- # <%= f.input :categories, :as => :check_boxes, :member_value => method(:some_helper)
75
- # <%= f.input :categories, :as => :check_boxes, :member_value => Proc.new { |category| some_helper(category) }
76
- #
77
61
  # @example `:value_as_class` can be used to add a class to the `<li>` wrapped around each choice using the checkbox value for custom styling of each choice
78
62
  # <%= f.input :categories, :as => :check_boxes, :value_as_class => true %>
79
63
  #
@@ -86,6 +70,11 @@ module Formtastic
86
70
  include Base::Collections
87
71
  include Base::Choices
88
72
 
73
+ def initialize(*args)
74
+ super
75
+ raise Formtastic::UnsupportedEnumCollection if collection_from_enum?
76
+ end
77
+
89
78
  def to_html
90
79
  input_wrapping do
91
80
  choices_wrapping do
@@ -111,8 +100,8 @@ module Formtastic
111
100
  end
112
101
 
113
102
  def hidden_field_for_all
114
- if hidden_fields?
115
- ''
103
+ if hidden_fields_for_every?
104
+ +''
116
105
  else
117
106
  options = {}
118
107
  options[:class] = [method.to_s.singularize, 'default'].join('_') if value_as_class?
@@ -121,7 +110,7 @@ module Formtastic
121
110
  end
122
111
  end
123
112
 
124
- def hidden_fields?
113
+ def hidden_fields_for_every?
125
114
  options[:hidden_fields]
126
115
  end
127
116
 
@@ -182,7 +171,7 @@ module Formtastic
182
171
  protected
183
172
 
184
173
  def checkbox_input(choice)
185
- if hidden_fields?
174
+ if hidden_fields_for_every?
186
175
  check_box_with_hidden_input(choice)
187
176
  else
188
177
  check_box_without_hidden_input(choice)
@@ -192,11 +181,14 @@ module Formtastic
192
181
  def make_selected_values
193
182
  if object.respond_to?(method)
194
183
  selected_items = object.send(method)
195
-
196
184
  # Construct an array from the return value, regardless of the return type
197
185
  selected_items = [*selected_items].compact.flatten
198
186
 
199
- [*selected_items.map { |o| send_or_call_or_object(value_method, o) }].compact
187
+ selected = []
188
+ selected_items.map do |selected_item|
189
+ selected_item_id = selected_item.id if selected_item.respond_to? :id
190
+ item = send_or_call_or_object(value_method, selected_item) || selected_item_id
191
+ end.compact
200
192
  else
201
193
  []
202
194
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -31,7 +32,6 @@ module Formtastic
31
32
  include Base::Placeholder
32
33
 
33
34
  def to_html
34
- raise "The :color input requires the color_field form helper, which is only available in Rails 4+" unless builder.respond_to?(:color_field)
35
35
  input_wrapping do
36
36
  label_html <<
37
37
  builder.color_field(method, input_html_options)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  # Outputs a country select input, wrapping around a regular country_select helper.
@@ -67,8 +68,10 @@ module Formtastic
67
68
  class CountryInput
68
69
  include Base
69
70
 
71
+ CountrySelectPluginMissing = Class.new(StandardError)
72
+
70
73
  def to_html
71
- raise "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)
74
+ 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)
72
75
  input_wrapping do
73
76
  label_html <<
74
77
  builder.country_select(method, priority_countries, input_options, input_html_options)
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ module Formtastic
3
+ module Inputs
4
+ # Outputs a label and a text field, along with a datalist tag
5
+ # datalist tag provides a list of options which drives a simple autocomplete
6
+ # on the text field. This is a HTML5 feature, more info can be found at
7
+ # {https://developer.mozilla.org/en/docs/Web/HTML/Element/datalist <datalist> at MDN}
8
+ # This input accepts a :collection option which takes data in all the usual formats accepted by
9
+ # {http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_for_select options_for_select}
10
+ #
11
+ # @example Input is used as follows
12
+ # f.input :fav_book, :as => :datalist, :collection => Book.pluck(:name)
13
+ #
14
+ class DatalistInput
15
+ include Base
16
+ include Base::Stringish
17
+ include Base::Collections
18
+
19
+ def to_html
20
+ @name = input_html_options[:id].gsub(/_id$/, "")
21
+ input_wrapping do
22
+ label_html <<
23
+ builder.text_field(method, input_html_options) << # standard input
24
+ data_list_html # append new datalist element
25
+ end
26
+ end
27
+
28
+ def input_html_options
29
+ super.merge(:list => html_id_of_datalist)
30
+ end
31
+
32
+ def html_id_of_datalist
33
+ "#{@name}_datalist"
34
+ end
35
+
36
+ def data_list_html
37
+ html = builder.template.options_for_select(collection)
38
+ builder.template.content_tag(:datalist,html, { :id => html_id_of_datalist }, false)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  # Outputs a series of select boxes for the fragments that make up a date (year, month, day).
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -20,7 +21,7 @@ module Formtastic
20
21
  # <form...>
21
22
  # <fieldset>
22
23
  # <ol>
23
- # <li class="email">
24
+ # <li class="file">
24
25
  # <label for="user_avatar">Avatar</label>
25
26
  # <input type="file" id="user_avatar" name="user[avatar]">
26
27
  # </li>
@@ -29,7 +30,7 @@ module Formtastic
29
30
  # </form>
30
31
  #
31
32
  # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
32
- class FileInput
33
+ class FileInput
33
34
  include Base
34
35
  def to_html
35
36
  input_wrapping do
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -42,7 +43,7 @@ module Formtastic
42
43
  end
43
44
 
44
45
  def error_html
45
- ""
46
+ +""
46
47
  end
47
48
 
48
49
  def errors?
@@ -50,7 +51,7 @@ module Formtastic
50
51
  end
51
52
 
52
53
  def hint_html
53
- ""
54
+ +""
54
55
  end
55
56
 
56
57
  def hint?
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -31,10 +32,13 @@ module Formtastic
31
32
  # `Section.all` for a `Post` form with an input for a `belongs_to :section` association.
32
33
  # You can override or customise this collection through the `:collection` option (see examples).
33
34
  #
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
35
+ # For radio inputs that map to ActiveRecord `enum` attributes, Formtastic will automatically
36
+ # load in your enum options to be used as the radio button choices. This can be overridden with
37
+ # the `:collection` option, or augmented with I18n translations. See examples below.
38
+ #
39
+ # The way on which Formtastic renders the `value` attribute and label for each choice in the `:collection` is
40
+ # customisable (see examples below). When not provided, we fall back to a list of methods to try on each
41
+ # object such as `:to_label`, `:name` and `:to_s`, which are defined in the configurations
38
42
  # `collection_label_methods` and `collection_value_methods`.
39
43
  #
40
44
  # @example Basic `belongs_to` example with full form context
@@ -78,6 +82,8 @@ module Formtastic
78
82
  # <%= f.input :author, :as => :radio, :collection => @authors %>
79
83
  # <%= f.input :author, :as => :radio, :collection => Author.all %>
80
84
  # <%= f.input :author, :as => :radio, :collection => Author.some_named_scope %>
85
+ # <%= f.input :author, :as => :radio, :collection => Author.pluck(:full_name, :id) %>
86
+ # <%= f.input :author, :as => :radio, :collection => Author.pluck(Arel.sql("CONCAT(`first_name`, ' ', `last_name`)"), :id)) %>
81
87
  # <%= f.input :author, :as => :radio, :collection => [Author.find_by_login("justin"), Category.find_by_name("kate")] %>
82
88
  # <%= f.input :author, :as => :radio, :collection => ["Justin", "Kate"] %>
83
89
  # <%= f.input :author, :as => :radio, :collection => [["Justin", "justin"], ["Kate", "kate"]] %>
@@ -87,23 +93,6 @@ module Formtastic
87
93
  # <%= f.input :author, :as => :radio, :collection => [:justin, :kate] %>
88
94
  # <%= f.input :author, :as => :radio, :collection => 1..5 %>
89
95
  #
90
- # @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)
91
- # <%= f.input :author, :as => :radio, :member_label => :name %>
92
- # <%= f.input :author, :as => :radio, :member_label => :name_with_post_count
93
- # <%= f.input :author, :as => :radio, :member_label => Proc.new { |a| "#{c.name} (#{pluralize("post", a.posts.count)})" }
94
- #
95
- # @example `:member_label` can be used with a helper method (both examples have the same result)
96
- # <%= f.input :author, :as => :radio, :member_label => method(:fancy_label)
97
- # <%= f.input :author, :as => :radio, :member_label => Proc.new { |author| fancy_label(author) }
98
- #
99
- # @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)
100
- # <%= f.input :author, :as => :radio, :member_value => :login %>
101
- # <%= f.input :author, :as => :radio, :member_value => Proc.new { |c| c.full_name.downcase.underscore }
102
- #
103
- # @example `:member_value` can be used with a helper method (both examples have the same result)
104
- # <%= f.input :author, :as => :radio, :member_value => method(:some_helper)
105
- # <%= f.input :author, :as => :radio, :member_value => Proc.new { |author| some_helper(author) }
106
- #
107
96
  # @example Set HTML attributes on each `<input type="radio">` tag with `:input_html`
108
97
  # <%= f.input :author, :as => :radio, :input_html => { :size => 20, :multiple => true, :class => "special" } %>
109
98
  #
@@ -116,6 +105,22 @@ module Formtastic
116
105
  # @example Set HTML options on a specific radio input option with a 3rd element in the array for a collection member
117
106
  # <%= f.input :author, :as => :radio, :collection => [["Test", 'test'], ["Try", "try", {:disabled => true}]]
118
107
  #
108
+ # @example Using ActiveRecord enum attribute with i18n translation:
109
+ # # post.rb
110
+ # class Post < ActiveRecord::Base
111
+ # enum :status => [ :active, :archived ]
112
+ # end
113
+ # # en.yml
114
+ # en:
115
+ # activerecord:
116
+ # attributes:
117
+ # post:
118
+ # statuses:
119
+ # active: I am active!
120
+ # archived: I am archived!
121
+ # # form
122
+ # <%= f.input :status, :as => :radio %>
123
+ #
119
124
  # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
120
125
  # @see Formtastic::Inputs::RadioInput as an alternative for `belongs_to` associations
121
126
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4