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.
- data/CHANGELOG.md +224 -0
- data/MIT-LICENSE +1 -1
- data/README.md +817 -0
- data/lib/generators/simple_form/install_generator.rb +15 -1
- data/lib/generators/simple_form/templates/README +12 -0
- data/lib/generators/simple_form/templates/config/initializers/simple_form.rb.tt +173 -0
- data/lib/simple_form.rb +109 -43
- data/lib/simple_form/action_view_extensions/builder.rb +158 -53
- data/lib/simple_form/action_view_extensions/form_helper.rb +29 -22
- data/lib/simple_form/components.rb +11 -1
- data/lib/simple_form/components/errors.rb +6 -24
- data/lib/simple_form/components/hints.rb +7 -21
- data/lib/simple_form/components/html5.rb +26 -0
- data/lib/simple_form/components/labels.rb +15 -13
- data/lib/simple_form/components/maxlength.rb +41 -0
- data/lib/simple_form/components/min_max.rb +49 -0
- data/lib/simple_form/components/pattern.rb +34 -0
- data/lib/simple_form/components/placeholders.rb +5 -17
- data/lib/simple_form/components/readonly.rb +22 -0
- data/lib/simple_form/core_ext/hash.rb +16 -0
- data/lib/simple_form/error_notification.rb +8 -1
- data/lib/simple_form/form_builder.rb +86 -22
- data/lib/simple_form/helpers.rb +7 -4
- data/lib/simple_form/helpers/autofocus.rb +11 -0
- data/lib/simple_form/helpers/disabled.rb +15 -0
- data/lib/simple_form/helpers/readonly.rb +15 -0
- data/lib/simple_form/helpers/required.rb +7 -10
- data/lib/simple_form/helpers/validators.rb +4 -4
- data/lib/simple_form/inputs.rb +17 -13
- data/lib/simple_form/inputs/base.rb +50 -81
- data/lib/simple_form/inputs/boolean_input.rb +43 -4
- data/lib/simple_form/inputs/collection_check_boxes_input.rb +21 -0
- data/lib/simple_form/inputs/collection_input.rb +27 -13
- data/lib/simple_form/inputs/collection_radio_buttons_input.rb +69 -0
- data/lib/simple_form/inputs/collection_select_input.rb +14 -0
- data/lib/simple_form/inputs/date_time_input.rb +2 -2
- data/lib/simple_form/inputs/grouped_collection_select_input.rb +41 -0
- data/lib/simple_form/inputs/hidden_input.rb +3 -6
- data/lib/simple_form/inputs/numeric_input.rb +3 -51
- data/lib/simple_form/inputs/password_input.rb +1 -2
- data/lib/simple_form/inputs/priority_input.rb +2 -2
- data/lib/simple_form/inputs/range_input.rb +1 -3
- data/lib/simple_form/inputs/string_input.rb +6 -8
- data/lib/simple_form/inputs/text_input.rb +1 -2
- data/lib/simple_form/version.rb +1 -1
- data/lib/simple_form/wrappers.rb +8 -0
- data/lib/simple_form/wrappers/builder.rb +75 -0
- data/lib/simple_form/wrappers/many.rb +68 -0
- data/lib/simple_form/wrappers/root.rb +34 -0
- data/lib/simple_form/wrappers/single.rb +18 -0
- data/test/action_view_extensions/builder_test.rb +195 -100
- data/test/action_view_extensions/form_helper_test.rb +24 -2
- data/test/components/label_test.rb +20 -5
- data/test/form_builder/association_test.rb +167 -0
- data/test/form_builder/button_test.rb +28 -0
- data/test/{error_notification_test.rb → form_builder/error_notification_test.rb} +2 -1
- data/test/form_builder/error_test.rb +101 -0
- data/test/form_builder/general_test.rb +348 -0
- data/test/form_builder/hint_test.rb +115 -0
- data/test/form_builder/input_field_test.rb +51 -0
- data/test/form_builder/label_test.rb +51 -0
- data/test/form_builder/wrapper_test.rb +140 -0
- data/test/generators/simple_form_generator_test.rb +32 -0
- data/test/inputs/boolean_input_test.rb +91 -0
- data/test/inputs/collection_check_boxes_input_test.rb +224 -0
- data/test/inputs/collection_radio_buttons_input_test.rb +326 -0
- data/test/inputs/collection_select_input_test.rb +241 -0
- data/test/inputs/datetime_input_test.rb +85 -0
- data/test/inputs/disabled_test.rb +38 -0
- data/test/inputs/discovery_test.rb +61 -0
- data/test/inputs/file_input_test.rb +16 -0
- data/test/inputs/general_test.rb +69 -0
- data/test/inputs/grouped_collection_select_input_test.rb +109 -0
- data/test/inputs/hidden_input_test.rb +30 -0
- data/test/inputs/numeric_input_test.rb +167 -0
- data/test/inputs/priority_input_test.rb +43 -0
- data/test/inputs/readonly_test.rb +61 -0
- data/test/inputs/required_test.rb +113 -0
- data/test/inputs/string_input_test.rb +140 -0
- data/test/inputs/text_input_test.rb +24 -0
- data/test/{discovery_inputs.rb → support/discovery_inputs.rb} +0 -0
- data/test/support/misc_helpers.rb +48 -6
- data/test/support/mock_controller.rb +2 -2
- data/test/support/models.rb +20 -5
- data/test/test_helper.rb +5 -8
- metadata +123 -98
- data/.gitignore +0 -3
- data/.gitmodules +0 -3
- data/.travis.yml +0 -15
- data/CHANGELOG.rdoc +0 -159
- data/Gemfile +0 -9
- data/README.rdoc +0 -466
- data/Rakefile +0 -27
- data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +0 -93
- data/lib/simple_form/components/wrapper.rb +0 -38
- data/lib/simple_form/helpers/has_errors.rb +0 -15
- data/lib/simple_form/helpers/maxlength.rb +0 -24
- data/lib/simple_form/helpers/pattern.rb +0 -28
- data/simple_form.gemspec +0 -25
- data/test/components/error_test.rb +0 -56
- data/test/components/hint_test.rb +0 -74
- data/test/components/wrapper_test.rb +0 -63
- data/test/custom_components.rb +0 -7
- data/test/form_builder_test.rb +0 -930
- 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
|
-
|
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
|
-
|
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
|
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
|
-
|
17
|
-
|
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 ||=
|
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 &&
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
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
|
-
|
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,6 +1,6 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
|
-
class PriorityInput <
|
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
|
-
|
13
|
+
protected
|
14
14
|
|
15
15
|
def has_required?
|
16
16
|
false
|