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,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Helpers
3
4
  module Validators
@@ -24,7 +25,7 @@ module SimpleForm
24
25
  end
25
26
 
26
27
  def action_validator_match?(validator)
27
- return true if !validator.options.include?(:on)
28
+ return true unless validator.options.include?(:on)
28
29
 
29
30
  case validator.options[:on]
30
31
  when :save
@@ -36,8 +37,8 @@ module SimpleForm
36
37
  end
37
38
  end
38
39
 
39
- def find_validator(validator)
40
- attribute_validators.find { |v| validator === v } if has_validators?
40
+ def find_validator(kind)
41
+ attribute_validators.find { |v| v.kind == kind } if has_validators?
41
42
  end
42
43
  end
43
44
  end
@@ -1,12 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  # Helpers are made of several helpers that cannot be turned on automatically.
3
4
  # For instance, disabled cannot be turned on automatically, it requires the
4
- # user to explicitly pass the option :disabled => true so it may work.
5
+ # user to explicitly pass the option disabled: true so it may work.
5
6
  module Helpers
6
- autoload :Autofocus, 'simple_form/helpers/autofocus'
7
- autoload :Disabled, 'simple_form/helpers/disabled'
8
- autoload :Readonly, 'simple_form/helpers/readonly'
9
- autoload :Required, 'simple_form/helpers/required'
10
- autoload :Validators, 'simple_form/helpers/validators'
7
+ autoload :Autofocus, 'simple_form/helpers/autofocus'
8
+ autoload :Disabled, 'simple_form/helpers/disabled'
9
+ autoload :Readonly, 'simple_form/helpers/readonly'
10
+ autoload :Required, 'simple_form/helpers/required'
11
+ autoload :Validators, 'simple_form/helpers/validators'
11
12
  end
12
13
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  # A lot of configuration values are retrived from I18n,
3
4
  # like boolean collection, required string. This module provides
@@ -1,6 +1,14 @@
1
+ # frozen_string_literal: true
2
+ require 'simple_form/i18n_cache'
3
+ require 'active_support/core_ext/string/output_safety'
4
+ require 'action_view/helpers'
5
+
1
6
  module SimpleForm
2
7
  module Inputs
3
8
  class Base
9
+ include ERB::Util
10
+ include ActionView::Helpers::TranslationHelper
11
+
4
12
  extend I18nCache
5
13
 
6
14
  include SimpleForm::Helpers::Autofocus
@@ -14,6 +22,7 @@ module SimpleForm
14
22
  include SimpleForm::Components::HTML5
15
23
  include SimpleForm::Components::LabelInput
16
24
  include SimpleForm::Components::Maxlength
25
+ include SimpleForm::Components::Minlength
17
26
  include SimpleForm::Components::MinMax
18
27
  include SimpleForm::Components::Pattern
19
28
  include SimpleForm::Components::Placeholders
@@ -22,7 +31,7 @@ module SimpleForm
22
31
  attr_reader :attribute_name, :column, :input_type, :reflection,
23
32
  :options, :input_html_options, :input_html_classes, :html_classes
24
33
 
25
- delegate :template, :object, :object_name, :lookup_model_names, :lookup_action, :to => :@builder
34
+ delegate :template, :object, :object_name, :lookup_model_names, :lookup_action, to: :@builder
26
35
 
27
36
  class_attribute :default_options
28
37
  self.default_options = {}
@@ -43,27 +52,30 @@ module SimpleForm
43
52
  enable :hint
44
53
 
45
54
  # Usually disabled, needs to be enabled explicitly passing true as option.
46
- disable :maxlength, :placeholder, :pattern, :min_max
55
+ disable :maxlength, :minlength, :placeholder, :pattern, :min_max
47
56
 
48
57
  def initialize(builder, attribute_name, column, input_type, options = {})
49
58
  super
50
59
 
