simple_form 1.5.2 → 2.0.0.rc

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (105) hide show
  1. data/CHANGELOG.md +224 -0
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +817 -0
  4. data/lib/generators/simple_form/install_generator.rb +15 -1
  5. data/lib/generators/simple_form/templates/README +12 -0
  6. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb.tt +173 -0
  7. data/lib/simple_form.rb +109 -43
  8. data/lib/simple_form/action_view_extensions/builder.rb +158 -53
  9. data/lib/simple_form/action_view_extensions/form_helper.rb +29 -22
  10. data/lib/simple_form/components.rb +11 -1
  11. data/lib/simple_form/components/errors.rb +6 -24
  12. data/lib/simple_form/components/hints.rb +7 -21
  13. data/lib/simple_form/components/html5.rb +26 -0
  14. data/lib/simple_form/components/labels.rb +15 -13
  15. data/lib/simple_form/components/maxlength.rb +41 -0
  16. data/lib/simple_form/components/min_max.rb +49 -0
  17. data/lib/simple_form/components/pattern.rb +34 -0
  18. data/lib/simple_form/components/placeholders.rb +5 -17
  19. data/lib/simple_form/components/readonly.rb +22 -0
  20. data/lib/simple_form/core_ext/hash.rb +16 -0
  21. data/lib/simple_form/error_notification.rb +8 -1
  22. data/lib/simple_form/form_builder.rb +86 -22
  23. data/lib/simple_form/helpers.rb +7 -4
  24. data/lib/simple_form/helpers/autofocus.rb +11 -0
  25. data/lib/simple_form/helpers/disabled.rb +15 -0
  26. data/lib/simple_form/helpers/readonly.rb +15 -0
  27. data/lib/simple_form/helpers/required.rb +7 -10
  28. data/lib/simple_form/helpers/validators.rb +4 -4
  29. data/lib/simple_form/inputs.rb +17 -13
  30. data/lib/simple_form/inputs/base.rb +50 -81
  31. data/lib/simple_form/inputs/boolean_input.rb +43 -4
  32. data/lib/simple_form/inputs/collection_check_boxes_input.rb +21 -0
  33. data/lib/simple_form/inputs/collection_input.rb +27 -13
  34. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +69 -0
  35. data/lib/simple_form/inputs/collection_select_input.rb +14 -0
  36. data/lib/simple_form/inputs/date_time_input.rb +2 -2
  37. data/lib/simple_form/inputs/grouped_collection_select_input.rb +41 -0
  38. data/lib/simple_form/inputs/hidden_input.rb +3 -6
  39. data/lib/simple_form/inputs/numeric_input.rb +3 -51
  40. data/lib/simple_form/inputs/password_input.rb +1 -2
  41. data/lib/simple_form/inputs/priority_input.rb +2 -2
  42. data/lib/simple_form/inputs/range_input.rb +1 -3
  43. data/lib/simple_form/inputs/string_input.rb +6 -8
  44. data/lib/simple_form/inputs/text_input.rb +1 -2
  45. data/lib/simple_form/version.rb +1 -1
  46. data/lib/simple_form/wrappers.rb +8 -0
  47. data/lib/simple_form/wrappers/builder.rb +75 -0
  48. data/lib/simple_form/wrappers/many.rb +68 -0
  49. data/lib/simple_form/wrappers/root.rb +34 -0
  50. data/lib/simple_form/wrappers/single.rb +18 -0
  51. data/test/action_view_extensions/builder_test.rb +195 -100
  52. data/test/action_view_extensions/form_helper_test.rb +24 -2
  53. data/test/components/label_test.rb +20 -5
  54. data/test/form_builder/association_test.rb +167 -0
  55. data/test/form_builder/button_test.rb +28 -0
  56. data/test/{error_notification_test.rb → form_builder/error_notification_test.rb} +2 -1
  57. data/test/form_builder/error_test.rb +101 -0
  58. data/test/form_builder/general_test.rb +348 -0
  59. data/test/form_builder/hint_test.rb +115 -0
  60. data/test/form_builder/input_field_test.rb +51 -0
  61. data/test/form_builder/label_test.rb +51 -0
  62. data/test/form_builder/wrapper_test.rb +140 -0
  63. data/test/generators/simple_form_generator_test.rb +32 -0
  64. data/test/inputs/boolean_input_test.rb +91 -0
  65. data/test/inputs/collection_check_boxes_input_test.rb +224 -0
  66. data/test/inputs/collection_radio_buttons_input_test.rb +326 -0
  67. data/test/inputs/collection_select_input_test.rb +241 -0
  68. data/test/inputs/datetime_input_test.rb +85 -0
  69. data/test/inputs/disabled_test.rb +38 -0
  70. data/test/inputs/discovery_test.rb +61 -0
  71. data/test/inputs/file_input_test.rb +16 -0
  72. data/test/inputs/general_test.rb +69 -0
  73. data/test/inputs/grouped_collection_select_input_test.rb +109 -0
  74. data/test/inputs/hidden_input_test.rb +30 -0
  75. data/test/inputs/numeric_input_test.rb +167 -0
  76. data/test/inputs/priority_input_test.rb +43 -0
  77. data/test/inputs/readonly_test.rb +61 -0
  78. data/test/inputs/required_test.rb +113 -0
  79. data/test/inputs/string_input_test.rb +140 -0
  80. data/test/inputs/text_input_test.rb +24 -0
  81. data/test/{discovery_inputs.rb → support/discovery_inputs.rb} +0 -0
  82. data/test/support/misc_helpers.rb +48 -6
  83. data/test/support/mock_controller.rb +2 -2
  84. data/test/support/models.rb +20 -5
  85. data/test/test_helper.rb +5 -8
  86. metadata +123 -98
  87. data/.gitignore +0 -3
  88. data/.gitmodules +0 -3
  89. data/.travis.yml +0 -15
  90. data/CHANGELOG.rdoc +0 -159
  91. data/Gemfile +0 -9
  92. data/README.rdoc +0 -466
  93. data/Rakefile +0 -27
  94. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +0 -93
  95. data/lib/simple_form/components/wrapper.rb +0 -38
  96. data/lib/simple_form/helpers/has_errors.rb +0 -15
  97. data/lib/simple_form/helpers/maxlength.rb +0 -24
  98. data/lib/simple_form/helpers/pattern.rb +0 -28
  99. data/simple_form.gemspec +0 -25
  100. data/test/components/error_test.rb +0 -56
  101. data/test/components/hint_test.rb +0 -74
  102. data/test/components/wrapper_test.rb +0 -63
  103. data/test/custom_components.rb +0 -7
  104. data/test/form_builder_test.rb +0 -930
  105. data/test/inputs_test.rb +0 -995
