simple_form 3.1.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 (104) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +148 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +278 -68
  5. data/lib/generators/simple_form/install_generator.rb +1 -0
  6. data/lib/generators/simple_form/templates/README +3 -3
  7. data/lib/generators/simple_form/templates/_form.html.erb +2 -0
  8. data/lib/generators/simple_form/templates/_form.html.haml +2 -0
  9. data/lib/generators/simple_form/templates/_form.html.slim +1 -0
  10. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +19 -9
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +367 -63
  12. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +23 -8
  13. data/lib/simple_form/action_view_extensions/builder.rb +1 -0
  14. data/lib/simple_form/action_view_extensions/form_helper.rb +4 -1
  15. data/lib/simple_form/components/errors.rb +15 -2
  16. data/lib/simple_form/components/hints.rb +1 -0
  17. data/lib/simple_form/components/html5.rb +15 -4
  18. data/lib/simple_form/components/label_input.rb +2 -1
  19. data/lib/simple_form/components/labels.rb +12 -5
  20. data/lib/simple_form/components/maxlength.rb +8 -4
  21. data/lib/simple_form/components/min_max.rb +1 -0
  22. data/lib/simple_form/components/minlength.rb +38 -0
  23. data/lib/simple_form/components/pattern.rb +1 -0
  24. data/lib/simple_form/components/placeholders.rb +2 -1
  25. data/lib/simple_form/components/readonly.rb +1 -0
  26. data/lib/simple_form/components.rb +2 -0
  27. data/lib/simple_form/error_notification.rb +1 -0
  28. data/lib/simple_form/form_builder.rb +117 -35
  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 +2 -1
  34. data/lib/simple_form/helpers.rb +1 -0
  35. data/lib/simple_form/i18n_cache.rb +1 -0
  36. data/lib/simple_form/inputs/base.rb +36 -12
  37. data/lib/simple_form/inputs/block_input.rb +1 -0
  38. data/lib/simple_form/inputs/boolean_input.rb +14 -3
  39. data/lib/simple_form/inputs/collection_check_boxes_input.rb +2 -1
  40. data/lib/simple_form/inputs/collection_input.rb +7 -5
  41. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +3 -2
  42. data/lib/simple_form/inputs/collection_select_input.rb +1 -0
  43. data/lib/simple_form/inputs/color_input.rb +14 -0
  44. data/lib/simple_form/inputs/date_time_input.rb +13 -8
  45. data/lib/simple_form/inputs/file_input.rb +1 -0
  46. data/lib/simple_form/inputs/grouped_collection_select_input.rb +1 -0
  47. data/lib/simple_form/inputs/hidden_input.rb +1 -0
  48. data/lib/simple_form/inputs/numeric_input.rb +1 -0
  49. data/lib/simple_form/inputs/password_input.rb +2 -1
  50. data/lib/simple_form/inputs/priority_input.rb +1 -4
  51. data/lib/simple_form/inputs/range_input.rb +1 -0
  52. data/lib/simple_form/inputs/string_input.rb +3 -2
  53. data/lib/simple_form/inputs/text_input.rb +2 -1
  54. data/lib/simple_form/inputs.rb +2 -0
  55. data/lib/simple_form/map_type.rb +1 -0
  56. data/lib/simple_form/railtie.rb +1 -0
  57. data/lib/simple_form/tags.rb +7 -2
  58. data/lib/simple_form/version.rb +2 -1
  59. data/lib/simple_form/wrappers/builder.rb +1 -0
  60. data/lib/simple_form/wrappers/leaf.rb +2 -1
  61. data/lib/simple_form/wrappers/many.rb +1 -0
  62. data/lib/simple_form/wrappers/root.rb +2 -0
  63. data/lib/simple_form/wrappers/single.rb +2 -1
  64. data/lib/simple_form/wrappers.rb +1 -0
  65. data/lib/simple_form.rb +79 -14
  66. data/test/action_view_extensions/builder_test.rb +28 -9
  67. data/test/action_view_extensions/form_helper_test.rb +3 -2
  68. data/test/components/custom_components_test.rb +62 -0
  69. data/test/components/label_test.rb +33 -4
  70. data/test/form_builder/association_test.rb +33 -2
  71. data/test/form_builder/button_test.rb +1 -0
  72. data/test/form_builder/error_notification_test.rb +1 -0
  73. data/test/form_builder/error_test.rb +44 -9
  74. data/test/form_builder/general_test.rb +92 -20
  75. data/test/form_builder/hint_test.rb +6 -0
  76. data/test/form_builder/input_field_test.rb +76 -70
  77. data/test/form_builder/label_test.rb +27 -4
  78. data/test/form_builder/wrapper_test.rb +66 -14
  79. data/test/generators/simple_form_generator_test.rb +4 -3
  80. data/test/inputs/boolean_input_test.rb +35 -0
  81. data/test/inputs/collection_check_boxes_input_test.rb +38 -14
  82. data/test/inputs/collection_radio_buttons_input_test.rb +48 -24
  83. data/test/inputs/collection_select_input_test.rb +40 -39
  84. data/test/inputs/color_input_test.rb +10 -0
  85. data/test/inputs/datetime_input_test.rb +12 -8
  86. data/test/inputs/disabled_test.rb +14 -0
  87. data/test/inputs/discovery_test.rb +23 -0
  88. data/test/inputs/file_input_test.rb +1 -0
  89. data/test/inputs/general_test.rb +3 -2
  90. data/test/inputs/grouped_collection_select_input_test.rb +11 -10
  91. data/test/inputs/hidden_input_test.rb +1 -0
  92. data/test/inputs/numeric_input_test.rb +5 -1
  93. data/test/inputs/priority_input_test.rb +7 -6
  94. data/test/inputs/readonly_test.rb +1 -0
  95. data/test/inputs/required_test.rb +45 -0
  96. data/test/inputs/string_input_test.rb +18 -16
  97. data/test/inputs/text_input_test.rb +13 -0
  98. data/test/simple_form_test.rb +1 -0
  99. data/test/support/discovery_inputs.rb +8 -0
  100. data/test/support/misc_helpers.rb +44 -2
  101. data/test/support/mock_controller.rb +7 -1
  102. data/test/support/models.rb +105 -22
  103. data/test/test_helper.rb +14 -3
  104. metadata +42 -36
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Components
3
4
  module Errors