51
- @builder = builder
52
- @attribute_name = attribute_name
53
- @column = column
54
- @input_type = input_type
55
- @reflection = options.delete(:reflection)
56
- @options = options.reverse_merge!(self.class.default_options)
57
- @required = calculate_required
60
+ options = options.dup
61
+ @builder = builder
62
+ @attribute_name = attribute_name
63
+ @column = column
64
+ @input_type = input_type
65
+ @reflection = options.delete(:reflection)
66
+ @options = options.reverse_merge!(self.class.default_options)
67
+ @required = calculate_required
58
68
 
59
69
  # Notice that html_options_for receives a reference to input_html_classes.
60
70
  # This means that classes added dynamically to input_html_classes will
61
71
  # still propagate to input_html_options.
62
- @html_classes = SimpleForm.additional_classes_for(:input) {
63
- [input_type, required_class, readonly_class, disabled_class].compact
64
- }
72
+ @html_classes = SimpleForm.additional_classes_for(:input) { additional_classes }
65
73
 
66
74
  @input_html_classes = @html_classes.dup
75
+ if SimpleForm.input_class && !input_html_classes.empty?
76
+ input_html_classes << SimpleForm.input_class
77
+ end
78
+
67
79
  @input_html_options = html_options_for(:input, input_html_classes).tap do |o|
68
80
  o[:readonly] = true if has_readonly?
69
81
  o[:disabled] = true if has_disabled?
@@ -71,7 +83,7 @@ module SimpleForm
71
83
  end
72
84
  end
73
85
 
74
- def input
86
+ def input(wrapper_options = nil)
75
87
  raise NotImplementedError
76
88
  end
77
89
 
@@ -79,14 +91,33 @@ module SimpleForm
79
91
  options
80
92
  end
81
93
 
82
- private
94
+ def additional_classes
95
+ @additional_classes ||= [input_type, required_class, readonly_class, disabled_class].compact
96
+ end
83
97
 
84
- def add_size!
85
- input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
98
+ def input_class
99
+ "#{lookup_model_names.join('_')}_#{reflection_or_attribute_name}"
86
100
  end
87
101
 
102
+ private
103
+
88
104
  def limit
89
- column && column.limit
105
+ if column
106
+ decimal_or_float? ? decimal_limit : column_limit
107
+ end
108
+ end
109
+
110
+ def column_limit
111
+ column.limit
112
+ end
113
+
114
+ # Add one for decimal point
115
+ def decimal_limit
116
+ column_limit && (column_limit + 1)
117
+ end
118
+
119
+ def decimal_or_float?
120
+ column.type == :float || column.type == :decimal
90
121
  end
91
122
 
92
123
  def nested_boolean_style?
@@ -95,14 +126,15 @@ module SimpleForm
95
126
 
96
127
  # Find reflection name when available, otherwise use attribute
97
128
  def reflection_or_attribute_name
98
- reflection ? reflection.name : attribute_name
129
+ @reflection_or_attribute_name ||= reflection ? reflection.name : attribute_name
99
130
  end
100
131
 
101
132
  # Retrieve options for the given namespace from the options hash
102
133
  def html_options_for(namespace, css_classes)
103
- html_options = options[:"#{namespace}_html"] || {}
134
+ html_options = options[:"#{namespace}_html"]
135
+ html_options = html_options ? html_options.dup : {}
104
136
  css_classes << html_options[:class] if html_options.key?(:class)
105
- html_options[:class] = css_classes
137
+ html_options[:class] = css_classes unless css_classes.empty?
106
138
  html_options
107
139
  end
108
140
 
@@ -139,7 +171,7 @@ module SimpleForm
139
171
  # email: 'E-mail.'
140
172
  #
141
173
  # Take a look at our locale example file.
142
- def translate(namespace, default='')
174
+ def translate_from_namespace(namespace, default = '')
143
175
  model_names = lookup_model_names.dup
144
176
  lookups = []
145
177
 
@@ -151,10 +183,31 @@ module SimpleForm
151
183
  lookups << :"#{joined_model_names}.#{reflection_or_attribute_name}"
