simple_form 2.0.0 → 3.5.1

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.

Potentially problematic release.


This version of simple_form might be problematic. Click here for more details.

Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +97 -198
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +572 -296
  5. data/lib/generators/simple_form/install_generator.rb +17 -7
  6. data/lib/generators/simple_form/templates/README +3 -4
  7. data/lib/generators/simple_form/templates/_form.html.erb +1 -0
  8. data/lib/generators/simple_form/templates/_form.html.haml +1 -0
  9. data/lib/generators/simple_form/templates/config/initializers/{simple_form.rb.tt → simple_form.rb} +57 -63
  10. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +155 -0
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +111 -0
  12. data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +14 -7
  13. data/lib/simple_form/action_view_extensions/builder.rb +5 -305
  14. data/lib/simple_form/action_view_extensions/form_helper.rb +18 -20
  15. data/lib/simple_form/components/errors.rb +30 -3
  16. data/lib/simple_form/components/hints.rb +10 -3
  17. data/lib/simple_form/components/html5.rb +17 -3
  18. data/lib/simple_form/components/label_input.rb +21 -2
  19. data/lib/simple_form/components/labels.rb +16 -11
  20. data/lib/simple_form/components/maxlength.rb +19 -12
  21. data/lib/simple_form/components/min_max.rb +4 -2
  22. data/lib/simple_form/components/minlength.rb +48 -0
  23. data/lib/simple_form/components/pattern.rb +5 -4
  24. data/lib/simple_form/components/placeholders.rb +3 -2
  25. data/lib/simple_form/components/readonly.rb +3 -2
  26. data/lib/simple_form/components.rb +15 -11
  27. data/lib/simple_form/error_notification.rb +4 -3
  28. data/lib/simple_form/form_builder.rb +283 -105
  29. data/lib/simple_form/helpers/autofocus.rb +1 -0
  30. data/lib/simple_form/helpers/disabled.rb +1 -0
  31. data/lib/simple_form/helpers/readonly.rb +1 -0
  32. data/lib/simple_form/helpers/required.rb +1 -0
  33. data/lib/simple_form/helpers/validators.rb +4 -3
  34. data/lib/simple_form/helpers.rb +7 -6
  35. data/lib/simple_form/i18n_cache.rb +1 -0
  36. data/lib/simple_form/inputs/base.rb +76 -23
  37. data/lib/simple_form/inputs/block_input.rb +3 -2
  38. data/lib/simple_form/inputs/boolean_input.rb +55 -16
  39. data/lib/simple_form/inputs/collection_check_boxes_input.rb +2 -1
  40. data/lib/simple_form/inputs/collection_input.rb +41 -18
  41. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +11 -19
  42. data/lib/simple_form/inputs/collection_select_input.rb +5 -2
  43. data/lib/simple_form/inputs/date_time_input.rb +23 -12
  44. data/lib/simple_form/inputs/file_input.rb +5 -2
  45. data/lib/simple_form/inputs/grouped_collection_select_input.rb +16 -3
  46. data/lib/simple_form/inputs/hidden_input.rb +5 -2
  47. data/lib/simple_form/inputs/numeric_input.rb +4 -8
  48. data/lib/simple_form/inputs/password_input.rb +6 -4
  49. data/lib/simple_form/inputs/priority_input.rb +5 -2
  50. data/lib/simple_form/inputs/range_input.rb +2 -1
  51. data/lib/simple_form/inputs/string_input.rb +6 -4
  52. data/lib/simple_form/inputs/text_input.rb +6 -3
  53. data/lib/simple_form/inputs.rb +20 -17
  54. data/lib/simple_form/map_type.rb +1 -0
  55. data/lib/simple_form/railtie.rb +15 -0
  56. data/lib/simple_form/tags.rb +69 -0
  57. data/lib/simple_form/version.rb +2 -1
  58. data/lib/simple_form/wrappers/builder.rb +12 -35
  59. data/lib/simple_form/wrappers/leaf.rb +29 -0
  60. data/lib/simple_form/wrappers/many.rb +12 -7
  61. data/lib/simple_form/wrappers/root.rb +7 -4
  62. data/lib/simple_form/wrappers/single.rb +12 -3
  63. data/lib/simple_form/wrappers.rb +3 -1
  64. data/lib/simple_form.rb +118 -63
  65. data/test/action_view_extensions/builder_test.rb +230 -164
  66. data/test/action_view_extensions/form_helper_test.rb +107 -39
  67. data/test/components/label_test.rb +105 -87
  68. data/test/form_builder/association_test.rb +131 -62
  69. data/test/form_builder/button_test.rb +15 -14
  70. data/test/form_builder/error_notification_test.rb +11 -10
  71. data/test/form_builder/error_test.rb +188 -34
  72. data/test/form_builder/general_test.rb +247 -102
  73. data/test/form_builder/hint_test.rb +59 -32
  74. data/test/form_builder/input_field_test.rb +138 -25
  75. data/test/form_builder/label_test.rb +84 -13
  76. data/test/form_builder/wrapper_test.rb +236 -33
  77. data/test/generators/simple_form_generator_test.rb +15 -4
  78. data/test/inputs/boolean_input_test.rb +147 -13
  79. data/test/inputs/collection_check_boxes_input_test.rb +166 -71
  80. data/test/inputs/collection_radio_buttons_input_test.rb +229 -113
  81. data/test/inputs/collection_select_input_test.rb +222 -85
  82. data/test/inputs/datetime_input_test.rb +134 -47
  83. data/test/inputs/disabled_test.rb +62 -21
  84. data/test/inputs/discovery_test.rb +70 -10
  85. data/test/inputs/file_input_test.rb +4 -3
  86. data/test/inputs/general_test.rb +90 -26
  87. data/test/inputs/grouped_collection_select_input_test.rb +88 -23
  88. data/test/inputs/hidden_input_test.rb +7 -5
  89. data/test/inputs/numeric_input_test.rb +56 -46
  90. data/test/inputs/priority_input_test.rb +31 -16
  91. data/test/inputs/readonly_test.rb +68 -27
  92. data/test/inputs/required_test.rb +63 -18
  93. data/test/inputs/string_input_test.rb +76 -51
  94. data/test/inputs/text_input_test.rb +21 -8
  95. data/test/simple_form_test.rb +9 -0
  96. data/test/support/discovery_inputs.rb +39 -2
  97. data/test/support/misc_helpers.rb +176 -20
  98. data/test/support/mock_controller.rb +13 -7
  99. data/test/support/models.rb +187 -71
  100. data/test/test_helper.rb +38 -39
  101. metadata +53 -39
  102. data/lib/simple_form/core_ext/hash.rb +0 -16
  103. data/test/support/mock_response.rb +0 -14