@@ -10,7 +11,15 @@ module SimpleForm
10
11
  end
11
12
 
12
13
  def has_errors?
13
- object && object.respond_to?(:errors) && errors.present?
14
+ object_with_errors? || object.nil? && has_custom_error?
15
+ end
16
+
17
+ def has_value?
18
+ object && object.respond_to?(attribute_name) && object.send(attribute_name).present?
19
+ end
20
+
21
+ def valid?
22
+ !has_errors? && has_value?
14
23
  end
15
24
 
16
25
  protected
@@ -25,6 +34,10 @@ module SimpleForm
25
34
  has_custom_error? ? options[:error] : full_errors.send(error_method)
26
35
  end
27
36
 
37
+ def object_with_errors?
38
+ object && object.respond_to?(:errors) && errors.present?
39
+ end
40
+
28
41
  def error_method
29
42
  options[:error_method] || SimpleForm.error_method
30
43
  end
@@ -38,7 +51,7 @@ module SimpleForm
38
51
  end
39
52
 
40
53
  def errors_on_attribute
41
- object.errors[attribute_name]
54
+ object.errors[attribute_name] || []
42
55
  end
43
56
 
44
57
  def full_errors_on_attribute
@@ -1,3 +1,4 @@
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.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Components
3
4
  module HTML5
@@ -7,10 +8,12 @@ module SimpleForm
7
8
 
8
9
  def html5(wrapper_options = nil)
9
10
  @html5 = true
10
- if has_required?
11
- input_html_options[:required] = true
12
- input_html_options[:'aria-required'] = true
13
- end
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
+
14
17
  nil
15
18
  end
16
19
 
@@ -18,6 +21,14 @@ module SimpleForm
18
21
  @html5
19
22
  end
20
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
+
21
32
  def has_required?
22
33
  # We need to check browser_validations because
23
34
  # some browsers are still checking required even
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Components
3
4
  module LabelInput
@@ -20,7 +21,7 @@ module SimpleForm
20
21
  def deprecated_component(namespace, wrapper_options)
21
22
  method = method(namespace)
22
23
 
23
- if method.arity == 0
24
+ if method.arity.zero?
24
25
  ActiveSupport::Deprecation.warn(SimpleForm::CUSTOM_INPUT_DEPRECATION_WARN % { name: namespace })
25
26
 
26
27
  method.call
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Components
3
4
  module Labels