@@ -2,21 +2,60 @@ module SimpleForm
2
2
  module Inputs
3
3
  class BooleanInput < Base
4
4
  def input
5
- @builder.check_box(attribute_name, input_html_options)
5
+ if nested_boolean_style?
6
+ build_hidden_field_for_checkbox +
7
+ template.label_tag(nil, :class => "checkbox") {
8
+ build_check_box_without_hidden_field
9
+ }
10
+ else
11
+ build_check_box
12
+ end
6
13
  end
7
14
 
8
15
  def label_input
9
- input + (options[:label] == false ? "" : label)
16
+ if options[:label] == false
17
+ input
18
+ elsif nested_boolean_style?
19
+ build_hidden_field_for_checkbox +
20
+ @builder.label(label_target, label_html_options) {
21
+ build_check_box_without_hidden_field + label_text
22
+ }
23
+ else
24
+ input + label
25
+ end
10
26
  end
11
27
 
12
28
  private
13
29
 
30
+ # Build a checkbox tag using default unchecked value. This allows us to
31
+ # reuse the method for nested boolean style, but with no unchecked value,
32
+ # which won't generate the hidden checkbox. This is the default functionality
33
+ # in Rails > 3.2.1, and is backported in SimpleForm AV helpers.
34
+ def build_check_box(unchecked_value='0')
35
+ @builder.check_box(attribute_name, input_html_options, '1', unchecked_value)
36
+ end
37
+
38
+ # Build a checkbox without generating the hidden field. See
39
+ # #build_hidden_field_for_checkbox for more info.
40
+ def build_check_box_without_hidden_field
41
+ build_check_box(nil)
42
+ end
43
+
44
+ # Create a hidden field for the current checkbox, so we can simulate Rails
45
+ # functionality with hidden + checkbox, but under a nested context, where
46
+ # we need the hidden field to be *outside* the label (otherwise it
47
+ # generates invalid html - html5 only).
48
+ def build_hidden_field_for_checkbox
49
+ @builder.hidden_field(attribute_name, :value => '0', :id => nil,
50
+ :disabled => input_html_options[:disabled])
51
+ end
52
+
14
53
  # Booleans are not required by default because in most of the cases