@@ -1,187 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module ActionViewExtensions
3
- # Base builder to handle each instance of a collection of radio buttons / check boxes.
4
- # Based on (at this time upcoming) Rails 4 collection builders.
5
- class BuilderBase #:nodoc:
6
- attr_reader :object, :text, :value
7
-
8
- def initialize(template_object, object_name, method_name, object,
9
- sanitized_attribute_name, text, value, input_html_options)
10
- @template_object = template_object
11
- @object_name = object_name
12
- @method_name = method_name
13
- @object = object
14
- @sanitized_attribute_name = sanitized_attribute_name
15
- @text = text
16
- @value = value
17
- @input_html_options = input_html_options
18
- end
19
-
20
- def label(label_html_options={}, &block)
21
- @template_object.label(@object_name, @sanitized_attribute_name, @text, label_html_options, &block)
22
- end
23
- end
24
-
25
- # Handles generating an instance of radio + label for collection_radio_buttons.
26
- class RadioButtonBuilder < BuilderBase #:nodoc:
27
- def radio_button(extra_html_options={})
28
- html_options = extra_html_options.merge(@input_html_options)
29
- @template_object.radio_button(@object_name, @method_name, @value, html_options)
30
- end
31
- end
32
-
33
- # Handles generating an instance of check box + label for collection_check_boxes.
34
- class CheckBoxBuilder < BuilderBase #:nodoc:
35
- def check_box(extra_html_options={})
36
- html_options = extra_html_options.merge(@input_html_options)
37
- @template_object.check_box(@object_name, @method_name, html_options, @value, nil)
38
- end
39
- end
40
-
41
4
  # A collection of methods required by simple_form but added to rails default form.