152
184
  end
153
185
  lookups << :"defaults.#{lookup_action}.#{reflection_or_attribute_name}"
154
- lookups << :"defaults.#{attribute_name}"
186
+ lookups << :"defaults.#{reflection_or_attribute_name}"
155
187
  lookups << default
156
188
 
157
- I18n.t(lookups.shift, :scope => :"simple_form.#{namespace}", :default => lookups).presence
189
+ I18n.t(lookups.shift, scope: :"#{i18n_scope}.#{namespace}", default: lookups).presence
190
+ end
191
+
192
+ def merge_wrapper_options(options, wrapper_options)
193
+ if wrapper_options
194
+ wrapper_options.merge(options) do |key, oldval, newval|
195
+ case key.to_s
196
+ when "class"
197
+ Array(oldval) + Array(newval)
198
+ when "data", "aria"
199
+ oldval.merge(newval)
200
+ else
201
+ newval
202
+ end
203
+ end
204
+ else
205
+ options
206
+ end
207
+ end
208
+
209
+ def i18n_scope
210
+ SimpleForm.i18n_scope
158
211
  end
159
212
  end
160
213
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class BlockInput < Base
@@ -6,9 +7,9 @@ module SimpleForm
6
7
  @block = block
7
8
  end
8
9
 
9
- def input
10
+ def input(wrapper_options = nil)
10
11
  template.capture(&@block)
11
12
  end
12
13
  end
13
14
  end
14
- end
15
+ end
@@ -1,47 +1,58 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class BooleanInput < Base
4
- def input
5
+ def input(wrapper_options = nil)
6
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
7
+
5
8
  if nested_boolean_style?
6
9
  build_hidden_field_for_checkbox +