@@ -6,18 +7,24 @@ 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(:"required.html", scope: i18n_scope, 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(:"required.text", scope: i18n_scope, default: 'required')
17
18
  end
18
19
 
19
20
  def translate_required_mark
20
- I18n.t(:"simple_form.required.mark", default: '*')
21
+ I18n.t(:"required.mark", scope: i18n_scope, default: '*')
22
+ end
23
+
24
+ private
25
+
26
+ def i18n_scope
27
+ SimpleForm.i18n_scope
21
28
  end
22
29
  end
23
30
 
@@ -42,7 +49,7 @@ module SimpleForm
42
49
 
43
50
  def label_html_options
44
51
  label_html_classes = SimpleForm.additional_classes_for(:label) {
45
- [input_type, required_class, SimpleForm.label_class].compact
52
+ [input_type, required_class, disabled_class, SimpleForm.label_class].compact
46
53
  }
47
54
 
48
55
  label_options = html_options_for(:label, label_html_classes)
@@ -1,3 +1,4 @@
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.
@@ -15,10 +16,7 @@ module SimpleForm
15
16
  maxlength
16
17
  else
17
18
  length_validator = find_length_validator
18
-
19
- if length_validator && !has_tokenizer?(length_validator)
20
- length_validator.options[:is] || length_validator.options[:maximum]
21
- end
19
+ maximum_length_value_from(length_validator)
22
20
  end
23
21
  end
24
22
 
@@ -29,6 +27,12 @@ module SimpleForm
29
27
  def has_tokenizer?(length_validator)
30
28
  length_validator.options[:tokenizer]
31
29
  end
30
+
31
+ def maximum_length_value_from(length_validator)
32
+ if length_validator
33
+ length_validator.options[:is] || length_validator.options[:maximum]
34
+ end
35
+ end
32
36
  end
33
37
  end
34
38
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Components
3
4
  module MinMax
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ module SimpleForm
3
+ module Components
4
+ # Needs to be enabled in order to do automatic lookups.
5
+ module Minlength
6
+ def minlength(wrapper_options = nil)
7
+ input_html_options[:minlength] ||= minimum_length_from_validation
8
+ nil
9
+ end
10
+
11
+ private
12
+
13
+ def minimum_length_from_validation
14
+ minlength = options[:minlength]
15
+ if minlength.is_a?(String) || minlength.is_a?(Integer)
16
+ minlength
17
+ else
18
+ length_validator = find_length_validator
19
+ minimum_length_value_from(length_validator)
20
+ end
21
+ end
22
+
23
+ def find_length_validator
24
+ find_validator(:length)
25
+ end
26
+
27
+ def has_tokenizer?(length_validator)
28
+ length_validator.options[:tokenizer]
29
+ end
30
+
31
+ def minimum_length_value_from(length_validator)
32
+ if length_validator
33
+ length_validator.options[:is] || length_validator.options[:minimum]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,4 @@
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.
@@ -1,3 +1,4 @@
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.
@@ -7,7 +8,7 @@ module SimpleForm
7
8
  nil
8
9
  end
9
10
 
10
- def placeholder_text
11
+ def placeholder_text(wrapper_options = nil)
11
12
  placeholder = options[:placeholder]
12
13
  placeholder.is_a?(String) ? placeholder : translate_from_namespace(:placeholders)
13
14
  end
@@ -1,3 +1,4 @@
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.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  # Components are a special type of helpers that can work on their own.
3
4
  # For example, by using a component, it will automatically change the
@@ -15,6 +16,7 @@ module SimpleForm
15
16
  autoload :Labels
16
17
  autoload :MinMax
17
18
  autoload :Maxlength
19
+ autoload :Minlength
18
20
  autoload :Pattern
19
21
  autoload :Placeholders
20
22
  autoload :Readonly
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  class ErrorNotification
3
4
  delegate :object, :object_name, :template, to: :@builder
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'active_support/core_ext/object/deep_dup'
2
3
  require 'simple_form/map_type'
3
4
  require 'simple_form/tags'
@@ -12,24 +13,25 @@ module SimpleForm
12
13
  'update' => 'edit'
13
14
  }
14
15
 
15
- ATTRIBUTE_COMPONENTS = [:html5, :min_max, :maxlength, :placeholder, :pattern, :readonly]
16
+ ATTRIBUTE_COMPONENTS = %i[html5 min_max maxlength minlength placeholder pattern readonly]
16
17
 
17
18
  extend MapType
18
19
  include SimpleForm::Inputs
19
20
 