42
5
  # This means that you can use such methods outside simple_form context.
43
6
  module Builder
44
- # Create a collection of radio inputs for the attribute. Basically this
45
- # helper will create a radio input associated with a label for each
46
- # text/value option in the collection, using value_method and text_method
47
- # to convert these text/value. You can give a symbol or a proc to both
48
- # value_method and text_method, that will be evaluated for each item in
49
- # the collection.
50
- #
51
- # == Examples
52
- #
53
- # form_for @user do |f|
54
- # f.collection_radio_buttons :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
55
- # end
56
- #
57
- # <input id="user_options_true" name="user[options]" type="radio" value="true" />
58
- # <label class="collection_radio_buttons" for="user_options_true">Yes</label>
59
- # <input id="user_options_false" name="user[options]" type="radio" value="false" />
60
- # <label class="collection_radio_buttons" for="user_options_false">No</label>
61
- #
62
- # It is also possible to give a block that should generate the radio +
63
- # label. To wrap the radio with the label, for instance:
64
- #
65
- # form_for @user do |f|
66
- # f.collection_radio_buttons(
67
- # :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
68
- # ) do |b|
69
- # b.label { b.radio_button + b.text }
70
- # end
71
- # end
72
- #
73
- # == Options
74
- #
75
- # Collection radio accepts some extra options:
76
- #
77
- # * checked => the value that should be checked initially.
78
- #
79
- # * disabled => the value or values that should be disabled. Accepts a single
80
- # item or an array of items.
81
- #
82
- # * collection_wrapper_tag => the tag to wrap the entire collection.
83
- #
84
- # * collection_wrapper_class => the CSS class to use for collection_wrapper_tag
85
- #
86
- # * item_wrapper_tag => the tag to wrap each item in the collection.
87
- #
88
- # * item_wrapper_class => the CSS class to use for item_wrapper_tag
89
- #
90
- # * a block => to generate the label + radio or any other component.
91
- #
92
- def collection_radio_buttons(attribute, collection, value_method, text_method, options={}, html_options={})
93
- rendered_collection = render_collection(
94
- collection, value_method, text_method, options, html_options
95
- ) do |item, value, text, default_html_options|
96
- builder = instantiate_builder(RadioButtonBuilder, attribute, item, value, text, default_html_options)
97
-
98
- if block_given?
99
- yield builder
100
- else
101
- builder.radio_button + builder.label(:class => "collection_radio_buttons")
102
- end
103
- end
104
-
105
- wrap_rendered_collection(rendered_collection, options)
106
- end
107
-
108
- # deprecated
109
- def collection_radio(*args, &block)
110
- SimpleForm.deprecation_warn "The `collection_radio` helper is deprecated, " \
111
- "please use `collection_radio_buttons` instead."
112
- collection_radio_buttons(*args, &block)
113
- end
114
-
115
- # Creates a collection of check boxes for each item in the collection,
116
- # associated with a clickable label. Use value_method and text_method to
117
- # convert items in the collection for use as text/value in check boxes.
118
- # You can give a symbol or a proc to both value_method and text_method,
119
- # that will be evaluated for each item in the collection.
120
- #
121
- # == Examples
122
- #
123
- # form_for @user do |f|
124
- # f.collection_check_boxes :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
125
- # end
126
- #
127
- # <input name="user[options][]" type="hidden" value="" />
128
- # <input id="user_options_true" name="user[options][]" type="checkbox" value="true" />
129
- # <label class="collection_check_boxes" for="user_options_true">Yes</label>
130
- # <input name="user[options][]" type="hidden" value="" />
131
- # <input id="user_options_false" name="user[options][]" type="checkbox" value="false" />
132
- # <label class="collection_check_boxes" for="user_options_false">No</label>
133
- #
134
- # It is also possible to give a block that should generate the check box +
135
- # label. To wrap the check box with the label, for instance:
136
- #
137
- # form_for @user do |f|
138
- # f.collection_check_boxes(
139
- # :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
140
- # ) do |b|
141
- # b.label { b.check_box + b.text }
142
- # end
143
- # end
144
- #
145
- # == Options
146
- #
147
- # Collection check box accepts some extra options:
148
- #
149
- # * checked => the value or values that should be checked initially. Accepts
150
- # a single item or an array of items. It overrides existing associations.
151
- #
152
- # * disabled => the value or values that should be disabled. Accepts a single
153
- # item or an array of items.
154
- #
155
- # * collection_wrapper_tag => the tag to wrap the entire collection.
156
- #
157
- # * collection_wrapper_class => the CSS class to use for collection_wrapper_tag
158
- #
159
- # * item_wrapper_tag => the tag to wrap each item in the collection.
160
- #
161
- # * item_wrapper_class => the CSS class to use for item_wrapper_tag
162
- #
163
- # * a block => to generate the label + check box or any other component.
164
- #
165
- def collection_check_boxes(attribute, collection, value_method, text_method, options={}, html_options={})
166
- rendered_collection = render_collection(
167
- collection, value_method, text_method, options, html_options
168
- ) do |item, value, text, default_html_options|
169
- default_html_options[:multiple] = true
170
- builder = instantiate_builder(CheckBoxBuilder, attribute, item, value, text, default_html_options)
171
-
172
- if block_given?
173
- yield builder
174
- else
175
- builder.check_box + builder.label(:class => "collection_check_boxes")
176
- end
177
- end
178
-
179
- # Append a hidden field to make sure something will be sent back to the
180
- # server if all checkboxes are unchecked.
181
- hidden = template.hidden_field_tag("#{object_name}[#{attribute}][]", "", :id => nil)
182
-
183
- wrap_rendered_collection(rendered_collection + hidden, options)
184
- end
185
7
 
