simple_form 3.0.1 → 3.1.0.rc1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -27
- 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/action_view_extensions/form_helper.rb +1 -1
- data/lib/simple_form/components/errors.rb +30 -2
- 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 -57
- data/lib/simple_form/helpers.rb +5 -5
- data/lib/simple_form/inputs/base.rb +33 -11
- 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/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/lib/simple_form/wrappers.rb +1 -0
- data/lib/simple_form.rb +38 -6
- 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 +61 -0
- data/test/form_builder/input_field_test.rb +25 -1
- 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 +49 -5
- data/test/support/models.rb +49 -24
- data/test/test_helper.rb +2 -0
- metadata +23 -34
|
@@ -1,45 +1,106 @@
|
|
|
1
1
|
# Use this setup block to configure all options available in SimpleForm.
|
|
2
2
|
SimpleForm.setup do |config|
|
|
3
|
-
config.
|
|
3
|
+
config.button_class = 'btn btn-default'
|
|
4
|
+
config.boolean_label_class = nil
|
|
5
|
+
|
|
6
|
+
config.wrappers :vertical_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
|
|
4
7
|
b.use :html5
|
|
5
8
|
b.use :placeholder
|
|
6
|
-
b.use :label
|
|
7
|
-
|
|
9
|
+
b.use :label, class: 'control-label'
|
|
10
|
+
|
|
11
|
+
b.wrapper tag: 'div' do |ba|
|
|
12
|
+
ba.use :input, class: 'form-control'
|
|
13
|
+
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
|
|
14
|
+
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
config.wrappers :vertical_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
|
|
19
|
+
b.use :html5
|
|
20
|
+
b.use :placeholder
|
|
21
|
+
b.use :label, class: 'control-label'
|
|
22
|
+
|
|
23
|
+
b.wrapper tag: 'div' do |ba|
|
|
8
24
|
ba.use :input
|
|
9
|
-
ba.use :error, wrap_with: { tag: 'span', class: 'help-
|
|
25
|
+
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
|
|
10
26
|
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
|
|
11
27
|
end
|
|
12
28
|
end
|
|
13
29
|
|
|
14
|
-
config.wrappers :
|
|
30
|
+
config.wrappers :vertical_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
|
|
15
31
|
b.use :html5
|
|
16
32
|
b.use :placeholder
|
|
17
|
-
|
|
18
|
-
b.wrapper tag: 'div', class: '
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
|
|
34
|
+
b.wrapper tag: 'div', class: 'checkbox' do |ba|
|
|
35
|
+
ba.use :label_input
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
|
|
39
|
+
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
config.wrappers :vertical_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
|
|
43
|
+
b.use :html5
|
|
44
|
+
b.use :placeholder
|
|
45
|
+
b.use :label_input
|
|
46
|
+
b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
|
|
47
|
+
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
config.wrappers :horizontal_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
|
|
51
|
+
b.use :html5
|
|
52
|
+
b.use :placeholder
|
|
53
|
+
b.use :label, class: 'col-sm-3 control-label'
|
|
54
|
+
|
|
55
|
+
b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
|
|
56
|
+
ba.use :input, class: 'form-control'
|
|
57
|
+
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
|
|
58
|
+
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
|
|
24
59
|
end
|
|
25
60
|
end
|
|
26
61
|
|
|
27
|
-
config.wrappers :
|
|
62
|
+
config.wrappers :horizontal_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
|
|
28
63
|
b.use :html5
|
|
29
64
|
b.use :placeholder
|
|
30
|
-
b.use :label
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
65
|
+
b.use :label, class: 'col-sm-3 control-label'
|
|
66
|
+
|
|
67
|
+
b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
|
|
68
|
+
ba.use :input
|
|
69
|
+
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
|
|
70
|
+
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
|
|
75
|
+
b.use :html5
|
|
76
|
+
b.use :placeholder
|
|
77
|
+
|
|
78
|
+
b.wrapper tag: 'div', class: 'col-sm-offset-3 col-sm-9' do |wr|
|
|
79
|
+
wr.wrapper tag: 'div', class: 'checkbox' do |ba|
|
|
80
|
+
ba.use :label_input, class: 'col-sm-9'
|
|
34
81
|
end
|
|
35
|
-
|
|
36
|
-
|
|
82
|
+
|
|
83
|
+
wr.use :error, wrap_with: { tag: 'span', class: 'help-block' }
|
|
84
|
+
wr.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
config.wrappers :horizontal_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
|
|
89
|
+
b.use :html5
|
|
90
|
+
b.use :placeholder
|
|
91
|
+
|
|
92
|
+
b.use :label, class: 'col-sm-3 control-label'
|
|
93
|
+
|
|
94
|
+
b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
|
|
95
|
+
ba.use :input
|
|
96
|
+
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
|
|
97
|
+
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
|
|
37
98
|
end
|
|
38
99
|
end
|
|
39
100
|
|
|
40
|
-
# Wrappers for forms and inputs using the
|
|
41
|
-
# Check the Bootstrap docs (http://
|
|
101
|
+
# Wrappers for forms and inputs using the Bootstrap toolkit.
|
|
102
|
+
# Check the Bootstrap docs (http://getbootstrap.com)
|
|
42
103
|
# to learn about the different styles for forms and inputs,
|
|
43
104
|
# buttons and other elements.
|
|
44
|
-
config.default_wrapper = :
|
|
105
|
+
config.default_wrapper = :vertical_form
|
|
45
106
|
end
|
|
@@ -8,7 +8,7 @@ SimpleForm.setup do |config|
|
|
|
8
8
|
b.optional :min_max
|
|
9
9
|
b.optional :readonly
|
|
10
10
|
b.use :label_input
|
|
11
|
-
b.use :error, wrap_with: { tag: :small }
|
|
11
|
+
b.use :error, wrap_with: { tag: :small, class: :error }
|
|
12
12
|
|
|
13
13
|
# Uncomment the following line to enable hints. The line is commented out by default since Foundation
|
|
14
14
|
# does't provide styles for hints. You will need to provide your own CSS styles for hints.
|
|
@@ -10,7 +10,7 @@ en:
|
|
|
10
10
|
# html: '<abbr title="required">*</abbr>'
|
|
11
11
|
error_notification:
|
|
12
12
|
default_message: "Please review the problems below:"
|
|
13
|
-
#
|
|
13
|
+
# Examples
|
|
14
14
|
# labels:
|
|
15
15
|
# defaults:
|
|
16
16
|
# password: 'Password'
|
|
@@ -23,4 +23,9 @@ en:
|
|
|
23
23
|
# defaults:
|
|
24
24
|
# username: 'User name to sign in.'
|
|
25
25
|
# password: 'No special characters, please.'
|
|
26
|
-
|
|
26
|
+
# include_blanks:
|
|
27
|
+
# defaults:
|
|
28
|
+
# age: 'Rather not say'
|
|
29
|
+
# prompts:
|
|
30
|
+
# defaults:
|
|
31
|
+
# age: 'Select your age'
|
|
@@ -10,7 +10,7 @@ module SimpleForm
|
|
|
10
10
|
#
|
|
11
11
|
module FormHelper
|
|
12
12
|
|
|
13
|
-
def simple_form_for(record, options={}, &block)
|
|
13
|
+
def simple_form_for(record, options = {}, &block)
|
|
14
14
|
options[:builder] ||= SimpleForm::FormBuilder
|
|
15
15
|
options[:html] ||= {}
|
|
16
16
|
unless options[:html].key?(:novalidate)
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
module SimpleForm
|
|
2
2
|
module Components
|
|
3
3
|
module Errors
|
|
4
|
-
def error
|
|
4
|
+
def error(wrapper_options = nil)
|
|
5
5
|
error_text if has_errors?
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
+
def full_error(wrapper_options = nil)
|
|
9
|
+
full_error_text if options[:error] != false && has_errors?
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
def has_errors?
|
|
9
13
|
object && object.respond_to?(:errors) && errors.present?
|
|
10
14
|
end
|
|
@@ -12,7 +16,15 @@ module SimpleForm
|
|
|
12
16
|
protected
|
|
13
17
|
|
|
14
18
|
def error_text
|
|
15
|
-
|
|
19
|
+
text = has_custom_error? ? options[:error] : errors.send(error_method)
|
|
20
|
+
|
|
21
|
+
"#{html_escape(options[:error_prefix])} #{text}".lstrip.html_safe
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def full_error_text
|
|
25
|
+
text = has_custom_error? ? options[:error] : full_errors.send(error_method)
|
|
26
|
+
|
|
27
|
+
text.html_safe
|
|
16
28
|
end
|
|
17
29
|
|
|
18
30
|
def error_method
|
|
@@ -23,13 +35,29 @@ module SimpleForm
|
|
|
23
35
|
@errors ||= (errors_on_attribute + errors_on_association).compact
|
|
24
36
|
end
|
|
25
37
|
|
|
38
|
+
def full_errors
|
|
39
|
+
@full_errors ||= (full_errors_on_attribute + full_errors_on_association).compact
|
|
40
|
+
end
|
|
41
|
+
|
|
26
42
|
def errors_on_attribute
|
|
27
43
|
object.errors[attribute_name]
|
|
28
44
|
end
|
|
29
45
|
|
|
46
|
+
def full_errors_on_attribute
|
|
47
|
+
object.errors.full_messages_for(attribute_name)
|
|
48
|
+
end
|
|
49
|
+
|
|
30
50
|
def errors_on_association
|
|
31
51
|
reflection ? object.errors[reflection.name] : []
|
|
32
52
|
end
|
|
53
|
+
|
|
54
|
+
def full_errors_on_association
|
|
55
|
+
reflection ? object.full_messages_for(reflection.name) : []
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def has_custom_error?
|
|
59
|
+
options[:error].is_a?(String)
|
|
60
|
+
end
|
|
33
61
|
end
|
|
34
62
|
end
|
|
35
63
|
end
|
|
@@ -2,14 +2,14 @@ module SimpleForm
|
|
|
2
2
|
module Components
|
|
3
3
|
# Needs to be enabled in order to do automatic lookups.
|
|
4
4
|
module Hints
|
|
5
|
-
def hint
|
|
5
|
+
def hint(wrapper_options = nil)
|
|
6
6
|
@hint ||= begin
|
|
7
7
|
hint = options[:hint]
|
|
8
8
|
|
|
9
9
|
if hint.is_a?(String)
|
|
10
10
|
html_escape(hint)
|
|
11
11
|
else
|
|
12
|
-
content =
|
|
12
|
+
content = translate_from_namespace(:hints)
|
|
13
13
|
content.html_safe if content
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -7,8 +7,26 @@ module SimpleForm
|
|
|
7
7
|
include SimpleForm::Components::Labels
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def label_input
|
|
11
|
-
options[:label] == false
|
|
10
|
+
def label_input(wrapper_options = nil)
|
|
11
|
+
if options[:label] == false
|
|
12
|
+
deprecated_component(:input, wrapper_options)
|
|
13
|
+
else
|
|
14
|
+
deprecated_component(:label, wrapper_options) + deprecated_component(:input, wrapper_options)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def deprecated_component(namespace, wrapper_options)
|
|
21
|
+
method = method(namespace)
|
|
22
|
+
|
|
23
|
+
if method.arity == 0
|
|
24
|
+
ActiveSupport::Deprecation.warn(SimpleForm::CUSTOM_INPUT_DEPRECATION_WARN % { name: namespace })
|
|
25
|
+
|
|
26
|
+
method.call
|
|
27
|
+
else
|
|
28
|
+
method.call(wrapper_options)
|
|
29
|
+
end
|
|
12
30
|
end
|
|
13
31
|
end
|
|
14
32
|
end
|
|
@@ -21,16 +21,19 @@ module SimpleForm
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def label
|
|
24
|
+
def label(wrapper_options = nil)
|
|
25
|
+
label_options = merge_wrapper_options(label_html_options, wrapper_options)
|
|
26
|
+
|
|
25
27
|
if generate_label_for_attribute?
|
|
26
|
-
@builder.label(label_target, label_text,
|
|
28
|
+
@builder.label(label_target, label_text, label_options)
|
|
27
29
|
else
|
|
28
|
-
template.label_tag(nil, label_text,
|
|
30
|
+
template.label_tag(nil, label_text, label_options)
|
|
29
31
|
end
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def label_text
|
|
33
|
-
SimpleForm.label_text
|
|
35
|
+
label_text = options[:label_text] || SimpleForm.label_text
|
|
36
|
+
label_text.call(html_escape(raw_label_text), required_label_text, options[:label].present?).strip.html_safe
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
def label_target
|
|
@@ -46,6 +49,7 @@ module SimpleForm
|
|
|
46
49
|
if options.key?(:input_html) && options[:input_html].key?(:id)
|
|
47
50
|
label_options[:for] = options[:input_html][:id]
|
|
48
51
|
end
|
|
52
|
+
|
|
49
53
|
label_options
|
|
50
54
|
end
|
|
51
55
|
|
|
@@ -62,7 +66,7 @@ module SimpleForm
|
|
|
62
66
|
|
|
63
67
|
# First check labels translation and then human attribute name.
|
|
64
68
|
def label_translation #:nodoc:
|
|
65
|
-
if SimpleForm.translate_labels && (translated_label =
|
|
69
|
+
if SimpleForm.translate_labels && (translated_label = translate_from_namespace(:labels))
|
|
66
70
|
translated_label
|
|
67
71
|
elsif object.class.respond_to?(:human_attribute_name)
|
|
68
72
|
object.class.human_attribute_name(reflection_or_attribute_name.to_s)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module SimpleForm
|
|
2
2
|
module Components
|
|
3
3
|
module MinMax
|
|
4
|
-
def min_max
|
|
4
|
+
def min_max(wrapper_options = nil)
|
|
5
5
|
if numeric_validator = find_numericality_validator
|
|
6
6
|
validator_options = numeric_validator.options
|
|
7
7
|
input_html_options[:min] ||= minimum_value(validator_options)
|
|
@@ -2,14 +2,14 @@ module SimpleForm
|
|
|
2
2
|
module Components
|
|
3
3
|
# Needs to be enabled in order to do automatic lookups.
|
|
4
4
|
module Placeholders
|
|
5
|
-
def placeholder
|
|
5
|
+
def placeholder(wrapper_options = nil)
|
|
6
6
|
input_html_options[:placeholder] ||= placeholder_text
|
|
7
7
|
nil
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def placeholder_text
|
|
11
11
|
placeholder = options[:placeholder]
|
|
12
|
-
placeholder.is_a?(String) ? placeholder :
|
|
12
|
+
placeholder.is_a?(String) ? placeholder : translate_from_namespace(:placeholders)
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -2,7 +2,7 @@ module SimpleForm
|
|
|
2
2
|
module Components
|
|
3
3
|
# Needs to be enabled in order to do automatic lookups.
|
|
4
4
|
module Readonly
|
|
5
|
-
def readonly
|
|
5
|
+
def readonly(wrapper_options = nil)
|
|
6
6
|
if readonly_attribute? && !has_readonly?
|
|
7
7
|
input_html_options[:readonly] ||= true
|
|
8
8
|
input_html_classes << :readonly
|
|
@@ -8,8 +8,8 @@ module SimpleForm
|
|
|
8
8
|
|
|
9
9
|
# When action is create or update, we still should use new and edit
|
|
10
10
|
ACTIONS = {
|
|
11
|
-
create
|
|
12
|
-
update
|
|
11
|
+
'create' => 'new',
|
|
12
|
+
'update' => 'edit'
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
ATTRIBUTE_COMPONENTS = [:html5, :min_max, :maxlength, :placeholder, :pattern, :readonly]
|
|
@@ -106,18 +106,13 @@ module SimpleForm
|
|
|
106
106
|
# Some inputs, as :time_zone and :country accepts a :priority option. If none is
|
|
107
107
|
# given SimpleForm.time_zone_priority and SimpleForm.country_priority are used respectively.
|
|
108
108
|
#
|
|
109
|
-
def input(attribute_name, options={}, &block)
|
|
109
|
+
def input(attribute_name, options = {}, &block)
|
|
110
110
|
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
|
111
|
-
input = find_input(attribute_name, options, &block)
|
|
112
111
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
|
|
116
|
-
else
|
|
117
|
-
wrapper
|
|
118
|
-
end
|
|
112
|
+
input = find_input(attribute_name, options, &block)
|
|
113
|
+
wrapper = find_wrapper(input.input_type, options)
|
|
119
114
|
|
|
120
|
-
|
|
115
|
+
wrapper.render input
|
|
121
116
|
end
|
|
122
117
|
alias :attribute :input
|
|
123
118
|
|
|
@@ -135,12 +130,17 @@ module SimpleForm
|
|
|
135
130
|
# <input class="string required" id="user_name" maxlength="100"
|
|
136
131
|
# name="user[name]" type="text" value="Carlos" />
|
|
137
132
|
#
|
|
138
|
-
def input_field(attribute_name, options={})
|
|
133
|
+
def input_field(attribute_name, options = {})
|
|
139
134
|
options = options.dup
|
|
140
|
-
options[:input_html] = options.except(:as, :collection, :label_method, :value_method, *ATTRIBUTE_COMPONENTS)
|
|
135
|
+
options[:input_html] = options.except(:as, :boolean_style, :collection, :label_method, :value_method, *ATTRIBUTE_COMPONENTS)
|
|
141
136
|
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
|
142
137
|
|
|
143
|
-
|
|
138
|
+
input = find_input(attribute_name, options)
|
|
139
|
+
wrapper = find_wrapper(input.input_type, options)
|
|
140
|
+
components = (wrapper.components.map(&:namespace) & ATTRIBUTE_COMPONENTS) + [:input]
|
|
141
|
+
components = components.map { |component| SimpleForm::Wrappers::Leaf.new(component) }
|
|
142
|
+
|
|
143
|
+
SimpleForm::Wrappers::Root.new(components, wrapper.options.merge(wrapper: false)).render input
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
# Helper for dealing with association selects/radios, generating the
|
|
@@ -171,7 +171,7 @@ module SimpleForm
|
|
|
171
171
|
#
|
|
172
172
|
# Please note that the association helper is currently only tested with Active Record. Depending on the ORM you are using your mileage may vary.
|
|
173
173
|
#
|
|
174
|
-
def association(association, options={}, &block)
|
|
174
|
+
def association(association, options = {}, &block)
|
|
175
175
|
options = options.dup
|
|
176
176
|
|
|
177
177
|
return simple_fields_for(*[association,
|
|
@@ -183,31 +183,9 @@ module SimpleForm
|
|
|
183
183
|
raise "Association #{association.inspect} not found" unless reflection
|
|
184
184
|
|
|
185
185
|
options[:as] ||= :select
|
|
186
|
-
options[:collection] ||= options
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
reflection.klass.where(conditions).order(reflection.options[:order])
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
attribute = case reflection.macro
|
|
193
|
-
when :belongs_to
|
|
194
|
-
(reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
|
|
195
|
-
when :has_one
|
|
196
|
-
raise ArgumentError, ":has_one associations are not supported by f.association"
|
|
197
|
-
else
|
|
198
|
-
if options[:as] == :select
|
|
199
|
-
html_options = options[:input_html] ||= {}
|
|
200
|
-
html_options[:multiple] = true unless html_options.key?(:multiple)
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# Force the association to be preloaded for performance.
|
|
204
|
-
if options[:preload] != false && object.respond_to?(association)
|
|
205
|
-
target = object.send(association)
|
|
206
|
-
target.to_a if target.respond_to?(:to_a)
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
:"#{reflection.name.to_s.singularize}_ids"
|
|
210
|
-
end
|
|
186
|
+
options[:collection] ||= fetch_association_collection(reflection, options)
|
|
187
|
+
|
|
188
|
+
attribute = build_association_attribute(reflection, association, options)
|
|
211
189
|
|
|
212
190
|
input(attribute, options.merge(reflection: reflection))
|
|
213
191
|
end
|
|
@@ -242,7 +220,7 @@ module SimpleForm
|
|
|
242
220
|
# f.error :name
|
|
243
221
|
# f.error :name, id: "cool_error"
|
|
244
222
|
#
|
|
245
|
-
def error(attribute_name, options={})
|
|
223
|
+
def error(attribute_name, options = {})
|
|
246
224
|
options = options.dup
|
|
247
225
|
|
|
248
226
|
options[:error_html] = options.except(:error_tag, :error_prefix, :error_method)
|
|
@@ -259,7 +237,7 @@ module SimpleForm
|
|
|
259
237
|
#
|
|
260
238
|
# f.full_error :token #=> <span class="error">Token is invalid</span>
|
|
261
239
|
#
|
|
262
|
-
def full_error(attribute_name, options={})
|
|
240
|
+
def full_error(attribute_name, options = {})
|
|
263
241
|
options = options.dup
|
|
264
242
|
|
|
265
243
|
options[:error_prefix] ||= if object.class.respond_to?(:human_attribute_name)
|
|
@@ -281,7 +259,7 @@ module SimpleForm
|
|
|
281
259
|
# f.hint :name, id: "cool_hint"
|
|
282
260
|
# f.hint "Don't forget to accept this"
|
|
283
261
|
#
|
|
284
|
-
def hint(attribute_name, options={})
|
|
262
|
+
def hint(attribute_name, options = {})
|
|
285
263
|
options = options.dup
|
|
286
264
|
|
|
287
265
|
options[:hint_html] = options.except(:hint_tag, :hint)
|
|
@@ -332,7 +310,7 @@ module SimpleForm
|
|
|
332
310
|
# f.error_notification message: 'Something went wrong'
|
|
333
311
|
# f.error_notification id: 'user_error_message', class: 'form_error'
|
|
334
312
|
#
|
|
335
|
-
def error_notification(options={})
|
|
313
|
+
def error_notification(options = {})
|
|
336
314
|
SimpleForm::ErrorNotification.new(self, options).render
|
|
337
315
|
end
|
|
338
316
|
|
|
@@ -464,15 +442,56 @@ module SimpleForm
|
|
|
464
442
|
@lookup_action ||= begin
|
|
465
443
|
action = template.controller && template.controller.action_name
|
|
466
444
|
return unless action
|
|
467
|
-
action = action.
|
|
445
|
+
action = action.to_s
|
|
468
446
|
ACTIONS[action] || action
|
|
469
447
|
end
|
|
470
448
|
end
|
|
471
449
|
|
|
472
450
|
private
|
|
473
451
|
|
|
452
|
+
def fetch_association_collection(reflection, options)
|
|
453
|
+
options.fetch(:collection) do
|
|
454
|
+
relation = reflection.klass.all
|
|
455
|
+
|
|
456
|
+
if reflection.respond_to?(:scope) && reflection.scope
|
|
457
|
+
relation = reflection.klass.instance_exec(&reflection.scope)
|
|
458
|
+
else
|
|
459
|
+
order = reflection.options[:order]
|
|
460
|
+
conditions = reflection.options[:conditions]
|
|
461
|
+
conditions = object.instance_exec(&conditions) if conditions.respond_to?(:call)
|
|
462
|
+
|
|
463
|
+
relation = relation.where(conditions)
|
|
464
|
+
relation = relation.order(order) if relation.respond_to?(:order)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
relation
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def build_association_attribute(reflection, association, options)
|
|
472
|
+
case reflection.macro
|
|
473
|
+
when :belongs_to
|
|
474
|
+
(reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
|
|
475
|
+
when :has_one
|
|
476
|
+
raise ArgumentError, ":has_one associations are not supported by f.association"
|
|
477
|
+
else
|
|
478
|
+
if options[:as] == :select
|
|
479
|
+
html_options = options[:input_html] ||= {}
|
|
480
|
+
html_options[:multiple] = true unless html_options.key?(:multiple)
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# Force the association to be preloaded for performance.
|
|
484
|
+
if options[:preload] != false && object.respond_to?(association)
|
|
485
|
+
target = object.send(association)
|
|
486
|
+
target.to_a if target.respond_to?(:to_a)
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
:"#{reflection.name.to_s.singularize}_ids"
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
|
|
474
493
|
# Find an input based on the attribute name.
|
|
475
|
-
def find_input(attribute_name, options={}, &block)
|
|
494
|
+
def find_input(attribute_name, options = {}, &block)
|
|
476
495
|
column = find_attribute_column(attribute_name)
|
|
477
496
|
input_type = default_input_type(attribute_name, column, options)
|
|
478
497
|
|
|
@@ -486,7 +505,7 @@ module SimpleForm
|
|
|
486
505
|
# Attempt to guess the better input type given the defined options. By
|
|
487
506
|
# default alwayls fallback to the user :as option, or to a :select when a
|
|
488
507
|
# collection is given.
|
|
489
|
-
def default_input_type(attribute_name, column, options)
|
|
508
|
+
def default_input_type(attribute_name, column, options)
|
|
490
509
|
return options[:as].to_sym if options[:as]
|
|
491
510
|
return :select if options[:collection]
|
|
492
511
|
custom_type = find_custom_type(attribute_name.to_s) and return custom_type
|
|
@@ -511,24 +530,24 @@ module SimpleForm
|
|
|
511
530
|
end
|
|
512
531
|
end
|
|
513
532
|
|
|
514
|
-
def find_custom_type(attribute_name)
|
|
533
|
+
def find_custom_type(attribute_name)
|
|
515
534
|
SimpleForm.input_mappings.find { |match, type|
|
|
516
535
|
attribute_name =~ match
|
|
517
536
|
}.try(:last) if SimpleForm.input_mappings
|
|
518
537
|
end
|
|
519
538
|
|
|
520
|
-
def file_method?(attribute_name)
|
|
539
|
+
def file_method?(attribute_name)
|
|
521
540
|
file = @object.send(attribute_name) if @object.respond_to?(attribute_name)
|
|
522
541
|
file && SimpleForm.file_methods.any? { |m| file.respond_to?(m) }
|
|
523
542
|
end
|
|
524
543
|
|
|
525
|
-
def find_attribute_column(attribute_name)
|
|
544
|
+
def find_attribute_column(attribute_name)
|
|
526
545
|
if @object.respond_to?(:column_for_attribute)
|
|
527
546
|
@object.column_for_attribute(attribute_name)
|
|
528
547
|
end
|
|
529
548
|
end
|
|
530
549
|
|
|
531
|
-
def find_association_reflection(association)
|
|
550
|
+
def find_association_reflection(association)
|
|
532
551
|
if @object.class.respond_to?(:reflect_on_association)
|
|
533
552
|
@object.class.reflect_on_association(association)
|
|
534
553
|
end
|
|
@@ -541,7 +560,7 @@ module SimpleForm
|
|
|
541
560
|
# b) Or use the found mapping
|
|
542
561
|
# 2) If not, fallbacks to #{input_type}Input
|
|
543
562
|
# 3) If not, fallbacks to SimpleForm::Inputs::#{input_type}Input
|
|
544
|
-
def find_mapping(input_type)
|
|
563
|
+
def find_mapping(input_type)
|
|
545
564
|
discovery_cache[input_type] ||=
|
|
546
565
|
if mapping = self.class.mappings[input_type]
|
|
547
566
|
mapping_override(mapping) || mapping
|
|
@@ -552,13 +571,29 @@ module SimpleForm
|
|
|
552
571
|
end
|
|
553
572
|
end
|
|
554
573
|
|
|
555
|
-
|
|
556
|
-
|
|
574
|
+
# Attempts to find a wrapper mapping. It follows the following rules:
|
|
575
|
+
#
|
|
576
|
+
# 1) It tries to find a wrapper for the current form
|
|
577
|
+
# 2) If not, it tries to find a config
|
|
578
|
+
def find_wrapper_mapping(input_type)
|
|
579
|
+
if options[:wrapper_mappings] && options[:wrapper_mappings][input_type]
|
|
580
|
+
options[:wrapper_mappings][input_type]
|
|
581
|
+
else
|
|
582
|
+
SimpleForm.wrapper_mappings && SimpleForm.wrapper_mappings[input_type]
|
|
583
|
+
end
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def find_wrapper(input_type, options)
|
|
587
|
+
if name = options[:wrapper] || find_wrapper_mapping(input_type)
|
|
588
|
+
name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
|
|
589
|
+
else
|
|
590
|
+
wrapper
|
|
591
|
+
end
|
|
557
592
|
end
|
|
558
593
|
|
|
559
594
|
# If cache_discovery is enabled, use the class level cache that persists
|
|
560
595
|
# between requests, otherwise use the instance one.
|
|
561
|
-
def discovery_cache
|
|
596
|
+
def discovery_cache
|
|
562
597
|
if SimpleForm.cache_discovery
|
|
563
598
|
self.class.discovery_cache
|
|
564
599
|
else
|
|
@@ -566,14 +601,14 @@ module SimpleForm
|
|
|
566
601
|
end
|
|
567
602
|
end
|
|
568
603
|
|
|
569
|
-
def mapping_override(klass)
|
|
604
|
+
def mapping_override(klass)
|
|
570
605
|
name = klass.name
|
|
571
606
|
if name =~ /^SimpleForm::Inputs/
|
|
572
607
|
attempt_mapping name.split("::").last, Object
|
|
573
608
|
end
|
|
574
609
|
end
|
|
575
610
|
|
|
576
|
-
def attempt_mapping(mapping, at)
|
|
611
|
+
def attempt_mapping(mapping, at)
|
|
577
612
|
return if SimpleForm.inputs_discovery == false && at == Object
|
|
578
613
|
|
|
579
614
|
begin
|