20
- map_type :text, to: SimpleForm::Inputs::TextInput
21
- map_type :file, to: SimpleForm::Inputs::FileInput
22
- map_type :string, :email, :search, :tel, :url, :uuid, to: SimpleForm::Inputs::StringInput
23
- map_type :password, to: SimpleForm::Inputs::PasswordInput
24
- map_type :integer, :decimal, :float, to: SimpleForm::Inputs::NumericInput
25
- map_type :range, to: SimpleForm::Inputs::RangeInput
26
- map_type :check_boxes, to: SimpleForm::Inputs::CollectionCheckBoxesInput
27
- map_type :radio_buttons, to: SimpleForm::Inputs::CollectionRadioButtonsInput
28
- map_type :select, to: SimpleForm::Inputs::CollectionSelectInput
29
- map_type :grouped_select, to: SimpleForm::Inputs::GroupedCollectionSelectInput
30
- map_type :date, :time, :datetime, to: SimpleForm::Inputs::DateTimeInput
31
- map_type :country, :time_zone, to: SimpleForm::Inputs::PriorityInput
32
- map_type :boolean, to: SimpleForm::Inputs::BooleanInput
21
+ map_type :text, :hstore, :json, :jsonb, to: SimpleForm::Inputs::TextInput
22
+ map_type :file, to: SimpleForm::Inputs::FileInput
23
+ map_type :string, :email, :search, :tel, :url, :uuid, :citext, to: SimpleForm::Inputs::StringInput
24
+ map_type :password, to: SimpleForm::Inputs::PasswordInput
25
+ map_type :integer, :decimal, :float, to: SimpleForm::Inputs::NumericInput
26
+ map_type :range, to: SimpleForm::Inputs::RangeInput
27
+ map_type :check_boxes, to: SimpleForm::Inputs::CollectionCheckBoxesInput
28
+ map_type :radio_buttons, to: SimpleForm::Inputs::CollectionRadioButtonsInput
29
+ map_type :select, to: SimpleForm::Inputs::CollectionSelectInput
30
+ map_type :grouped_select, to: SimpleForm::Inputs::GroupedCollectionSelectInput
31
+ map_type :date, :time, :datetime, to: SimpleForm::Inputs::DateTimeInput
32
+ map_type :country, :time_zone, to: SimpleForm::Inputs::PriorityInput
33
+ map_type :boolean, to: SimpleForm::Inputs::BooleanInput
34
+ map_type :hidden, to: SimpleForm::Inputs::HiddenInput
33
35
 
34
36
  def self.discovery_cache
35
37
  @discovery_cache ||= {}
@@ -37,6 +39,7 @@ module SimpleForm
37
39
 
38
40
  def initialize(*) #:nodoc:
39
41
  super
42
+ @object = convert_to_model(@object)
40
43
  @defaults = options[:defaults]
41
44
  @wrapper = SimpleForm.wrapper(options[:wrapper] || SimpleForm.default_wrapper)
42
45
  end
@@ -47,6 +50,11 @@ module SimpleForm
47
50
  # label + input + hint (when defined) + errors (when exists), and all can
48
51
  # be configured inside a wrapper html.
49
52
  #
53
+ # If a block is given, the contents of the block will replace the input
54
+ # field that would otherwise be generated automatically. The content will
55
+ # be given a label and wrapper div to make it consistent with the other
56
+ # elements in the form.
57
+ #
50
58
  # == Examples
51
59
  #
52
60
  # # Imagine @user has error "can't be blank" on name
@@ -130,16 +138,39 @@ module SimpleForm
130
138
  # <input class="string required" id="user_name" maxlength="100"
131
139
  # name="user[name]" type="text" value="Carlos" />
132
140
  #
141
+ # It also support validation classes once it is configured.
142
+ #
143
+ # # config/initializers/simple_form.rb
144
+ # SimpleForm.setup do |config|
145
+ # config.input_field_valid_class = 'is-valid'
146
+ # config.input_field_error_class = 'is-invalid'
147
+ # end
148
+ #
149
+ # simple_form_for @user do |f|
150
+ # f.input_field :name
151
+ # end
152
+ #
153
+ # When the validation happens, the input will be rendered with
154
+ # the class configured according to the validation:
155
+ #
156
+ # - when the input is valid:
157
+ #
158
+ # <input class="is-valid string required" id="user_name" value="Carlos" />
159
+ #
160
+ # - when the input is invalid:
161
+ #
162
+ # <input class="is-invalid string required" id="user_name" value="" />
163
+ #
133
164
  def input_field(attribute_name, options = {})
134
165
  components = (wrapper.components.map(&:namespace) & ATTRIBUTE_COMPONENTS)