186
8
  # Wrapper for using SimpleForm inside a default rails form.
187
9
  # Example:
@@ -194,79 +16,16 @@ module SimpleForm
194
16
  # end
195
17
  def simple_fields_for(*args, &block)
196
18
  options = args.extract_options!
197
- options[:wrapper] ||= self.options[:wrapper]
19
+ options[:wrapper] = self.options[:wrapper] if options[:wrapper].nil?
20
+ options[:defaults] ||= self.options[:defaults]
21
+ options[:wrapper_mappings] ||= self.options[:wrapper_mappings]
198
22
 
199
23
  if self.class < ActionView::Helpers::FormBuilder
200
24
  options[:builder] ||= self.class
201
25
  else
202
26
  options[:builder] ||= SimpleForm::FormBuilder
203
27
  end
204
- fields_for(*(args << options), &block)
205
- end
206
-
207
- private
208
-
209
- def instantiate_builder(builder_class, attribute, item, value, text, html_options)
210
- builder_class.new(@template, object_name, attribute, item,
211
- sanitize_attribute_name(attribute, value), text, value, html_options)
212
- end
213
-
214
- # Generate default options for collection helpers, such as :checked and
215
- # :disabled.
216
- def default_html_options_for_collection(item, value, options, html_options) #:nodoc:
217
- html_options = html_options.dup
218
-
219
- [:checked, :selected, :disabled].each do |option|
220
- next unless current_option = options[option]
221
-
222
- accept = if current_option.respond_to?(:call)
223
- current_option.call(item)
224
- else
225
- Array(current_option).include?(value)
226
- end
227
-
228
- if accept
229
- html_options[option] = true
230
- elsif option == :checked
231
- html_options[option] = false
232
- end
233
- end
234
-
235
- html_options
236
- end
237
-
238
- def sanitize_attribute_name(attribute, value) #:nodoc:
239
- "#{attribute}_#{value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase}"
240
- end
241
-
242
- def render_collection(collection, value_method, text_method, options={}, html_options={}) #:nodoc:
243
- item_wrapper_tag = options.fetch(:item_wrapper_tag, :span)
244
- item_wrapper_class = options[:item_wrapper_class]
245
-
246
- collection.map do |item|
247
- value = value_for_collection(item, value_method)
248
- text = value_for_collection(item, text_method)
249
- default_html_options = default_html_options_for_collection(item, value, options, html_options)
250
-
251
- rendered_item = yield item, value, text, default_html_options
252
-
253
- item_wrapper_tag ? @template.content_tag(item_wrapper_tag, rendered_item, :class => item_wrapper_class) : rendered_item
254
- end.join.html_safe
255
- end
256
-
257
- def value_for_collection(item, value) #:nodoc:
258
- value.respond_to?(:call) ? value.call(item) : item.send(value)
259
- end
260
-
261
- def wrap_rendered_collection(collection, options)
262
- wrapper_tag = options[:collection_wrapper_tag]
263
-
264
- if wrapper_tag
265
- wrapper_class = options[:collection_wrapper_class]
266
- @template.content_tag(wrapper_tag, collection, :class => wrapper_class)
267
- else
268
- collection
269
- end
28
+ fields_for(*args, options, &block)
270
29
  end
