formtastic 3.0.0 → 5.0.0

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