135
166
 
136
167
  options = options.dup
137
- options[:input_html] = options.except(:as, :boolean_style, :collection, :label_method, :value_method, *components)
168
+ options[:input_html] = options.except(:as, :boolean_style, :collection, :disabled, :label_method, :value_method, :prompt, *components)
138
169
  options = @defaults.deep_dup.deep_merge(options) if @defaults
139
170
 
140
171
  input = find_input(attribute_name, options)
141
172
  wrapper = find_wrapper(input.input_type, options)
142
- components = components.concat([:input]).map { |component| SimpleForm::Wrappers::Leaf.new(component) }
173
+ components = build_input_field_components(components.push(:input))
143
174
 
144
175
  SimpleForm::Wrappers::Root.new(components, wrapper.options.merge(wrapper: false)).render input
145
176
  end
@@ -206,8 +237,8 @@ module SimpleForm
206
237
  options = args.extract_options!.dup
207
238
  options[:class] = [SimpleForm.button_class, options[:class]].compact
208
239
  args << options
209
- if respond_to?("#{type}_button")
210
- send("#{type}_button", *args, &block)
240
+ if respond_to?(:"#{type}_button")
241
+ send(:"#{type}_button", *args, &block)
211
242
  else
212
243
  send(type, *args, &block)
213
244
  end
@@ -284,7 +315,7 @@ module SimpleForm
284
315
  #
285
316
  # f.label :name # Do I18n lookup
286
317
  # f.label :name, "Name" # Same behavior as Rails, do not add required tag
287
- # f.label :name, label: "Name" # Same as above, but adds required tag
318
+ # f.label :name, label: "Name" # Same as above, but adds required tag
288
319
  #
289
320
  # f.label :name, required: false
290
321
  # f.label :name, id: "cool_label"
@@ -293,7 +324,7 @@ module SimpleForm
293
324
  return super if args.first.is_a?(String) || block_given?
294
325
 
295
326
  options = args.extract_options!.dup
296
- options[:label_html] = options.except(:label, :required, :as)
327
+ options[:label_html] = options.except(:label, :label_text, :required, :as)
297
328
 
298
329
  column = find_attribute_column(attribute_name)
299
330
  input_type = default_input_type(attribute_name, column, options)
@@ -455,13 +486,17 @@ module SimpleForm
455
486
  relation = reflection.klass.all
456
487
 
457
488
  if reflection.respond_to?(:scope) && reflection.scope
458
- relation = reflection.klass.instance_exec(&reflection.scope)
489
+ if reflection.scope.parameters.any?
490
+ relation = reflection.klass.instance_exec(object, &reflection.scope)
491
+ else
492
+ relation = reflection.klass.instance_exec(&reflection.scope)
493
+ end
459
494
  else
460
495
  order = reflection.options[:order]
461
496
  conditions = reflection.options[:conditions]
462
497
  conditions = object.instance_exec(&conditions) if conditions.respond_to?(:call)
463
498
 
464
- relation = relation.where(conditions)
499
+ relation = relation.where(conditions) if relation.respond_to?(:where)
465
500
  relation = relation.order(order) if relation.respond_to?(:order)
466
501
  end
467
502
 
@@ -476,7 +511,7 @@ module SimpleForm
476
511
  when :has_one
477
512
  raise ArgumentError, ":has_one associations are not supported by f.association"
478
513
  else
479
- if options[:as] == :select
514
+ if options[:as] == :select || options[:as] == :grouped_select
480
515
  html_options = options[:input_html] ||= {}
481
516
  html_options[:multiple] = true unless html_options.key?(:multiple)
482
517
  end
@@ -504,25 +539,25 @@ module SimpleForm
504
539
  end
505
540
 
506
541
  # Attempt to guess the better input type given the defined options. By
507
- # default alwayls fallback to the user :as option, or to a :select when a
542
+ # default always fallback to the user :as option, or to a :select when a
508
543
  # collection is given.
509
544
  def default_input_type(attribute_name, column, options)
510
545
  return options[:as].to_sym if options[:as]
511
- return :select if options[:collection]
512
546
  custom_type = find_custom_type(attribute_name.to_s) and return custom_type
547
+ return :select if options[:collection]
513
548
 
514
549
  input_type = column.try(:type)
515
550
  case input_type
516
551
  when :timestamp
517
552
  :datetime
518
- when :string, nil
553
+ when :string, :citext, nil
519
554
  case attribute_name.to_s