271
30
  end
272
31
  end
@@ -275,64 +34,5 @@ end
275
34
  module ActionView::Helpers
276
35
  class FormBuilder
277
36
  include SimpleForm::ActionViewExtensions::Builder
278
-
279
- # Override default Rails collection_select helper to handle lambdas/procs in
280
- # text and value methods, so it works the same way as collection_radio_buttons
281
- # and collection_check_boxes in SimpleForm. If none of text/value methods is a
282
- # callable object, then it just delegates back to original collection select.
283
- #
284
- alias :original_collection_select :collection_select
285
- def collection_select(attribute, collection, value_method, text_method, options={}, html_options={})
286
- if value_method.respond_to?(:call) || text_method.respond_to?(:call)
287
- collection = collection.map do |item|
288
- value = value_for_collection(item, value_method)
289
- text = value_for_collection(item, text_method)
290
-
291
- default_html_options = default_html_options_for_collection(item, value, options, html_options)
292
- disabled = value if default_html_options[:disabled]
293
- selected = value if default_html_options[:selected]
294
-
295
- [value, text, selected, disabled]
296
- end
297
-
298
- [:disabled, :selected].each do |option|
299
- option_value = collection.map(&:pop).compact
300
- options[option] = option_value if option_value.present?
301
- end
302
- value_method, text_method = :first, :last
303
- end
304
-
305
- original_collection_select(attribute, collection, value_method, text_method, options, html_options)
306
- end
307
- end
308
-
309
- # Backport Rails fix to checkbox tag element, which does not generate the
310
- # hidden input when given nil as unchecked value. This is to make SimpleForm
311
- # collection check boxes helper to work fine with nested boolean style, when
312
- # they are wrapped in labels. Without that, clicking in the label would
313
- # actually change the hidden input, instead of the checkbox.
314
- # FIXME: remove when support only Rails >= 3.2.2.
315
- class InstanceTag
316
- def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
317
- options = options.stringify_keys
318
- options["type"] = "checkbox"
319
- options["value"] = checked_value
320
- if options.has_key?("checked")
321
- cv = options.delete "checked"
322
- checked = cv == true || cv == "checked"
323
- else
324
- checked = self.class.check_box_checked?(value(object), checked_value)
325
- end
326
- options["checked"] = "checked" if checked
327
- if options["multiple"]
328
- add_default_name_and_id_for_value(checked_value, options)
329
- options.delete("multiple")
330
- else
331
- add_default_name_and_id(options)
332
- end
333
- hidden = unchecked_value ? tag("input", "name" => options["name"], "type" => "hidden", "value" => unchecked_value, "disabled" => options["disabled"]) : "".html_safe
334
- checkbox = tag("input", options)
335
- hidden + checkbox
336
- end
337
37
  end
