simple_form_with_client_validation 0.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.
- data/CHANGELOG.md +6 -0
- data/MIT-LICENSE +20 -0
- data/README.md +783 -0
- data/lib/generators/simple_form_with_client_validation/USAGE +3 -0
- data/lib/generators/simple_form_with_client_validation/install_generator.rb +32 -0
- data/lib/generators/simple_form_with_client_validation/templates/README +12 -0
- data/lib/generators/simple_form_with_client_validation/templates/_form.html.erb +13 -0
- data/lib/generators/simple_form_with_client_validation/templates/_form.html.haml +10 -0
- data/lib/generators/simple_form_with_client_validation/templates/_form.html.slim +10 -0
- data/lib/generators/simple_form_with_client_validation/templates/config/initializers/simple_form.rb.tt +179 -0
- data/lib/generators/simple_form_with_client_validation/templates/config/locales/simple_form.en.yml +26 -0
- data/lib/simple_form_with_client_validation/action_view_extensions/builder.rb +331 -0
- data/lib/simple_form_with_client_validation/action_view_extensions/form_helper.rb +74 -0
- data/lib/simple_form_with_client_validation/components/errors.rb +35 -0
- data/lib/simple_form_with_client_validation/components/hints.rb +18 -0
- data/lib/simple_form_with_client_validation/components/html5.rb +26 -0
- data/lib/simple_form_with_client_validation/components/label_input.rb +15 -0
- data/lib/simple_form_with_client_validation/components/labels.rb +79 -0
- data/lib/simple_form_with_client_validation/components/maxlength.rb +41 -0
- data/lib/simple_form_with_client_validation/components/min_max.rb +50 -0
- data/lib/simple_form_with_client_validation/components/minlength.rb +41 -0
- data/lib/simple_form_with_client_validation/components/pattern.rb +34 -0
- data/lib/simple_form_with_client_validation/components/placeholders.rb +16 -0
- data/lib/simple_form_with_client_validation/components/readonly.rb +22 -0
- data/lib/simple_form_with_client_validation/components.rb +21 -0
- data/lib/simple_form_with_client_validation/core_ext/hash.rb +16 -0
- data/lib/simple_form_with_client_validation/error_notification.rb +48 -0
- data/lib/simple_form_with_client_validation/form_builder.rb +484 -0
- data/lib/simple_form_with_client_validation/helpers/autofocus.rb +11 -0
- data/lib/simple_form_with_client_validation/helpers/disabled.rb +15 -0
- data/lib/simple_form_with_client_validation/helpers/readonly.rb +15 -0
- data/lib/simple_form_with_client_validation/helpers/required.rb +35 -0
- data/lib/simple_form_with_client_validation/helpers/validators.rb +44 -0
- data/lib/simple_form_with_client_validation/helpers.rb +12 -0
- data/lib/simple_form_with_client_validation/i18n_cache.rb +22 -0
- data/lib/simple_form_with_client_validation/inputs/base.rb +162 -0
- data/lib/simple_form_with_client_validation/inputs/block_input.rb +14 -0
- data/lib/simple_form_with_client_validation/inputs/boolean_input.rb +64 -0
- data/lib/simple_form_with_client_validation/inputs/collection_check_boxes_input.rb +21 -0
- data/lib/simple_form_with_client_validation/inputs/collection_input.rb +101 -0
- data/lib/simple_form_with_client_validation/inputs/collection_radio_buttons_input.rb +63 -0
- data/lib/simple_form_with_client_validation/inputs/collection_select_input.rb +14 -0
- data/lib/simple_form_with_client_validation/inputs/date_time_input.rb +28 -0
- data/lib/simple_form_with_client_validation/inputs/file_input.rb +9 -0
- data/lib/simple_form_with_client_validation/inputs/grouped_collection_select_input.rb +41 -0
- data/lib/simple_form_with_client_validation/inputs/hidden_input.rb +17 -0
- data/lib/simple_form_with_client_validation/inputs/numeric_input.rb +24 -0
- data/lib/simple_form_with_client_validation/inputs/password_input.rb +12 -0
- data/lib/simple_form_with_client_validation/inputs/priority_input.rb +24 -0
- data/lib/simple_form_with_client_validation/inputs/range_input.rb +14 -0
- data/lib/simple_form_with_client_validation/inputs/string_input.rb +23 -0
- data/lib/simple_form_with_client_validation/inputs/text_input.rb +11 -0
- data/lib/simple_form_with_client_validation/inputs.rb +21 -0
- data/lib/simple_form_with_client_validation/map_type.rb +16 -0
- data/lib/simple_form_with_client_validation/version.rb +3 -0
- data/lib/simple_form_with_client_validation/wrappers/builder.rb +115 -0
- data/lib/simple_form_with_client_validation/wrappers/many.rb +78 -0
- data/lib/simple_form_with_client_validation/wrappers/root.rb +34 -0
- data/lib/simple_form_with_client_validation/wrappers/single.rb +18 -0
- data/lib/simple_form_with_client_validation/wrappers.rb +8 -0
- data/lib/simple_form_with_client_validation.rb +218 -0
- data/test/action_view_extensions/builder_test.rb +577 -0
- data/test/action_view_extensions/form_helper_test.rb +104 -0
- data/test/components/label_test.rb +310 -0
- data/test/form_builder/association_test.rb +177 -0
- data/test/form_builder/button_test.rb +47 -0
- data/test/form_builder/error_notification_test.rb +79 -0
- data/test/form_builder/error_test.rb +121 -0
- data/test/form_builder/general_test.rb +356 -0
- data/test/form_builder/hint_test.rb +139 -0
- data/test/form_builder/input_field_test.rb +63 -0
- data/test/form_builder/label_test.rb +71 -0
- data/test/form_builder/wrapper_test.rb +149 -0
- data/test/generators/simple_form_generator_test.rb +32 -0
- data/test/inputs/boolean_input_test.rb +108 -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 +99 -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 +118 -0
- data/test/inputs/hidden_input_test.rb +30 -0
- data/test/inputs/numeric_input_test.rb +173 -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 +29 -0
- data/test/simple_form_test.rb +9 -0
- data/test/support/discovery_inputs.rb +21 -0
- data/test/support/misc_helpers.rb +102 -0
- data/test/support/mock_controller.rb +24 -0
- data/test/support/mock_response.rb +14 -0
- data/test/support/models.rb +210 -0
- data/test/test_helper.rb +95 -0
- metadata +227 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
module SimpleFormWithClientValidation
|
2
|
+
module Helpers
|
3
|
+
module Required
|
4
|
+
private
|
5
|
+
|
6
|
+
def required_field?
|
7
|
+
@required
|
8
|
+
end
|
9
|
+
|
10
|
+
def calculate_required
|
11
|
+
if !options[:required].nil?
|
12
|
+
options[:required]
|
13
|
+
elsif has_validators?
|
14
|
+
required_by_validators?
|
15
|
+
else
|
16
|
+
required_by_default?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def required_by_validators?
|
21
|
+
(attribute_validators + reflection_validators).any? { |v| v.kind == :presence && valid_validator?(v) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def required_by_default?
|
25
|
+
SimpleFormWithClientValidation.required_by_default
|
26
|
+
end
|
27
|
+
|
28
|
+
# Do not use has_required? because we want to add the class
|
29
|
+
# regardless of the required option.
|
30
|
+
def required_class
|
31
|
+
required_field? ? :required : :optional
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module SimpleFormWithClientValidation
|
2
|
+
module Helpers
|
3
|
+
module Validators
|
4
|
+
def has_validators?
|
5
|
+
@has_validators ||= attribute_name && object.class.respond_to?(:validators_on)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def attribute_validators
|
11
|
+
object.class.validators_on(attribute_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def reflection_validators
|
15
|
+
reflection ? object.class.validators_on(reflection.name) : []
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid_validator?(validator)
|
19
|
+
!conditional_validators?(validator) && action_validator_match?(validator)
|
20
|
+
end
|
21
|
+
|
22
|
+
def conditional_validators?(validator)
|
23
|
+
validator.options.include?(:if) || validator.options.include?(:unless)
|
24
|
+
end
|
25
|
+
|
26
|
+
def action_validator_match?(validator)
|
27
|
+
return true if !validator.options.include?(:on)
|
28
|
+
|
29
|
+
case validator.options[:on]
|
30
|
+
when :save
|
31
|
+
true
|
32
|
+
when :create
|
33
|
+
!object.persisted?
|
34
|
+
when :update
|
35
|
+
object.persisted?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_validator(validator)
|
40
|
+
attribute_validators.find { |v| validator === v } if has_validators?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module SimpleFormWithClientValidation
|
2
|
+
# Helpers are made of several helpers that cannot be turned on automatically.
|
3
|
+
# For instance, disabled cannot be turned on automatically, it requires the
|
4
|
+
# user to explicitly pass the option :disabled => true so it may work.
|
5
|
+
module Helpers
|
6
|
+
autoload :Autofocus, 'simple_form_with_client_validation/helpers/autofocus'
|
7
|
+
autoload :Disabled, 'simple_form_with_client_validation/helpers/disabled'
|
8
|
+
autoload :Readonly, 'simple_form_with_client_validation/helpers/readonly'
|
9
|
+
autoload :Required, 'simple_form_with_client_validation/helpers/required'
|
10
|
+
autoload :Validators, 'simple_form_with_client_validation/helpers/validators'
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SimpleFormWithClientValidation
|
2
|
+
# A lot of configuration values are retrived from I18n,
|
3
|
+
# like boolean collection, required string. This module provides
|
4
|
+
# caching facility to speed up form construction.
|
5
|
+
module I18nCache
|
6
|
+
def i18n_cache(key)
|
7
|
+
get_i18n_cache(key)[I18n.locale] ||= yield.freeze
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_i18n_cache(key)
|
11
|
+
if class_variable_defined?(:"@@#{key}")
|
12
|
+
class_variable_get(:"@@#{key}")
|
13
|
+
else
|
14
|
+
reset_i18n_cache(key)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def reset_i18n_cache(key)
|
19
|
+
class_variable_set(:"@@#{key}", {})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module SimpleFormWithClientValidation
|
2
|
+
module Inputs
|
3
|
+
class Base
|
4
|
+
extend I18nCache
|
5
|
+
|
6
|
+
include SimpleFormWithClientValidation::Helpers::Autofocus
|
7
|
+
include SimpleFormWithClientValidation::Helpers::Disabled
|
8
|
+
include SimpleFormWithClientValidation::Helpers::Readonly
|
9
|
+
include SimpleFormWithClientValidation::Helpers::Required
|
10
|
+
include SimpleFormWithClientValidation::Helpers::Validators
|
11
|
+
|
12
|
+
include SimpleFormWithClientValidation::Components::Errors
|
13
|
+
include SimpleFormWithClientValidation::Components::Hints
|
14
|
+
include SimpleFormWithClientValidation::Components::HTML5
|
15
|
+
include SimpleFormWithClientValidation::Components::LabelInput
|
16
|
+
include SimpleFormWithClientValidation::Components::Maxlength
|
17
|
+
include SimpleFormWithClientValidation::Components::Minlength
|
18
|
+
include SimpleFormWithClientValidation::Components::MinMax
|
19
|
+
include SimpleFormWithClientValidation::Components::Pattern
|
20
|
+
include SimpleFormWithClientValidation::Components::Placeholders
|
21
|
+
include SimpleFormWithClientValidation::Components::Readonly
|
22
|
+
|
23
|
+
attr_reader :attribute_name, :column, :input_type, :reflection,
|
24
|
+
:options, :input_html_options, :input_html_classes, :html_classes
|
25
|
+
|
26
|
+
delegate :template, :object, :object_name, :lookup_model_names, :lookup_action, :to => :@builder
|
27
|
+
|
28
|
+
class_attribute :default_options
|
29
|
+
self.default_options = {}
|
30
|
+
|
31
|
+
def self.enable(*keys)
|
32
|
+
options = self.default_options.dup
|
33
|
+
keys.each { |key| options.delete(key) }
|
34
|
+
self.default_options = options
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.disable(*keys)
|
38
|
+
options = self.default_options.dup
|
39
|
+
keys.each { |key| options[key] = false }
|
40
|
+
self.default_options = options
|
41
|
+
end
|
42
|
+
|
43
|
+
# Always enabled.
|
44
|
+
enable :hint
|
45
|
+
|
46
|
+
# Usually disabled, needs to be enabled explicitly passing true as option.
|
47
|
+
disable :maxlength, :placeholder, :pattern, :min_max
|
48
|
+
|
49
|
+
def initialize(builder, attribute_name, column, input_type, options = {})
|
50
|
+
super
|
51
|
+
|
52
|
+
@builder = builder
|
53
|
+
@attribute_name = attribute_name
|
54
|
+
@column = column
|
55
|
+
@input_type = input_type
|
56
|
+
@reflection = options.delete(:reflection)
|
57
|
+
@options = options.reverse_merge!(self.class.default_options)
|
58
|
+
@required = calculate_required
|
59
|
+
|
60
|
+
# Notice that html_options_for receives a reference to input_html_classes.
|
61
|
+
# This means that classes added dynamically to input_html_classes will
|
62
|
+
# still propagate to input_html_options.
|
63
|
+
@html_classes = SimpleFormWithClientValidation.additional_classes_for(:input) {
|
64
|
+
[input_type, required_class, readonly_class, disabled_class].compact
|
65
|
+
}
|
66
|
+
|
67
|
+
@input_html_classes = @html_classes.dup
|
68
|
+
@input_html_options = html_options_for(:input, input_html_classes).tap do |o|
|
69
|
+
o[:readonly] = true if has_readonly?
|
70
|
+
o[:disabled] = true if has_disabled?
|
71
|
+
o[:autofocus] = true if has_autofocus?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def input
|
76
|
+
raise NotImplementedError
|
77
|
+
end
|
78
|
+
|
79
|
+
def input_options
|
80
|
+
options
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def add_size!
|
86
|
+
input_html_options[:size] ||= [limit, SimpleFormWithClientValidation.default_input_size].compact.min
|
87
|
+
end
|
88
|
+
|
89
|
+
def limit
|
90
|
+
column && column.limit
|
91
|
+
end
|
92
|
+
|
93
|
+
def nested_boolean_style?
|
94
|
+
options.fetch(:boolean_style, SimpleFormWithClientValidation.boolean_style) == :nested
|
95
|
+
end
|
96
|
+
|
97
|
+
# Find reflection name when available, otherwise use attribute
|
98
|
+
def reflection_or_attribute_name
|
99
|
+
reflection ? reflection.name : attribute_name
|
100
|
+
end
|
101
|
+
|
102
|
+
# Retrieve options for the given namespace from the options hash
|
103
|
+
def html_options_for(namespace, css_classes)
|
104
|
+
html_options = options[:"#{namespace}_html"] || {}
|
105
|
+
css_classes << html_options[:class] if html_options.key?(:class)
|
106
|
+
html_options[:class] = css_classes
|
107
|
+
html_options
|
108
|
+
end
|
109
|
+
|
110
|
+
# Lookup translations for the given namespace using I18n, based on object name,
|
111
|
+
# actual action and attribute name. Lookup priority as follows:
|
112
|
+
#
|
113
|
+
# simple_form.{namespace}.{model}.{action}.{attribute}
|
114
|
+
# simple_form.{namespace}.{model}.{attribute}
|
115
|
+
# simple_form.{namespace}.defaults.{attribute}
|
116
|
+
#
|
117
|
+
# Namespace is used for :labels and :hints.
|
118
|
+
#
|
119
|
+
# Model is the actual object name, for a @user object you'll have :user.
|
120
|
+
# Action is the action being rendered, usually :new or :edit.
|
121
|
+
# And attribute is the attribute itself, :name for example.
|
122
|
+
#
|
123
|
+
# The lookup for nested attributes is also done in a nested format using
|
124
|
+
# both model and nested object names, such as follow:
|
125
|
+
#
|
126
|
+
# simple_form.{namespace}.{model}.{nested}.{action}.{attribute}
|
127
|
+
# simple_form.{namespace}.{model}.{nested}.{attribute}
|
128
|
+
# simple_form.{namespace}.{nested}.{action}.{attribute}
|
129
|
+
# simple_form.{namespace}.{nested}.{attribute}
|
130
|
+
# simple_form.{namespace}.defaults.{attribute}
|
131
|
+
#
|
132
|
+
# Example:
|
133
|
+
#
|
134
|
+
# simple_form:
|
135
|
+
# labels:
|
136
|
+
# user:
|
137
|
+
# new:
|
138
|
+
# email: 'E-mail para efetuar o sign in.'
|
139
|
+
# edit:
|
140
|
+
# email: 'E-mail.'
|
141
|
+
#
|
142
|
+
# Take a look at our locale example file.
|
143
|
+
def translate(namespace, default='')
|
144
|
+
model_names = lookup_model_names.dup
|
145
|
+
lookups = []
|
146
|
+
|
147
|
+
while !model_names.empty?
|
148
|
+
joined_model_names = model_names.join(".")
|
149
|
+
model_names.shift
|
150
|
+
|
151
|
+
lookups << :"#{joined_model_names}.#{lookup_action}.#{reflection_or_attribute_name}"
|
152
|
+
lookups << :"#{joined_model_names}.#{reflection_or_attribute_name}"
|
153
|
+
end
|
154
|
+
lookups << :"defaults.#{lookup_action}.#{reflection_or_attribute_name}"
|
155
|
+
lookups << :"defaults.#{attribute_name}"
|
156
|
+
lookups << default
|
157
|
+
|
158
|
+
I18n.t(lookups.shift, :scope => :"simple_form.#{namespace}", :default => lookups).presence
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module SimpleFormWithClientValidation
|
2
|
+
module Inputs
|
3
|
+
class BooleanInput < Base
|
4
|
+
def input
|
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 + options[:inline_label]
|
9
|
+
}
|
10
|
+
else
|
11
|
+
build_check_box
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def label_input
|
16
|
+
if options[:label] == false
|
17
|
+
input
|
18
|
+
elsif nested_boolean_style?
|
19
|
+
html_options = label_html_options.dup
|
20
|
+
html_options[:class].push(:checkbox)
|
21
|
+
|
22
|
+
build_hidden_field_for_checkbox +
|
23
|
+
@builder.label(label_target, html_options) {
|
24
|
+
build_check_box_without_hidden_field + label_text
|
25
|
+
}
|
26
|
+
else
|
27
|
+
input + label
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Build a checkbox tag using default unchecked value. This allows us to
|
34
|
+
# reuse the method for nested boolean style, but with no unchecked value,
|
35
|
+
# which won't generate the hidden checkbox. This is the default functionality
|
36
|
+
# in Rails > 3.2.1, and is backported in SimpleFormWithClientValidation AV helpers.
|
37
|
+
def build_check_box(unchecked_value='0')
|
38
|
+
@builder.check_box(attribute_name, input_html_options, '1', unchecked_value)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Build a checkbox without generating the hidden field. See
|
42
|
+
# #build_hidden_field_for_checkbox for more info.
|
43
|
+
def build_check_box_without_hidden_field
|
44
|
+
build_check_box(nil)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Create a hidden field for the current checkbox, so we can simulate Rails
|
48
|
+
# functionality with hidden + checkbox, but under a nested context, where
|
49
|
+
# we need the hidden field to be *outside* the label (otherwise it
|
50
|
+
# generates invalid html - html5 only).
|
51
|
+
def build_hidden_field_for_checkbox
|
52
|
+
@builder.hidden_field(attribute_name, :value => '0', :id => nil,
|
53
|
+
:disabled => input_html_options[:disabled])
|
54
|
+
end
|
55
|
+
|
56
|
+
# Booleans are not required by default because in most of the cases
|
57
|
+
# it makes no sense marking them as required. The only exception is
|
58
|
+
# Terms of Use usually presented at most sites sign up screen.
|
59
|
+
def required_by_default?
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SimpleFormWithClientValidation
|
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(collection_builder)
|
13
|
+
collection_builder.check_box + collection_builder.text
|
14
|
+
end
|
15
|
+
|
16
|
+
def item_wrapper_class
|
17
|
+
"checkbox"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module SimpleFormWithClientValidation
|
2
|
+
module Inputs
|
3
|
+
class CollectionInput < Base
|
4
|
+
# Default boolean collection for use with selects/radios when no
|
5
|
+
# collection is given. Always fallback to this boolean collection.
|
6
|
+
# Texts can be translated using i18n in "simple_form.yes" and
|
7
|
+
# "simple_form.no" keys. See the example locale file.
|
8
|
+
def self.boolean_collection
|
9
|
+
i18n_cache :boolean_collection do
|
10
|
+
[ [I18n.t(:"simple_form.yes", :default => 'Yes'), true],
|
11
|
+
[I18n.t(:"simple_form.no", :default => 'No'), false] ]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def input
|
16
|
+
raise NotImplementedError,
|
17
|
+
"input should be implemented by classes inheriting from CollectionInput"
|
18
|
+
end
|
19
|
+
|
20
|
+
def input_options
|
21
|
+
options = super
|
22
|
+
options[:include_blank] = true unless skip_include_blank?
|
23
|
+
options
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def 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
|
33
|
+
end
|
34
|
+
|
35
|
+
def has_required?
|
36
|
+
super && (input_options[:include_blank] || multiple?)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Check if :include_blank must be included by default.
|
40
|
+
def skip_include_blank?
|
41
|
+
(options.keys & [:prompt, :include_blank, :default, :selected]).any? || multiple?
|
42
|
+
end
|
43
|
+
|
44
|
+
def multiple?
|
45
|
+
!!options[:input_html].try(:[], :multiple)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Detect the right method to find the label and value for a collection.
|
49
|
+
# If no label or value method are defined, will attempt to find them based
|
50
|
+
# on default label and value methods that can be configured through
|
51
|
+
# SimpleFormWithClientValidation.collection_label_methods and
|
52
|
+
# SimpleFormWithClientValidation.collection_value_methods.
|
53
|
+
def detect_collection_methods
|
54
|
+
label, value = options.delete(:label_method), options.delete(:value_method)
|
55
|
+
|
56
|
+
unless label && value
|
57
|
+
common_method_for = detect_common_display_methods
|
58
|
+
label ||= common_method_for[:label]
|
59
|
+
value ||= common_method_for[:value]
|
60
|
+
end
|
61
|
+
|
62
|
+
[label, value]
|
63
|
+
end
|
64
|
+
|
65
|
+
def detect_common_display_methods(collection_classes = detect_collection_classes)
|
66
|
+
collection_translated = translate_collection if collection_classes == [Symbol]
|
67
|
+
|
68
|
+
if collection_translated || collection_classes.include?(Array)
|
69
|
+
{ :label => :first, :value => :last }
|
70
|
+
elsif collection_includes_basic_objects?(collection_classes)
|
71
|
+
{ :label => :to_s, :value => :to_s }
|
72
|
+
else
|
73
|
+
sample = collection.first || collection.last
|
74
|
+
|
75
|
+
{ :label => SimpleFormWithClientValidation.collection_label_methods.find { |m| sample.respond_to?(m) },
|
76
|
+
:value => SimpleFormWithClientValidation.collection_value_methods.find { |m| sample.respond_to?(m) } }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def detect_collection_classes(some_collection = collection)
|
81
|
+
some_collection.map { |e| e.class }.uniq
|
82
|
+
end
|
83
|
+
|
84
|
+
def collection_includes_basic_objects?(collection_classes)
|
85
|
+
(collection_classes & [
|
86
|
+
String, Integer, Fixnum, Bignum, Float, NilClass, Symbol, TrueClass, FalseClass
|
87
|
+
]).any?
|
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
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|