7
- template.label_tag(nil, :class => "checkbox") {
8
- build_check_box_without_hidden_field
10
+ template.label_tag(nil, class: boolean_label_class) {
11
+ build_check_box_without_hidden_field(merged_input_options) +
12
+ inline_label
9
13
  }
10
14
  else
11
- build_check_box
15
+ build_check_box(unchecked_value, merged_input_options)
12
16
  end
13
17
  end
14
18
 
15
- def label_input
16
- if options[:label] == false
17
- input
19
+ def label_input(wrapper_options = nil)
20
+ if options[:label] == false || inline_label?
21
+ input(wrapper_options)
18
22
  elsif nested_boolean_style?
19
23
  html_options = label_html_options.dup
20
- html_options[:class].push(:checkbox)
24
+ html_options[:class] ||= []
25
+ html_options[:class].push(boolean_label_class) if boolean_label_class
26
+
27
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
21
28
 
22
29
  build_hidden_field_for_checkbox +
23
30
  @builder.label(label_target, html_options) {
24
- build_check_box_without_hidden_field + label_text
31
+ build_check_box_without_hidden_field(merged_input_options) + label_text
25
32
  }
26
33
  else
27
- input + label
34
+ input(wrapper_options) + label(wrapper_options)
28
35
  end
29
36
  end
30
37
 
31
38
  private
32
39
 
40
+ def boolean_label_class
41
+ options[:boolean_label_class] || SimpleForm.boolean_label_class
42
+ end
43
+
33
44
  # Build a checkbox tag using default unchecked value. This allows us to
34
45
  # reuse the method for nested boolean style, but with no unchecked value,
35
46
  # which won't generate the hidden checkbox. This is the default functionality
36
47
  # in Rails > 3.2.1, and is backported in SimpleForm AV helpers.
37
- def build_check_box(unchecked_value='0')
38
- @builder.check_box(attribute_name, input_html_options, '1', unchecked_value)
48
+ def build_check_box(unchecked_value, options)
49
+ @builder.check_box(attribute_name, options, checked_value, unchecked_value)
39
50
  end
40
51
 
41
52
  # Build a checkbox without generating the hidden field. See
42
53
  # #build_hidden_field_for_checkbox for more info.
43
- def build_check_box_without_hidden_field
44
- build_check_box(nil)
54
+ def build_check_box_without_hidden_field(options)
55
+ build_check_box(nil, options)
45
56
  end
46
57
 
47
58
  # Create a hidden field for the current checkbox, so we can simulate Rails
@@ -49,8 +60,24 @@ module SimpleForm
49
60
  # we need the hidden field to be *outside* the label (otherwise it
50
61
  # generates invalid html - html5 only).
51
62
  def build_hidden_field_for_checkbox
52
- @builder.hidden_field(attribute_name, :value => '0', :id => nil,
53
- :disabled => input_html_options[:disabled])
63
+ return "" if !include_hidden? || !unchecked_value
64
+ options = { value: unchecked_value, id: nil, disabled: input_html_options[:disabled] }
65
+ options[:name] = input_html_options[:name] if input_html_options.key?(:name)
66
+
67
+ @builder.hidden_field(attribute_name, options)
68
+ end
69
+
70
+ def inline_label?
71
+ nested_boolean_style? && options[:inline_label]
72
+ end
73
+
74
+ def inline_label
75
+ inline_option = options[:inline_label]
76
+
77
+ if inline_option
78
+ label = inline_option == true ? label_text : html_escape(inline_option)
79
+ " #{label}".html_safe
80
+ end
54
81
  end
55
82
 
56
83
  # Booleans are not required by default because in most of the cases
@@ -59,6 +86,18 @@ module SimpleForm
59
86
  def required_by_default?
60
87
  false
61
88
  end
89
+
90
+ def include_hidden?
91
+ options.fetch(:include_hidden, true)
92
+ end
93
+
94
+ def checked_value
95
+ options.fetch(:checked_value, '1')
96
+ end
97
+
98
+ def unchecked_value
99
+ options.fetch(:unchecked_value, '0')
100
+ end
62
101
  end
63
102
  end
64
103
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class CollectionCheckBoxesInput < CollectionRadioButtonsInput
@@ -10,7 +11,7 @@ module SimpleForm
10
11
  end
11
12
 
12
13
  def build_nested_boolean_style_item_tag(collection_builder)
13
- collection_builder.check_box + collection_builder.text
14
+ collection_builder.check_box + collection_builder.text.to_s
14
15
  end
15
16
 
16
17
  def item_wrapper_class
@@ -1,25 +1,33 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class CollectionInput < Base
5
+ BASIC_OBJECT_CLASSES = [String, Integer, Float, NilClass, Symbol, TrueClass, FalseClass]
6
+ BASIC_OBJECT_CLASSES.push(Fixnum, Bignum) unless 1.class == Integer
7
+
4
8
  # Default boolean collection for use with selects/radios when no
5
9
  # collection is given. Always fallback to this boolean collection.
6
10
  # Texts can be translated using i18n in "simple_form.yes" and
7
11
  # "simple_form.no" keys. See the example locale file.
8
12
  def self.boolean_collection
9
13
  i18n_cache :boolean_collection do
10
- [ [I18n.t(:"simple_form.yes", :default => 'Yes'), true],
11
- [I18n.t(:"simple_form.no", :default => 'No'), false] ]
14
+ [ [I18n.t(:"simple_form.yes", default: 'Yes'), true],
15
+ [I18n.t(:"simple_form.no", default: 'No'), false] ]
12
16
  end
13
17
  end
14
18
 
15
- def input
19
+ def input(wrapper_options = nil)
16
20
  raise NotImplementedError,
17
21
  "input should be implemented by classes inheriting from CollectionInput"
18
22
  end
19
23
 
20
24
  def input_options
21
25
  options = super
26
+
22
27
  options[:include_blank] = true unless skip_include_blank?
28
+ translate_option options, :prompt
29
+ translate_option options, :include_blank
30
+
23
31
  options
24
32
  end
25
33
 
@@ -33,12 +41,12 @@ module SimpleForm
33
41
  end
34
42
 
35
43
  def has_required?
36
- super && (input_options[:include_blank] || multiple?)
44
+ super && (input_options[:include_blank] || input_options[:prompt] || multiple?)
37
45
  end
38
46
 
39
47
  # Check if :include_blank must be included by default.
40
48
  def skip_include_blank?
41
- (options.keys & [:prompt, :include_blank, :default, :selected]).any? || multiple?
49
+ (options.keys & %i[prompt include_blank default selected]).any? || multiple?
42
50
  end
43
51
 
44
52
  def multiple?
@@ -66,36 +74,51 @@ module SimpleForm
66
74
  collection_translated = translate_collection if collection_classes == [Symbol]
67
75
 
68
76
  if collection_translated || collection_classes.include?(Array)
69
- { :label => :first, :value => :last }
77
+ { label: :first, value: :second }
70
78
  elsif collection_includes_basic_objects?(collection_classes)
71
- { :label => :to_s, :value => :to_s }
79
+ { label: :to_s, value: :to_s }
72
80
  else
73
- sample = collection.first || collection.last
74
-
75
- { :label => SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
76
- :value => SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
81
+ detect_method_from_class(collection_classes)
77
82
  end
78
83
  end
79
84
 
85
+ def detect_method_from_class(collection_classes)
86
+ sample = collection.first || collection.last
87
+
88
+ { label: SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
89
+ value: SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
90
+ end
91
+
80
92
  def detect_collection_classes(some_collection = collection)
81
- some_collection.map { |e| e.class }.uniq
93
+ some_collection.map(&:class).uniq
82
94
  end
83
95
 
84
96
  def collection_includes_basic_objects?(collection_classes)
85
- (collection_classes & [
86
- String, Integer, Fixnum, Bignum, Float, NilClass, Symbol, TrueClass, FalseClass
87
- ]).any?
97
+ (collection_classes & BASIC_OBJECT_CLASSES).any?
88
98
  end
89
99
 
90
100
  def translate_collection
91
- if translated_collection = translate(:options)
101
+ if translated_collection = translate_from_namespace(:options)
92
102
  @collection = collection.map do |key|
93
- [translated_collection[key] || key, key]
103
+ html_key = "#{key}_html".to_sym
104
+
105
+ if translated_collection[html_key]
106
+ [translated_collection[html_key].html_safe || key, key.to_s]
107
+ else
108
+ [translated_collection[key] || key, key.to_s]
109
+ end
94
110
  end
95
111
  true
96
112
  end
97
113
  end
114
+
115
+ def translate_option(options, key)
116
+ if options[key] == :translate
117
+ namespace = key.to_s.pluralize
118
+
119
+ options[key] = translate_from_namespace(namespace, true)
120
+ end
121
+ end
98
122
  end
99
123
  end
100
124
  end
101
-
@@ -1,47 +1,39 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class CollectionRadioButtonsInput < CollectionInput
4
- def input
5
+ def input(wrapper_options = nil)
5
6
  label_method, value_method = detect_collection_methods
6
7
 
7
- @builder.send("collection_#{input_type}",
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
10
+ @builder.send(:"collection_#{input_type}",
8
11
  attribute_name, collection, value_method, label_method,
9
- input_options, input_html_options, &collection_block_for_nested_boolean_style
12
+ input_options, merged_input_options,
13
+ &collection_block_for_nested_boolean_style
10
14
  )
11
15
  end
12
16
 
13
17
  def input_options
14
18
  options = super
15
19
  apply_default_collection_options!(options)
16
- apply_nested_boolean_collection_options!(options) if nested_boolean_style?
17
20
  options
18
21
  end
19
22
 
20
23
  protected
21
24
 
22
25
  def apply_default_collection_options!(options)
23
- unless options.key?(:item_wrapper_tag)
24
- options[:item_wrapper_tag] = SimpleForm.item_wrapper_tag
25
- end
26
+ options[:item_wrapper_tag] ||= options.fetch(:item_wrapper_tag, SimpleForm.item_wrapper_tag)
26
27
  options[:item_wrapper_class] = [
27
28
  item_wrapper_class, options[:item_wrapper_class], SimpleForm.item_wrapper_class
28
- ].compact.presence
29
+ ].compact.presence if SimpleForm.include_default_input_wrapper_class
29
30
 
30
- unless options.key?(:collection_wrapper_tag)
31
- options[:collection_wrapper_tag] = SimpleForm.collection_wrapper_tag
32
- end
31
+ options[:collection_wrapper_tag] ||= options.fetch(:collection_wrapper_tag, SimpleForm.collection_wrapper_tag)
33
32
  options[:collection_wrapper_class] = [
34
33
  options[:collection_wrapper_class], SimpleForm.collection_wrapper_class
35
34
  ].compact.presence
36
35
  end
37
36
 
38
- # Force item wrapper to be a label when using nested boolean, to support
39
- # configuring classes through :item_wrapper_class, and to maintain
40
- # compatibility with :inline style and default :item_wrapper_tag.
41
- def apply_nested_boolean_collection_options!(options)
42
- options[:item_wrapper_tag] = :label
43
- end
44
-
45
37
  def collection_block_for_nested_boolean_style
46
38
  return unless nested_boolean_style?
47
39
 
@@ -49,7 +41,7 @@ module SimpleForm
49
41
  end
50
42
 
51
43
  def build_nested_boolean_style_item_tag(collection_builder)
52
- collection_builder.radio_button + collection_builder.text
44
+ collection_builder.radio_button + collection_builder.text.to_s
53
45
  end
54
46
 
55
47
  def item_wrapper_class
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class CollectionSelectInput < CollectionInput
4
- def input
5
+ def input(wrapper_options = nil)
5
6
  label_method, value_method = detect_collection_methods
6
7
 
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+
7
10
  @builder.collection_select(
8
11
  attribute_name, collection, value_method, label_method,
9
- input_options, input_html_options
12
+ input_options, merged_input_options
10
13
  )
11
14
  end
12
15
  end
@@ -1,27 +1,38 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class DateTimeInput < Base
4
- def input
5
- @builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options)
6
- end
5
+ def input(wrapper_options = nil)
6
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
7
7
 
8
- def has_required?
9
- false
8
+ if use_html5_inputs?
9
+ @builder.send(:"#{input_type}_field", attribute_name, merged_input_options)
10
+ else
11
+ @builder.send(:"#{input_type}_select", attribute_name, input_options, merged_input_options)
12
+ end
10
13
  end
11
14
 
12
15
  private
13
16
 
14
17
  def label_target
15
- position = case input_type
16
- when :date, :datetime
17
- date_order = input_options[:order] || I18n.t('date.order')
18
- date_order.first
18
+ if use_html5_inputs?
19
+ attribute_name
19
20
  else
20
- :hour
21
+ position = case input_type
22
+ when :date, :datetime
23
+ date_order = input_options[:order] || I18n.t('date.order')
24
+ date_order.first.to_sym
25
+ else
26
+ :hour
27
+ end
28
+
29
+ position = ActionView::Helpers::DateTimeSelector::POSITION[position]
30
+ "#{attribute_name}_#{position}i"
21
31
  end
32
+ end
22
33
 
23
- position = ActionView::Helpers::DateTimeSelector::POSITION[position]
24
- "#{attribute_name}_#{position}i"
34
+ def use_html5_inputs?
35
+ input_options[:html5]
25
36
  end
26
37
  end
27
38
  end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Inputs
3
4
  class FileInput < Base
4
- def input
5
- @builder.file_field(attribute_name, input_html_options)
5
+ def input(wrapper_options = nil)
6
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
7
+
8
+ @builder.file_field(attribute_name, merged_input_options)
6
9
  end
7
10
  end
8
11
  end