338
38
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module ActionViewExtensions
3
4
  # This module creates SimpleForm wrappers around default form_for and fields_for.
@@ -5,29 +6,22 @@ module SimpleForm
5
6
  # Example:
6
7
  #
7
8
  # simple_form_for @user do |f|
8
- # f.input :name, :hint => 'My hint'
9
+ # f.input :name, hint: 'My hint'
9
10
  # end
10
11
  #
11
12
  module FormHelper
12
- # based on what is done in formtastic
13
- # http://github.com/justinfrench/formtastic/blob/master/lib/formtastic.rb#L1706
14
- @@default_field_error_proc = nil
15
13
 
16
- # Override the default ActiveRecordHelper behaviour of wrapping the input.
17
- # This gets taken care of semantically by adding an error class to the wrapper tag
18
- # containing the input.
19
- #
20
- FIELD_ERROR_PROC = proc do |html_tag, instance_tag|
21
- html_tag
22
- end
23
-
24
- def simple_form_for(record, options={}, &block)
14
+ def simple_form_for(record, options = {}, &block)
25
15
  options[:builder] ||= SimpleForm::FormBuilder
26
16
  options[:html] ||= {}
27
17
  unless options[:html].key?(:novalidate)
28
18
  options[:html][:novalidate] = !SimpleForm.browser_validations
29
19
  end
30
- options[:html][:class] = [SimpleForm.form_class, simple_form_css_class(record, options)].compact.join(" ")
20
+ if options[:html].key?(:class)
21
+ options[:html][:class] = [SimpleForm.form_class, options[:html][:class]].compact
22
+ else
23
+ options[:html][:class] = [SimpleForm.form_class, SimpleForm.default_form_class, simple_form_css_class(record, options)].compact
24
+ end
31
25
 
32
26
  with_simple_form_field_error_proc do
33
27
  form_for(record, options, &block)
@@ -46,11 +40,13 @@ module SimpleForm
46
40
  private
47
41
 
48
42
  def with_simple_form_field_error_proc
49
- @@default_field_error_proc = ::ActionView::Base.field_error_proc
50
- ::ActionView::Base.field_error_proc = FIELD_ERROR_PROC
51
- result = yield
52
- ::ActionView::Base.field_error_proc = @@default_field_error_proc
53
- result
43
+ default_field_error_proc = ::ActionView::Base.field_error_proc
44
+ begin
45
+ ::ActionView::Base.field_error_proc = SimpleForm.field_error_proc
46
+ yield
47
+ ensure
48
+ ::ActionView::Base.field_error_proc = default_field_error_proc
49
+ end
54
50
  end
55
51
 
56
52
  def simple_form_css_class(record, options)
@@ -71,4 +67,6 @@ module SimpleForm
71
67
  end
72
68
  end
73
69
 
74
- ActionView::Base.send :include, SimpleForm::ActionViewExtensions::FormHelper
70
+ ActiveSupport.on_load(:action_view) do
71
+ include SimpleForm::ActionViewExtensions::FormHelper
72
+ end
@@ -1,10 +1,15 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Components
3
4
  module Errors
4
- def error
5
+ def error(wrapper_options = nil)
5
6
  error_text if has_errors?
6
7
  end
7
8
 