15
54
  # it makes no sense marking them as required. The only exception is
16
55
  # Terms of Use usually presented at most sites sign up screen.
17
- def attribute_required_by_default?
56
+ def required_by_default?
18
57
  false
19
58
  end
20
59
  end
21
60
  end
22
- end
61
+ end
@@ -0,0 +1,21 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class CollectionCheckBoxesInput < CollectionRadioButtonsInput
4
+ protected
5
+
6
+ # Checkbox components do not use the required html tag.
7
+ # More info: https://github.com/plataformatec/simple_form/issues/340#issuecomment-2871956
8
+ def has_required?
9
+ false
10
+ end
11
+
12
+ def build_nested_boolean_style_item_tag(text, value, html_options)
13
+ @builder.check_box(attribute_name, html_options, value, nil) + text
14
+ end
15
+
16
+ def item_wrapper_class
17
+ "checkbox"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -13,9 +13,8 @@ module SimpleForm
13
13
  end
14
14
 
15
15
  def input
16
- label_method, value_method = detect_collection_methods
17
- @builder.send(:"collection_#{input_type}", attribute_name, collection,
18
- value_method, label_method, input_options, input_html_options)
16
+ raise NotImplementedError,
17
+ "input should be implemented by classes inheriting from CollectionInput"
19
18
  end
20
19
 
21
20
  def input_options
@@ -27,18 +26,23 @@ module SimpleForm
27
26
  private
28
27
 
29
28
  def collection
30
- @collection ||= (options.delete(:collection) || self.class.boolean_collection).to_a
29
+ @collection ||= begin
30
+ collection = options.delete(:collection) || self.class.boolean_collection
31
+ collection.respond_to?(:call) ? collection.call : collection.to_a
32
+ end
31
33
  end
32
34
 
33
- # Select components does not allow the required html tag.
34
35
  def has_required?
35
- super && input_type != :select
36
+ super && (input_options[:include_blank] || multiple?)
36
37
  end
37
38
 
38
39
  # Check if :include_blank must be included by default.
39
40
  def skip_include_blank?
40
- (options.keys & [:prompt, :include_blank, :default, :selected]).any? ||
41
- options[:input_html].try(:[], :multiple)
41
+ (options.keys & [:prompt, :include_blank, :default, :selected]).any? || multiple?
42
+ end
43
+
44
+ def multiple?
45
+ !!options[:input_html].try(:[], :multiple)
42
46
  end
43
47
 
44
48
  # Detect the right method to find the label and value for a collection.
@@ -58,10 +62,10 @@ module SimpleForm
58
62
  [label, value]
59
63
  end
60
64
 
61
- def detect_common_display_methods
62
- collection_classes = detect_collection_classes
65
+ def detect_common_display_methods(collection_classes = detect_collection_classes)
66
+ collection_translated = translate_collection if collection_classes == [Symbol]
63
67
 
64
- if collection_classes.include?(Array)
68
+ if collection_translated || collection_classes.include?(Array)
65
69
  { :label => :first, :value => :last }
66
70
  elsif collection_includes_basic_objects?(collection_classes)
67
71
  { :label => :to_s, :value => :to_s }
@@ -73,8 +77,8 @@ module SimpleForm
73
77
  end
74
78
  end
75
79
 
76
- def detect_collection_classes
77
- collection.map { |e| e.class }.uniq
80
+ def detect_collection_classes(some_collection = collection)
81
+ some_collection.map { |e| e.class }.uniq
78
82
  end
