simple_form 3.0.4 → 3.1.0.rc1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -43
- data/MIT-LICENSE +1 -1
- data/README.md +146 -71
- data/lib/generators/simple_form/install_generator.rb +2 -2
- data/lib/generators/simple_form/templates/README +3 -4
- data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +19 -3
- data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +83 -22
- data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +1 -1
- data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +7 -2
- data/lib/simple_form.rb +38 -6
- data/lib/simple_form/action_view_extensions/form_helper.rb +1 -1
- data/lib/simple_form/components/errors.rb +27 -5
- data/lib/simple_form/components/hints.rb +2 -2
- data/lib/simple_form/components/html5.rb +1 -1
- data/lib/simple_form/components/label_input.rb +20 -2
- data/lib/simple_form/components/labels.rb +9 -5
- data/lib/simple_form/components/maxlength.rb +1 -1
- data/lib/simple_form/components/min_max.rb +1 -1
- data/lib/simple_form/components/pattern.rb +1 -1
- data/lib/simple_form/components/placeholders.rb +2 -2
- data/lib/simple_form/components/readonly.rb +1 -1
- data/lib/simple_form/form_builder.rb +92 -59
- data/lib/simple_form/helpers.rb +5 -5
- data/lib/simple_form/inputs/base.rb +34 -12
- data/lib/simple_form/inputs/block_input.rb +1 -1
- data/lib/simple_form/inputs/boolean_input.rb +23 -13
- data/lib/simple_form/inputs/collection_input.rb +32 -9
- data/lib/simple_form/inputs/collection_radio_buttons_input.rb +6 -11
- data/lib/simple_form/inputs/collection_select_input.rb +4 -2
- data/lib/simple_form/inputs/date_time_input.rb +12 -2
- data/lib/simple_form/inputs/file_input.rb +4 -2
- data/lib/simple_form/inputs/grouped_collection_select_input.rb +15 -3
- data/lib/simple_form/inputs/hidden_input.rb +4 -2
- data/lib/simple_form/inputs/numeric_input.rb +5 -4
- data/lib/simple_form/inputs/password_input.rb +4 -2
- data/lib/simple_form/inputs/priority_input.rb +4 -2
- data/lib/simple_form/inputs/range_input.rb +1 -1
- data/lib/simple_form/inputs/string_input.rb +4 -2
- data/lib/simple_form/inputs/text_input.rb +4 -2
- data/lib/simple_form/railtie.rb +7 -0
- data/lib/simple_form/tags.rb +7 -0
- data/lib/simple_form/version.rb +1 -1
- data/lib/simple_form/wrappers.rb +1 -0
- data/lib/simple_form/wrappers/builder.rb +5 -5
- data/lib/simple_form/wrappers/leaf.rb +28 -0
- data/lib/simple_form/wrappers/many.rb +5 -6
- data/lib/simple_form/wrappers/root.rb +1 -1
- data/lib/simple_form/wrappers/single.rb +5 -3
- data/test/action_view_extensions/builder_test.rb +2 -2
- data/test/components/label_test.rb +1 -1
- data/test/form_builder/association_test.rb +17 -0
- data/test/form_builder/error_notification_test.rb +1 -1
- data/test/form_builder/error_test.rb +51 -32
- data/test/form_builder/general_test.rb +2 -2
- data/test/form_builder/input_field_test.rb +21 -37
- data/test/form_builder/label_test.rb +24 -1
- data/test/form_builder/wrapper_test.rb +67 -0
- data/test/generators/simple_form_generator_test.rb +2 -2
- data/test/inputs/boolean_input_test.rb +50 -2
- data/test/inputs/collection_check_boxes_input_test.rb +40 -11
- data/test/inputs/collection_radio_buttons_input_test.rb +76 -17
- data/test/inputs/collection_select_input_test.rb +108 -3
- data/test/inputs/datetime_input_test.rb +105 -38
- data/test/inputs/discovery_test.rb +12 -1
- data/test/inputs/grouped_collection_select_input_test.rb +36 -0
- data/test/inputs/string_input_test.rb +20 -0
- data/test/simple_form_test.rb +8 -0
- data/test/support/discovery_inputs.rb +12 -2
- data/test/support/misc_helpers.rb +46 -8
- data/test/support/models.rb +49 -24
- metadata +7 -7
@@ -1,31 +1,36 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
3
|
class BooleanInput < Base
|
4
|
-
def input
|
4
|
+
def input(wrapper_options = nil)
|
5
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
6
|
+
|
5
7
|
if nested_boolean_style?
|
6
8
|
build_hidden_field_for_checkbox +
|
7
|
-
template.label_tag(nil, class:
|
8
|
-
build_check_box_without_hidden_field +
|
9
|
+
template.label_tag(nil, class: SimpleForm.boolean_label_class) {
|
10
|
+
build_check_box_without_hidden_field(merged_input_options) +
|
11
|
+
inline_label
|
9
12
|
}
|
10
13
|
else
|
11
|
-
build_check_box
|
14
|
+
build_check_box(unchecked_value, merged_input_options)
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
15
|
-
def label_input
|
18
|
+
def label_input(wrapper_options = nil)
|
16
19
|
if options[:label] == false
|
17
|
-
input
|
20
|
+
input(wrapper_options)
|
18
21
|
elsif nested_boolean_style?
|
19
22
|
html_options = label_html_options.dup
|
20
23
|
html_options[:class] ||= []
|
21
|
-
html_options[:class].push(
|
24
|
+
html_options[:class].push(SimpleForm.boolean_label_class) if SimpleForm.boolean_label_class
|
25
|
+
|
26
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
22
27
|
|
23
28
|
build_hidden_field_for_checkbox +
|
24
29
|
@builder.label(label_target, html_options) {
|
25
|
-
build_check_box_without_hidden_field + label_text
|
30
|
+
build_check_box_without_hidden_field(merged_input_options) + label_text
|
26
31
|
}
|
27
32
|
else
|
28
|
-
input + label
|
33
|
+
input(wrapper_options) + label(wrapper_options)
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
@@ -35,14 +40,14 @@ module SimpleForm
|
|
35
40
|
# reuse the method for nested boolean style, but with no unchecked value,
|
36
41
|
# which won't generate the hidden checkbox. This is the default functionality
|
37
42
|
# in Rails > 3.2.1, and is backported in SimpleForm AV helpers.
|
38
|
-
def build_check_box(unchecked_value
|
43
|
+
def build_check_box(unchecked_value, options)
|
39
44
|
@builder.check_box(attribute_name, input_html_options, checked_value, unchecked_value)
|
40
45
|
end
|
41
46
|
|
42
47
|
# Build a checkbox without generating the hidden field. See
|
43
48
|
# #build_hidden_field_for_checkbox for more info.
|
44
|
-
def build_check_box_without_hidden_field
|
45
|
-
build_check_box(nil)
|
49
|
+
def build_check_box_without_hidden_field(options)
|
50
|
+
build_check_box(nil, options)
|
46
51
|
end
|
47
52
|
|
48
53
|
# Create a hidden field for the current checkbox, so we can simulate Rails
|
@@ -58,7 +63,12 @@ module SimpleForm
|
|
58
63
|
|
59
64
|
def inline_label
|
60
65
|
inline_option = options[:inline_label]
|
61
|
-
|
66
|
+
|
67
|
+
if inline_option
|
68
|
+
label = inline_option == true ? " #{label_text}" : " #{html_escape(inline_option)}"
|
69
|
+
|
70
|
+
label.html_safe
|
71
|
+
end
|
62
72
|
end
|
63
73
|
|
64
74
|
# Booleans are not required by default because in most of the cases
|
@@ -12,14 +12,20 @@ module SimpleForm
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def input
|
15
|
+
def input(wrapper_options = nil)
|
16
16
|
raise NotImplementedError,
|
17
17
|
"input should be implemented by classes inheriting from CollectionInput"
|
18
18
|
end
|
19
19
|
|
20
20
|
def input_options
|
21
21
|
options = super
|
22
|
+
|
22
23
|
options[:include_blank] = true unless skip_include_blank?
|
24
|
+
|
25
|
+
[:prompt, :include_blank].each do |key|
|
26
|
+
translate_option options, key
|
27
|
+
end
|
28
|
+
|
23
29
|
options
|
24
30
|
end
|
25
31
|
|
@@ -33,7 +39,7 @@ module SimpleForm
|
|
33
39
|
end
|
34
40
|
|
35
41
|
def has_required?
|
36
|
-
super && (input_options[:include_blank] || multiple?)
|
42
|
+
super && (input_options[:include_blank] || input_options[:prompt] || multiple?)
|
37
43
|
end
|
38
44
|
|
39
45
|
# Check if :include_blank must be included by default.
|
@@ -70,13 +76,17 @@ module SimpleForm
|
|
70
76
|
elsif collection_includes_basic_objects?(collection_classes)
|
71
77
|
{ label: :to_s, value: :to_s }
|
72
78
|
else
|
73
|
-
|
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) } }
|
79
|
+
detect_method_from_class(collection_classes)
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
83
|
+
def detect_method_from_class(collection_classes)
|
84
|
+
sample = collection.first || collection.last
|
85
|
+
|
86
|
+
{ label: SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
|
87
|
+
value: SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
|
88
|
+
end
|
89
|
+
|
80
90
|
def detect_collection_classes(some_collection = collection)
|
81
91
|
some_collection.map { |e| e.class }.uniq
|
82
92
|
end
|
@@ -88,14 +98,27 @@ module SimpleForm
|
|
88
98
|
end
|
89
99
|
|
90
100
|
def translate_collection
|
91
|
-
if translated_collection =
|
101
|
+
if translated_collection = translate_from_namespace(:options)
|
92
102
|
@collection = collection.map do |key|
|
93
|
-
|
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,19 +1,21 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
3
|
class CollectionRadioButtonsInput < CollectionInput
|
4
|
-
def input
|
4
|
+
def input(wrapper_options = nil)
|
5
5
|
label_method, value_method = detect_collection_methods
|
6
6
|
|
7
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
8
|
+
|
7
9
|
@builder.send("collection_#{input_type}",
|
8
10
|
attribute_name, collection, value_method, label_method,
|
9
|
-
input_options,
|
11
|
+
input_options, merged_input_options,
|
12
|
+
&collection_block_for_nested_boolean_style
|
10
13
|
)
|
11
14
|
end
|
12
15
|
|
13
16
|
def input_options
|
14
17
|
options = super
|
15
18
|
apply_default_collection_options!(options)
|
16
|
-
apply_nested_boolean_collection_options!(options) if nested_boolean_style?
|
17
19
|
options
|
18
20
|
end
|
19
21
|
|
@@ -23,7 +25,7 @@ module SimpleForm
|
|
23
25
|
options[:item_wrapper_tag] ||= options.fetch(:item_wrapper_tag, SimpleForm.item_wrapper_tag)
|
24
26
|
options[:item_wrapper_class] = [
|
25
27
|
item_wrapper_class, options[:item_wrapper_class], SimpleForm.item_wrapper_class
|
26
|
-
].compact.presence
|
28
|
+
].compact.presence if SimpleForm.include_default_input_wrapper_class
|
27
29
|
|
28
30
|
options[:collection_wrapper_tag] ||= options.fetch(:collection_wrapper_tag, SimpleForm.collection_wrapper_tag)
|
29
31
|
options[:collection_wrapper_class] = [
|
@@ -31,13 +33,6 @@ module SimpleForm
|
|
31
33
|
].compact.presence
|
32
34
|
end
|
33
35
|
|
34
|
-
# Force item wrapper to be a label when using nested boolean, to support
|
35
|
-
# configuring classes through :item_wrapper_class, and to maintain
|
36
|
-
# compatibility with :inline style and default :item_wrapper_tag.
|
37
|
-
def apply_nested_boolean_collection_options!(options)
|
38
|
-
options[:item_wrapper_tag] = :label
|
39
|
-
end
|
40
|
-
|
41
36
|
def collection_block_for_nested_boolean_style
|
42
37
|
return unless nested_boolean_style?
|
43
38
|
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
3
|
class CollectionSelectInput < CollectionInput
|
4
|
-
def input
|
4
|
+
def input(wrapper_options = nil)
|
5
5
|
label_method, value_method = detect_collection_methods
|
6
6
|
|
7
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
8
|
+
|
7
9
|
@builder.collection_select(
|
8
10
|
attribute_name, collection, value_method, label_method,
|
9
|
-
input_options,
|
11
|
+
input_options, merged_input_options
|
10
12
|
)
|
11
13
|
end
|
12
14
|
end
|
@@ -1,8 +1,14 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
3
|
class DateTimeInput < Base
|
4
|
-
def input
|
5
|
-
|
4
|
+
def input(wrapper_options = nil)
|
5
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
6
|
+
|
7
|
+
if use_html5_inputs?
|
8
|
+
@builder.send(:"#{input_type}_field", attribute_name, merged_input_options)
|
9
|
+
else
|
10
|
+
@builder.send(:"#{input_type}_select", attribute_name, input_options, merged_input_options)
|
11
|
+
end
|
6
12
|
end
|
7
13
|
|
8
14
|
private
|
@@ -19,6 +25,10 @@ module SimpleForm
|
|
19
25
|
position = ActionView::Helpers::DateTimeSelector::POSITION[position]
|
20
26
|
"#{attribute_name}_#{position}i"
|
21
27
|
end
|
28
|
+
|
29
|
+
def use_html5_inputs?
|
30
|
+
input_options[:html5]
|
31
|
+
end
|
22
32
|
end
|
23
33
|
end
|
24
34
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
3
|
class FileInput < Base
|
4
|
-
def input
|
5
|
-
|
4
|
+
def input(wrapper_options = nil)
|
5
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
6
|
+
|
7
|
+
@builder.file_field(attribute_name, merged_input_options)
|
6
8
|
end
|
7
9
|
end
|
8
10
|
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
3
|
class GroupedCollectionSelectInput < CollectionInput
|
4
|
-
def input
|
4
|
+
def input(wrapper_options = nil)
|
5
5
|
label_method, value_method = detect_collection_methods
|
6
|
+
|
7
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
8
|
+
|
6
9
|
@builder.grouped_collection_select(attribute_name, grouped_collection,
|
7
10
|
group_method, group_label_method, value_method, label_method,
|
8
|
-
input_options,
|
11
|
+
input_options, merged_input_options)
|
9
12
|
end
|
10
13
|
|
11
14
|
private
|
@@ -19,7 +22,7 @@ module SimpleForm
|
|
19
22
|
|
20
23
|
# Sample collection
|
21
24
|
def collection
|
22
|
-
@collection ||= grouped_collection.
|
25
|
+
@collection ||= grouped_collection.map { |collection| collection.try(:send, group_method) }.detect(&:present?) || []
|
23
26
|
end
|
24
27
|
|
25
28
|
def group_method
|
@@ -36,6 +39,15 @@ module SimpleForm
|
|
36
39
|
|
37
40
|
label
|
38
41
|
end
|
42
|
+
|
43
|
+
def detect_method_from_class(collection_classes)
|
44
|
+
return {} if collection_classes.empty?
|
45
|
+
|
46
|
+
sample = collection_classes.first
|
47
|
+
|
48
|
+
{ label: SimpleForm.collection_label_methods.find { |m| sample.instance_methods.include?(m) },
|
49
|
+
value: SimpleForm.collection_value_methods.find { |m| sample.instance_methods.include?(m) } }
|
50
|
+
end
|
39
51
|
end
|
40
52
|
end
|
41
53
|
end
|
@@ -3,8 +3,10 @@ module SimpleForm
|
|
3
3
|
class HiddenInput < Base
|
4
4
|
disable :label, :errors, :hint, :required
|
5
5
|
|
6
|
-
def input
|
7
|
-
|
6
|
+
def input(wrapper_options = nil)
|
7
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
8
|
+
|
9
|
+
@builder.hidden_field(attribute_name, merged_input_options)
|
8
10
|
end
|
9
11
|
|
10
12
|
private
|
@@ -3,16 +3,17 @@ module SimpleForm
|
|
3
3
|
class NumericInput < Base
|
4
4
|
enable :placeholder, :min_max
|
5
5
|
|
6
|
-
def input
|
6
|
+
def input(wrapper_options = nil)
|
7
7
|
input_html_classes.unshift("numeric")
|
8
8
|
if html5?
|
9
9
|
input_html_options[:type] ||= "number"
|
10
10
|
input_html_options[:step] ||= integer? ? 1 : "any"
|
11
11
|
end
|
12
|
-
@builder.text_field(attribute_name, input_html_options)
|
13
|
-
end
|
14
12
|
|
15
|
-
|
13
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
14
|
+
|
15
|
+
@builder.text_field(attribute_name, merged_input_options)
|
16
|
+
end
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
@@ -3,8 +3,10 @@ module SimpleForm
|
|
3
3
|
class PasswordInput < Base
|
4
4
|
enable :placeholder, :maxlength
|
5
5
|
|
6
|
-
def input
|
7
|
-
|
6
|
+
def input(wrapper_options = nil)
|
7
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
8
|
+
|
9
|
+
@builder.password_field(attribute_name, merged_input_options)
|
8
10
|
end
|
9
11
|
end
|
10
12
|
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
3
|
class PriorityInput < CollectionSelectInput
|
4
|
-
def input
|
4
|
+
def input(wrapper_options = nil)
|
5
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
6
|
+
|
5
7
|
@builder.send(:"#{input_type}_select", attribute_name, input_priority,
|
6
|
-
input_options,
|
8
|
+
input_options, merged_input_options)
|
7
9
|
end
|
8
10
|
|
9
11
|
def input_priority
|
@@ -3,13 +3,15 @@ module SimpleForm
|
|
3
3
|
class StringInput < Base
|
4
4
|
enable :placeholder, :maxlength, :pattern
|
5
5
|
|
6
|
-
def input
|
6
|
+
def input(wrapper_options = nil)
|
7
7
|
unless string?
|
8
8
|
input_html_classes.unshift("string")
|
9
9
|
input_html_options[:type] ||= input_type if html5?
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
13
|
+
|
14
|
+
@builder.text_field(attribute_name, merged_input_options)
|
13
15
|
end
|
14
16
|
|
15
17
|
private
|
@@ -3,8 +3,10 @@ module SimpleForm
|
|
3
3
|
class TextInput < Base
|
4
4
|
enable :placeholder, :maxlength
|
5
5
|
|
6
|
-
def input
|
7
|
-
|
6
|
+
def input(wrapper_options = nil)
|
7
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
8
|
+
|
9
|
+
@builder.text_area(attribute_name, merged_input_options)
|
8
10
|
end
|
9
11
|
end
|
10
12
|
end
|
data/lib/simple_form/railtie.rb
CHANGED
@@ -3,5 +3,12 @@ require 'rails/railtie'
|
|
3
3
|
module SimpleForm
|
4
4
|
class Railtie < Rails::Railtie
|
5
5
|
config.eager_load_namespaces << SimpleForm
|
6
|
+
|
7
|
+
config.after_initialize do
|
8
|
+
unless SimpleForm.configured?
|
9
|
+
warn '[Simple Form] Simple Form is not configured in the application and will use the default values.' +
|
10
|
+
' Use `rails generate simple_form:install` to generate the Simple Form configuration.'
|
11
|
+
end
|
12
|
+
end
|
6
13
|
end
|
7
14
|
end
|
data/lib/simple_form/tags.rb
CHANGED
@@ -15,6 +15,13 @@ module SimpleForm
|
|
15
15
|
|
16
16
|
rendered_item = yield item, value, text, default_html_options.merge(additional_html_options)
|
17
17
|
|
18
|
+
if @options.fetch(:boolean_style, SimpleForm.boolean_style) == :nested
|
19
|
+
label_options = {}
|
20
|
+
add_default_name_and_id_for_value(value, label_options)
|
21
|
+
label_options['for'] = label_options.delete('id')
|
22
|
+
rendered_item = content_tag(:label, rendered_item, label_options)
|
23
|
+
end
|
24
|
+
|
18
25
|
item_wrapper_tag ? @template_object.content_tag(item_wrapper_tag, rendered_item, class: item_wrapper_class) : rendered_item
|
19
26
|
end.join.html_safe
|
20
27
|
end
|