9
+ def full_error(wrapper_options = nil)
10
+ full_error_text if options[:error] != false && has_errors?
11
+ end
12
+
8
13
  def has_errors?
9
14
  object && object.respond_to?(:errors) && errors.present?
10
15
  end
@@ -12,7 +17,13 @@ module SimpleForm
12
17
  protected
13
18
 
14
19
  def error_text
15
- "#{options[:error_prefix]} #{errors.send(error_method)}".lstrip.html_safe
20
+ text = has_custom_error? ? options[:error] : errors.send(error_method)
21
+
22
+ "#{html_escape(options[:error_prefix])} #{html_escape(text)}".lstrip.html_safe
23
+ end
24
+
25
+ def full_error_text
26
+ has_custom_error? ? options[:error] : full_errors.send(error_method)
16
27
  end
17
28
 
18
29
  def error_method
@@ -23,13 +34,29 @@ module SimpleForm
23
34
  @errors ||= (errors_on_attribute + errors_on_association).compact
24
35
  end
25
36
 
37
+ def full_errors
38
+ @full_errors ||= (full_errors_on_attribute + full_errors_on_association).compact
39
+ end
40
+
26
41
  def errors_on_attribute
27
- object.errors[attribute_name]
42
+ object.errors[attribute_name] || []
43
+ end
44
+
45
+ def full_errors_on_attribute
46
+ object.errors.full_messages_for(attribute_name)
28
47
  end
29
48
 
30
49
  def errors_on_association
31
50
  reflection ? object.errors[reflection.name] : []
32
51
  end
52
+
53
+ def full_errors_on_association
54
+ reflection ? object.errors.full_messages_for(reflection.name) : []
55
+ end
56
+
57
+ def has_custom_error?
58
+ options[:error].is_a?(String)
59
+ end
33
60
  end
34
61
  end
35
62
  end
@@ -1,16 +1,23 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Components
3
4
  # Needs to be enabled in order to do automatic lookups.
4
5
  module Hints
5
- def hint
6
+ def hint(wrapper_options = nil)
6
7
  @hint ||= begin
7
8
  hint = options[:hint]
8
- hint.is_a?(String) ? hint : translate(:hints)
9
+
10
+ if hint.is_a?(String)
11
+ html_escape(hint)
12
+ else
13
+ content = translate_from_namespace(:hints)
14
+ content.html_safe if content
15
+ end
9
16
  end
10
17
  end
11
18
 
12
19
  def has_hint?
13
- hint.present?
20
+ options[:hint] != false && hint.present?
14
21
  end
15
22
  end
16
23
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Components
3
4
  module HTML5
@@ -5,9 +6,14 @@ module SimpleForm
5
6
  @html5 = false
6
7
  end
7
8
 
8
- def html5
9
+ def html5(wrapper_options = nil)
9
10
  @html5 = true
10
- input_html_options[:required] = true if has_required?
11
+
12
+ input_html_options[:required] = input_html_required_option
13
+ input_html_options[:'aria-required'] = input_html_aria_required_option
14
+
15
+ input_html_options[:'aria-invalid'] = has_errors? || nil
16
+
11
17
  nil
12
18
  end
13
19
 
@@ -15,6 +21,14 @@ module SimpleForm
15
21
  @html5
16
22
  end
17
23
 
24
+ def input_html_required_option
25
+ !options[:required].nil? ? required_field? : has_required?
26
+ end
27
+
28
+ def input_html_aria_required_option
29
+ !options[:required].nil? ? (required_field? || nil) : (has_required? || nil)
30
+ end
31
+
18
32
  def has_required?
19
33
  # We need to check browser_validations because
20
34
  # some browsers are still checking required even
@@ -23,4 +37,4 @@ module SimpleForm
23
37
  end
24
38
  end
25
39
  end
26
- end
40
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Components
3
4
  module LabelInput
@@ -7,8 +8,26 @@ module SimpleForm
7
8
  include SimpleForm::Components::Labels