79
83
 
80
84
  def collection_includes_basic_objects?(collection_classes)
@@ -82,6 +86,16 @@ module SimpleForm
82
86
  String, Integer, Fixnum, Bignum, Float, NilClass, Symbol, TrueClass, FalseClass
83
87
  ]).any?
84
88
  end
89
+
90
+ def translate_collection
91
+ if translated_collection = translate(:options)
92
+ @collection = collection.map do |key|
93
+ [translated_collection[key] || key, key]
94
+ end
95
+ true
96
+ end
97
+ end
85
98
  end
86
99
  end
87
100
  end
101
+
@@ -0,0 +1,69 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class CollectionRadioButtonsInput < CollectionInput
4
+ def input
5
+ label_method, value_method = detect_collection_methods
6
+
7
+ @builder.send("collection_#{input_type}",
8
+ attribute_name, collection, value_method, label_method,
9
+ input_options, input_html_options, &collection_block_for_nested_boolean_style
10
+ )
11
+ end
12
+
13
+ def input_options
14
+ options = super
15
+ apply_default_collection_options!(options)
16
+ apply_nested_boolean_collection_options!(options) if nested_boolean_style?
17
+ options
18
+ end
19
+
20
+ protected
21
+
22
+ 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_class] = [
27
+ item_wrapper_class, options[:item_wrapper_class], SimpleForm.item_wrapper_class
28
+ ].compact.presence
29
+
30
+ unless options.key?(:collection_wrapper_tag)
31
+ options[:collection_wrapper_tag] = SimpleForm.collection_wrapper_tag
32
+ end
33
+ options[:collection_wrapper_class] = [
34
+ options[:collection_wrapper_class], SimpleForm.collection_wrapper_class
35
+ ].compact.presence
36
+ end
37
+
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
+ def collection_block_for_nested_boolean_style
46
+ return unless nested_boolean_style?
47
+
48
+ proc do |label_for, text, value, html_options|
49
+ build_nested_boolean_style_item_tag(text, value, html_options)
50
+ end
51
+ end
52
+
53
+ def build_nested_boolean_style_item_tag(text, value, html_options)
54
+ @builder.radio_button(attribute_name, value, html_options) + text
55
+ end
56
+
57
+ def item_wrapper_class
58
+ "radio"
59
+ end
60
+
61
+ # Do not attempt to generate label[for] attributes by default, unless an
62
+ # explicit html option is given. This avoids generating labels pointing to
63
+ # non existent fields.
64
+ def generate_label_for_attribute?
65
+ false
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,14 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class CollectionSelectInput < CollectionInput
4
+ def input
5
+ label_method, value_method = detect_collection_methods
6
+
7
+ @builder.collection_select(
8
+ attribute_name, collection, value_method, label_method,
9
+ input_options, input_html_options
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
@@ -5,12 +5,12 @@ module SimpleForm
5
5
  @builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options)
6
6
  end
7
7
 
8
- private
9
-
10
8
  def has_required?
11
9
  false
12
10
  end
13
11
 
12
+ private
13
+
14
14
  def label_target
15
15
  case input_type
16
16
  when :date, :datetime
@@ -0,0 +1,41 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class GroupedCollectionSelectInput < CollectionInput
4
+ def input
5
+ label_method, value_method = detect_collection_methods
6
+ @builder.grouped_collection_select(attribute_name, grouped_collection,
7
+ group_method, group_label_method, value_method, label_method,
8
+ input_options, input_html_options)
9
+ end
10
+
11
+ private
12
+
13
+ def grouped_collection
14
+ @grouped_collection ||= begin
15
+ grouped_collection = options.delete(:collection)
16
+ grouped_collection.respond_to?(:call) ? grouped_collection.call : grouped_collection.to_a
17
+ end
18
+ end
19
+
20
+ # Sample collection
21
+ def collection
22
+ @collection ||= grouped_collection.first.try(:send, group_method)
23
+ end
24
+
25
+ def group_method
26
+ @group_method ||= options.delete(:group_method)
27
+ end
28
+
29
+ def group_label_method
30
+ label = options.delete(:group_label_method)
31
+
32
+ unless label
33
+ common_method_for = detect_common_display_methods(detect_collection_classes(grouped_collection))
34
+ label = common_method_for[:label]
35
+ end
36
+
37
+ label
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,17 +1,14 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class HiddenInput < Base
4
- def render
4
+ disable :label, :errors, :hint, :required
5
+
6
+ def input
5
7
  @builder.hidden_field(attribute_name, input_html_options)