520
- when /password/ then :password
521
- when /time_zone/ then :time_zone
522
- when /country/ then :country
523
- when /email/ then :email
524
- when /phone/ then :tel
525
- when /url/ then :url
555
+ when /(?:\b|\W|_)password(?:\b|\W|_)/ then :password
556
+ when /(?:\b|\W|_)time_zone(?:\b|\W|_)/ then :time_zone
557
+ when /(?:\b|\W|_)country(?:\b|\W|_)/ then :country
558
+ when /(?:\b|\W|_)email(?:\b|\W|_)/ then :email
559
+ when /(?:\b|\W|_)phone(?:\b|\W|_)/ then :tel
560
+ when /(?:\b|\W|_)url(?:\b|\W|_)/ then :url
526
561
  else
527
562
  file_method?(attribute_name) ? :file : (input_type || :string)
528
563
  end
@@ -537,13 +572,34 @@ module SimpleForm
537
572
  }.try(:last) if SimpleForm.input_mappings
538
573
  end
539
574
 
575
+ # Internal: Try to discover whether an attribute corresponds to a file or not.
576
+ #
577
+ # Most upload Gems add some kind of attributes to the ActiveRecord's model they are included in.
578
+ # This method tries to guess if an attribute belongs to some of these Gems by checking the presence
579
+ # of their methods using `#respond_to?`.
580
+ #
581
+ # Note: This does not support multiple file upload inputs, as this is very application-specific.
582
+ #
583
+ # The order here was choosen based on the popularity of Gems and for commodity - e.g. the method
584
+ # with the suffix `_url` is present in three Gems, so it's checked with priority:
585
+ #
586
+ # - `#{attribute_name}_attachment` - ActiveStorage >= `5.2` and Refile >= `0.2.0` <= `0.4.0`
587
+ # - `#{attribute_name}_url` - Shrine >= `0.9.0`, Refile >= `0.6.0` and CarrierWave >= `0.2.1`
588
+ # - `#{attribute_name}_attacher` - Refile >= `0.4.0` and Shrine >= `0.9.0`
589
+ # - `#{attribute_name}_file_name` - Paperclip ~> `2.0` (added for backwards compatibility)
590
+ #
591
+ # Returns a Boolean.
540
592
  def file_method?(attribute_name)
541
- file = @object.send(attribute_name) if @object.respond_to?(attribute_name)
542
- file && SimpleForm.file_methods.any? { |m| file.respond_to?(m) }
593
+ @object.respond_to?("#{attribute_name}_attachment") ||
594
+ @object.respond_to?("#{attribute_name}_url") ||
595
+ @object.respond_to?("#{attribute_name}_attacher") ||
596
+ @object.respond_to?("#{attribute_name}_file_name")
543
597
  end
544
598
 
545
599
  def find_attribute_column(attribute_name)
546
- if @object.respond_to?(:column_for_attribute) && @object.has_attribute?(attribute_name)
600
+ if @object.respond_to?(:type_for_attribute) && @object.has_attribute?(attribute_name)
601
+ @object.type_for_attribute(attribute_name.to_s)
602
+ elsif @object.respond_to?(:column_for_attribute) && @object.has_attribute?(attribute_name)
547
603
  @object.column_for_attribute(attribute_name)
548
604
  end
549
605
  end
@@ -632,5 +688,31 @@ module SimpleForm
632
688
 
633
689
  nil
634
690
  end
691
+
692
+ def build_input_field_components(components)
693
+ components.map do |component|
694
+ if component == :input
695
+ SimpleForm::Wrappers::Leaf.new(component, build_input_field_options)
696
+ else
697
+ SimpleForm::Wrappers::Leaf.new(component)
698
+ end
699
+ end
700
+ end
701
+
702
+ def build_input_field_options
703
+ input_field_options = {}
704
+ valid_class = SimpleForm.input_field_valid_class
705
+ error_class = SimpleForm.input_field_error_class
706
+
707
+ if error_class.present?
708
+ input_field_options[:error_class] = error_class
709
+ end
710
+
711
+ if valid_class.present?
712
+ input_field_options[:valid_class] = valid_class
713
+ end
714
+
715
+ input_field_options
716
+ end
635
717
  end
636
718
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Helpers
3
4
  module Autofocus
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Helpers
3
4
  module Disabled
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Helpers
3
4
  module Readonly
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module SimpleForm
2
3
  module Helpers
3
4
  module Required
@@ -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
@@ -1,3 +1,4 @@
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
@@ -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