8
9
  end
9
10
 
10
- def label_input
11
- (options[:label] == false ? "" : label) + input
11
+ def label_input(wrapper_options = nil)
12
+ if options[:label] == false
13
+ deprecated_component(:input, wrapper_options)
14
+ else
15
+ deprecated_component(:label, wrapper_options) + deprecated_component(:input, wrapper_options)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def deprecated_component(namespace, wrapper_options)
22
+ method = method(namespace)
23
+
24
+ if method.arity.zero?
25
+ ActiveSupport::Deprecation.warn(SimpleForm::CUSTOM_INPUT_DEPRECATION_WARN % { name: namespace })
26
+
27
+ method.call
28
+ else
29
+ method.call(wrapper_options)
30
+ end
12
31
  end
13
32
  end
14
33
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Components
3
4
  module Labels
@@ -6,31 +7,34 @@ module SimpleForm
6
7
  module ClassMethods #:nodoc:
7
8
  def translate_required_html
8
9
  i18n_cache :translate_required_html do
9
- I18n.t(:"simple_form.required.html", :default =>
10
- %[<abbr title="#{translate_required_text}">#{translate_required_mark}</abbr>]
10
+ I18n.t(:"simple_form.required.html", default:
11
+ %(<abbr title="#{translate_required_text}">#{translate_required_mark}</abbr>)
11
12
  )
12
13
  end
13
14
  end
14
15
 
15
16
  def translate_required_text
16
- I18n.t(:"simple_form.required.text", :default => 'required')
17
+ I18n.t(:"simple_form.required.text", default: 'required')
17
18
  end
18
19
 
19
20
  def translate_required_mark
20
- I18n.t(:"simple_form.required.mark", :default => '*')
21
+ I18n.t(:"simple_form.required.mark", default: '*')
21
22
  end
22
23
  end
23
24
 
24
- def label
25
+ def label(wrapper_options = nil)
26
+ label_options = merge_wrapper_options(label_html_options, wrapper_options)
27
+
25
28
  if generate_label_for_attribute?
26
- @builder.label(label_target, label_text, label_html_options)
29
+ @builder.label(label_target, label_text, label_options)
27
30
  else
28
- template.label_tag(nil, label_text, label_html_options)
31
+ template.label_tag(nil, label_text, label_options)
29
32
  end
30
33
  end
31
34
 
32
- def label_text
33
- SimpleForm.label_text.call(raw_label_text, required_label_text).html_safe
35
+ def label_text(wrapper_options = nil)
36
+ label_text = options[:label_text] || SimpleForm.label_text
37
+ label_text.call(html_escape(raw_label_text), required_label_text, options[:label].present?).strip.html_safe
34
38
  end
35
39
 
36
40
  def label_target
@@ -39,13 +43,14 @@ module SimpleForm
39
43
 
40
44
  def label_html_options
41
45
  label_html_classes = SimpleForm.additional_classes_for(:label) {
42
- [input_type, required_class, SimpleForm.label_class].compact
46
+ [input_type, required_class, disabled_class, SimpleForm.label_class].compact
43
47
  }
44
48
 
45
49
  label_options = html_options_for(:label, label_html_classes)
46
50
  if options.key?(:input_html) && options[:input_html].key?(:id)
47
51
  label_options[:for] = options[:input_html][:id]
48
52
  end
53
+
49
54
  label_options
50
55
  end
51
56
 
@@ -62,7 +67,7 @@ module SimpleForm
62
67
 
63
68
  # First check labels translation and then human attribute name.
64
69
  def label_translation #:nodoc:
65
- if SimpleForm.translate_labels && (translated_label = translate(:labels))
70
+ if SimpleForm.translate_labels && (translated_label = translate_from_namespace(:labels))
66
71
  translated_label
67
72
  elsif object.class.respond_to?(:human_attribute_name)
68
73
  object.class.human_attribute_name(reflection_or_attribute_name.to_s)