6
8
  end
7
- alias :input :render
8
9
 
9
10
  private
10
11
 
11
- def attribute_required?
12
- false
13
- end
14
-
15
12
  def required_class
16
13
  nil
17
14
  end
@@ -1,72 +1,24 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class NumericInput < Base
4
- enable :placeholder
4
+ enable :placeholder, :min_max
5
5
 
6
6
  def input
7
7
  add_size!
8
- if SimpleForm.html5
8
+ input_html_classes.unshift("numeric")
9
+ if html5?
9
10
  input_html_options[:type] ||= "number"
10
11
  input_html_options[:step] ||= integer? ? 1 : "any"
11
- infer_attributes_from_validations!
12
12
  end
13
13
  @builder.text_field(attribute_name, input_html_options)
14
14
  end
15
15
 
16
- def input_html_classes
17
- super.unshift("numeric")
18
- end
19
-
20
16
  private
21
17
 
22
18
  # Rails adds the size attr by default, if the :size key does not exist.
23
19
  def add_size!
24
20
  input_html_options[:size] ||= nil
25
21
  end
26
-
27
- def infer_attributes_from_validations!
28
- return unless has_validators?
29
-
30
- numeric_validator = find_numericality_validator or return
31
- validator_options = numeric_validator.options
32
-
33
- input_html_options[:min] ||= minimum_value(validator_options)
34
- input_html_options[:max] ||= maximum_value(validator_options)
35
- end
36
-
37
- def integer?
38
- input_type == :integer
39
- end
40
-
41
- def minimum_value(validator_options)
42
- if integer? && validator_options.key?(:greater_than)
43
- evaluate_validator_option(validator_options[:greater_than]) + 1
44
- else
45
- evaluate_validator_option(validator_options[:greater_than_or_equal_to])
46
- end
47
- end
48
-
49
- def maximum_value(validator_options)
50
- if integer? && validator_options.key?(:less_than)
51
- evaluate_validator_option(validator_options[:less_than]) - 1
52
- else
53
- evaluate_validator_option(validator_options[:less_than_or_equal_to])
54
- end
55
- end
56
-
57
- def find_numericality_validator
58
- find_validator(ActiveModel::Validations::NumericalityValidator)
59
- end
60
-
61
- def evaluate_validator_option(option)
62
- if option.is_a?(Numeric)
63
- option
64
- elsif option.is_a?(Symbol)
65
- object.send(option)
66
- elsif option.respond_to?(:call)
67
- option.call(object)
68
- end
69
- end
70
22
  end
71
23
  end
72
24
  end
@@ -1,11 +1,10 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class PasswordInput < Base
4
- enable :placeholder
4
+ enable :placeholder, :maxlength
5
5
 
6
6
  def input
7
7
  add_size!
8
- add_maxlength!
9
8
  @builder.password_field(attribute_name, input_html_options)
10
9
  end
11
10
  end
@@ -1,6 +1,6 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
- class PriorityInput < CollectionInput
3
+ class PriorityInput < CollectionSelectInput
4
4
  def input
5
5
  @builder.send(:"#{input_type}_select", attribute_name, input_priority,
6
6
  input_options, input_html_options)
@@ -10,7 +10,7 @@ module SimpleForm
10
10
  options[:priority] || SimpleForm.send(:"#{input_type}_priority")
11
11
  end
12
12
 
13
- protected
13
+ protected
14
14
 
15
15
  def has_required?
16
16
  false
@@ -1,10 +1,8 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class RangeInput < NumericInput
4
- disable :placeholder
5
-
6
4
  def input
7
- if SimpleForm.html5
5
+ if html5?
8
6
  input_html_options[:type] ||= "range"
9
7
  input_html_options[:step] ||= 1
10
8
  end