simple_form 1.4.2 → 1.5.0
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/.gitignore +1 -0
- data/.travis.yml +9 -1
- data/CHANGELOG.rdoc +20 -0
- data/Gemfile +5 -4
- data/README.rdoc +2 -2
- data/Rakefile +1 -1
- data/lib/generators/simple_form/install_generator.rb +2 -6
- data/lib/generators/simple_form/templates/{simple_form.rb → config/initializers/simple_form.rb} +0 -0
- data/lib/generators/simple_form/templates/{en.yml → config/locales/simple_form.en.yml} +0 -0
- data/lib/simple_form.rb +1 -1
- data/lib/simple_form/action_view_extensions/builder.rb +12 -5
- data/lib/simple_form/action_view_extensions/form_helper.rb +13 -8
- data/lib/simple_form/components.rb +1 -1
- data/lib/simple_form/components/errors.rb +10 -2
- data/lib/simple_form/components/hints.rb +10 -0
- data/lib/simple_form/components/label_input.rb +5 -3
- data/lib/simple_form/components/labels.rb +10 -4
- data/lib/simple_form/components/placeholders.rb +10 -4
- data/lib/simple_form/error_notification.rb +1 -1
- data/lib/simple_form/form_builder.rb +10 -7
- data/lib/simple_form/helpers.rb +9 -0
- data/lib/simple_form/helpers/has_errors.rb +15 -0
- data/lib/simple_form/helpers/maxlength.rb +24 -0
- data/lib/simple_form/helpers/pattern.rb +28 -0
- data/lib/simple_form/helpers/required.rb +36 -0
- data/lib/simple_form/helpers/validators.rb +44 -0
- data/lib/simple_form/inputs.rb +5 -2
- data/lib/simple_form/inputs/base.rb +24 -47
- data/lib/simple_form/inputs/boolean_input.rb +1 -1
- data/lib/simple_form/inputs/collection_input.rb +1 -1
- data/lib/simple_form/inputs/date_time_input.rb +1 -1
- data/lib/simple_form/inputs/file_input.rb +9 -0
- data/lib/simple_form/inputs/hidden_input.rb +1 -1
- data/lib/simple_form/inputs/numeric_input.rb +20 -13
- data/lib/simple_form/inputs/password_input.rb +13 -0
- data/lib/simple_form/inputs/range_input.rb +16 -0
- data/lib/simple_form/inputs/string_input.rb +7 -24
- data/lib/simple_form/inputs/text_input.rb +12 -0
- data/lib/simple_form/version.rb +1 -1
- data/test/action_view_extensions/builder_test.rb +59 -0
- data/test/action_view_extensions/form_helper_test.rb +21 -0
- data/test/discovery_inputs.rb +2 -2
- data/test/form_builder_test.rb +54 -0
- data/test/inputs_test.rb +185 -20
- data/test/support/models.rb +23 -2
- data/test/test_helper.rb +1 -0
- metadata +17 -11
- data/Gemfile.lock +0 -54
- data/init.rb +0 -1
- data/lib/simple_form/has_errors.rb +0 -14
- data/lib/simple_form/inputs/mapping_input.rb +0 -29
@@ -0,0 +1,36 @@
|
|
1
|
+
module SimpleForm
|
2
|
+
module Helpers
|
3
|
+
module Required
|
4
|
+
private
|
5
|
+
|
6
|
+
def attribute_required?
|
7
|
+
@required
|
8
|
+
end
|
9
|
+
|
10
|
+
def calculate_required
|
11
|
+
if !options[:required].nil?
|
12
|
+
options[:required]
|
13
|
+
elsif has_validators?
|
14
|
+
(attribute_validators + reflection_validators).any? do |v|
|
15
|
+
v.kind == :presence && valid_validator?(v)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
attribute_required_by_default?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Whether this input is valid for HTML 5 required attribute.
|
23
|
+
def has_required?
|
24
|
+
attribute_required? && SimpleForm.html5 && SimpleForm.browser_validations
|
25
|
+
end
|
26
|
+
|
27
|
+
def attribute_required_by_default?
|
28
|
+
SimpleForm.required_by_default
|
29
|
+
end
|
30
|
+
|
31
|
+
def required_class
|
32
|
+
attribute_required? ? :required : :optional
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module SimpleForm
|
2
|
+
module Helpers
|
3
|
+
module Validators
|
4
|
+
private
|
5
|
+
|
6
|
+
def has_validators?
|
7
|
+
attribute_name && object.class.respond_to?(:validators_on)
|
8
|
+
end
|
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 }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/simple_form/inputs.rb
CHANGED
@@ -5,10 +5,13 @@ module SimpleForm
|
|
5
5
|
autoload :BooleanInput, 'simple_form/inputs/boolean_input'
|
6
6
|
autoload :CollectionInput, 'simple_form/inputs/collection_input'
|
7
7
|
autoload :DateTimeInput, 'simple_form/inputs/date_time_input'
|
8
|
+
autoload :FileInput, 'simple_form/inputs/file_input'
|
8
9
|
autoload :HiddenInput, 'simple_form/inputs/hidden_input'
|
9
|
-
autoload :MappingInput, 'simple_form/inputs/mapping_input'
|
10
10
|
autoload :NumericInput, 'simple_form/inputs/numeric_input'
|
11
|
+
autoload :PasswordInput, 'simple_form/inputs/password_input'
|
11
12
|
autoload :PriorityInput, 'simple_form/inputs/priority_input'
|
13
|
+
autoload :RangeInput, 'simple_form/inputs/range_input'
|
12
14
|
autoload :StringInput, 'simple_form/inputs/string_input'
|
15
|
+
autoload :TextInput, 'simple_form/inputs/text_input'
|
13
16
|
end
|
14
|
-
end
|
17
|
+
end
|
@@ -9,12 +9,26 @@ module SimpleForm
|
|
9
9
|
:update => :edit
|
10
10
|
}
|
11
11
|
|
12
|
+
include SimpleForm::Helpers::Required
|
13
|
+
include SimpleForm::Helpers::Validators
|
14
|
+
include SimpleForm::Helpers::Maxlength
|
15
|
+
include SimpleForm::Helpers::Pattern
|
16
|
+
|
12
17
|
include SimpleForm::Components::Errors
|
13
18
|
include SimpleForm::Components::Hints
|
14
19
|
include SimpleForm::Components::LabelInput
|
15
20
|
include SimpleForm::Components::Placeholders
|
16
21
|
include SimpleForm::Components::Wrapper
|
17
22
|
|
23
|
+
# Enables certain components support to the given input.
|
24
|
+
def self.enable(*args)
|
25
|
+
args.each { |m| alias_method m, :"enabled_#{m}" }
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.disable(*args)
|
29
|
+
args.each { |m| alias_method m, :"disabled_#{m}" }
|
30
|
+
end
|
31
|
+
|
18
32
|
attr_reader :attribute_name, :column, :input_type, :reflection,
|
19
33
|
:options, :input_html_options
|
20
34
|
|
@@ -27,8 +41,9 @@ module SimpleForm
|
|
27
41
|
@input_type = input_type
|
28
42
|
@reflection = options.delete(:reflection)
|
29
43
|
@options = options
|
44
|
+
@required = calculate_required
|
30
45
|
@input_html_options = html_options_for(:input, input_html_classes).tap do |o|
|
31
|
-
o[:required] = true if has_required?
|
46
|
+
o[:required] = true if has_required?
|
32
47
|
o[:disabled] = true if disabled?
|
33
48
|
o[:autofocus] = true if has_autofocus? && SimpleForm.html5
|
34
49
|
end
|
@@ -56,57 +71,24 @@ module SimpleForm
|
|
56
71
|
wrap(content)
|
57
72
|
end
|
58
73
|
|
59
|
-
|
74
|
+
private
|
60
75
|
|
61
|
-
def
|
62
|
-
|
76
|
+
def add_size!
|
77
|
+
input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
|
63
78
|
end
|
64
79
|
|
65
|
-
def
|
66
|
-
|
67
|
-
options[:required]
|
68
|
-
elsif has_validators?
|
69
|
-
(attribute_validators + reflection_validators).any? do |v|
|
70
|
-
v.kind == :presence && !conditional_validators?(v)
|
71
|
-
end
|
72
|
-
else
|
73
|
-
attribute_required_by_default?
|
74
|
-
end
|
80
|
+
def limit
|
81
|
+
column && column.limit
|
75
82
|
end
|
76
83
|
|
77
|
-
|
78
|
-
|
79
|
-
attribute_required? && SimpleForm.html5 && SimpleForm.browser_validations
|
84
|
+
def components_list
|
85
|
+
options[:components] || SimpleForm.components
|
80
86
|
end
|
81
87
|
|
82
88
|
def has_autofocus?
|
83
89
|
options[:autofocus]
|
84
90
|
end
|
85
91
|
|
86
|
-
def has_validators?
|
87
|
-
attribute_name && object.class.respond_to?(:validators_on)
|
88
|
-
end
|
89
|
-
|
90
|
-
def attribute_validators
|
91
|
-
object.class.validators_on(attribute_name)
|
92
|
-
end
|
93
|
-
|
94
|
-
def reflection_validators
|
95
|
-
reflection ? object.class.validators_on(reflection.name) : []
|
96
|
-
end
|
97
|
-
|
98
|
-
def conditional_validators?(validator)
|
99
|
-
validator.options.include?(:if) || validator.options.include?(:unless)
|
100
|
-
end
|
101
|
-
|
102
|
-
def attribute_required_by_default?
|
103
|
-
SimpleForm.required_by_default
|
104
|
-
end
|
105
|
-
|
106
|
-
def required_class
|
107
|
-
attribute_required? ? :required : :optional
|
108
|
-
end
|
109
|
-
|
110
92
|
# Find reflection name when available, otherwise use attribute
|
111
93
|
def reflection_or_attribute_name
|
112
94
|
reflection ? reflection.name : attribute_name
|
@@ -120,7 +102,7 @@ module SimpleForm
|
|
120
102
|
end
|
121
103
|
|
122
104
|
def disabled?
|
123
|
-
options[:disabled]
|
105
|
+
options[:disabled] == true
|
124
106
|
end
|
125
107
|
|
126
108
|
# Lookup translations for the given namespace using I18n, based on object name,
|
@@ -197,11 +179,6 @@ module SimpleForm
|
|
197
179
|
action = action.to_sym
|
198
180
|
ACTIONS[action] || action
|
199
181
|
end
|
200
|
-
|
201
|
-
def input_method
|
202
|
-
self.class.mappings[input_type] or
|
203
|
-
raise("Could not find method for #{input_type.inspect}")
|
204
|
-
end
|
205
182
|
end
|
206
183
|
end
|
207
184
|
end
|
@@ -1,11 +1,15 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
3
|
class NumericInput < Base
|
4
|
+
enable :placeholder
|
5
|
+
|
4
6
|
def input
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
add_size!
|
8
|
+
if SimpleForm.html5
|
9
|
+
input_html_options[:type] ||= "number"
|
10
|
+
input_html_options[:step] ||= integer? ? 1 : "any"
|
11
|
+
infer_attributes_from_validations!
|
12
|
+
end
|
9
13
|
@builder.text_field(attribute_name, input_html_options)
|
10
14
|
end
|
11
15
|
|
@@ -13,10 +17,11 @@ module SimpleForm
|
|
13
17
|
super.unshift("numeric")
|
14
18
|
end
|
15
19
|
|
16
|
-
|
20
|
+
private
|
17
21
|
|
18
|
-
|
19
|
-
|
22
|
+
# Rails adds the size attr by default, if the :size key does not exist.
|
23
|
+
def add_size!
|
24
|
+
input_html_options[:size] ||= nil
|
20
25
|
end
|
21
26
|
|
22
27
|
def infer_attributes_from_validations!
|
@@ -50,15 +55,17 @@ module SimpleForm
|
|
50
55
|
end
|
51
56
|
|
52
57
|
def find_numericality_validator
|
53
|
-
|
58
|
+
find_validator(ActiveModel::Validations::NumericalityValidator)
|
54
59
|
end
|
55
60
|
|
56
|
-
private
|
57
|
-
|
58
61
|
def evaluate_validator_option(option)
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
62
69
|
end
|
63
70
|
end
|
64
71
|
end
|
@@ -1,42 +1,25 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
3
|
class StringInput < Base
|
4
|
-
|
5
|
-
|
6
|
-
map_type :string, :email, :search, :tel, :url, :to => :text_field
|
7
|
-
map_type :password, :to => :password_field
|
4
|
+
enable :placeholder
|
8
5
|
|
9
6
|
def input
|
10
|
-
input_html_options[:
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@builder.send(input_method, attribute_name, input_html_options)
|
7
|
+
input_html_options[:type] ||= input_type if SimpleForm.html5 && !string?
|
8
|
+
add_maxlength!
|
9
|
+
add_pattern!
|
10
|
+
add_size!
|
11
|
+
@builder.text_field(attribute_name, input_html_options)
|
17
12
|
end
|
18
13
|
|
19
14
|
def input_html_classes
|
20
15
|
string? ? super : super.unshift("string")
|
21
16
|
end
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
def limit
|
26
|
-
column && column.limit
|
27
|
-
end
|
28
|
-
|
29
|
-
def has_placeholder?
|
30
|
-
placeholder_present?
|
31
|
-
end
|
18
|
+
private
|
32
19
|
|
33
20
|
def string?
|
34
21
|
input_type == :string
|
35
22
|
end
|
36
|
-
|
37
|
-
def password?
|
38
|
-
input_type == :password
|
39
|
-
end
|
40
23
|
end
|
41
24
|
end
|
42
25
|
end
|
data/lib/simple_form/version.rb
CHANGED
@@ -96,6 +96,24 @@ class BuilderTest < ActionView::TestCase
|
|
96
96
|
assert_select 'form ul input[type=radio][value=false]#user_active_false'
|
97
97
|
end
|
98
98
|
|
99
|
+
test 'collection radio does not wrap the collection in the explicitly false collection wrapper tag' do
|
100
|
+
swap SimpleForm, :collection_wrapper_tag => :ul do
|
101
|
+
with_collection_radio @user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => false
|
102
|
+
|
103
|
+
assert_no_select 'form ul'
|
104
|
+
assert_no_select 'form ul'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
test 'collection radio does not wrap the collection in the explicitly nil collection wrapper tag' do
|
109
|
+
swap SimpleForm, :collection_wrapper_tag => :ul do
|
110
|
+
with_collection_radio @user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => nil
|
111
|
+
|
112
|
+
assert_no_select 'form ul'
|
113
|
+
assert_no_select 'form ul'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
99
117
|
test 'collection radio does not wrap the collection by default' do
|
100
118
|
with_collection_radio @user, :active, [true, false], :to_s, :to_s
|
101
119
|
|
@@ -118,6 +136,20 @@ class BuilderTest < ActionView::TestCase
|
|
118
136
|
assert_select 'form li input[type=radio][value=false]#user_active_false'
|
119
137
|
end
|
120
138
|
|
139
|
+
test 'collection radio does not wrap each label/radio in the explicitly false item wrapper tag' do
|
140
|
+
with_collection_radio @user, :active, [true, false], :to_s, :to_s, :item_wrapper_tag => false
|
141
|
+
|
142
|
+
assert_no_select 'form span input[type=radio][value=true]#user_active_true'
|
143
|
+
assert_no_select 'form span input[type=radio][value=false]#user_active_false'
|
144
|
+
end
|
145
|
+
|
146
|
+
test 'collection radio does not wrap each label/radio in the explicitly nil item wrapper tag' do
|
147
|
+
with_collection_radio @user, :active, [true, false], :to_s, :to_s, :item_wrapper_tag => nil
|
148
|
+
|
149
|
+
assert_no_select 'form span input[type=radio][value=true]#user_active_true'
|
150
|
+
assert_no_select 'form span input[type=radio][value=false]#user_active_false'
|
151
|
+
end
|
152
|
+
|
121
153
|
test 'collection radio wrap items in a span tag by default' do
|
122
154
|
with_collection_radio @user, :active, [true, false], :to_s, :to_s
|
123
155
|
|
@@ -246,6 +278,13 @@ class BuilderTest < ActionView::TestCase
|
|
246
278
|
assert_select 'form ul input[type=checkbox][value=false]#user_active_false'
|
247
279
|
end
|
248
280
|
|
281
|
+
test 'collection check box does not wrap the collection in the explicitly false collection wrapper tag' do
|
282
|
+
with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => false, :item_wrapper_tag => false
|
283
|
+
|
284
|
+
assert_select 'form > input[type=checkbox][value=true]#user_active_true'
|
285
|
+
assert_select 'form > input[type=checkbox][value=false]#user_active_false'
|
286
|
+
end
|
287
|
+
|
249
288
|
test 'collection check box does not wrap the collection by default' do
|
250
289
|
with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s
|
251
290
|
|
@@ -268,6 +307,13 @@ class BuilderTest < ActionView::TestCase
|
|
268
307
|
assert_select 'form li input[type=checkbox][value=false]#user_active_false'
|
269
308
|
end
|
270
309
|
|
310
|
+
test 'collection check box does not wrapp each label/radio in the explicitly false item wrapper tag' do
|
311
|
+
with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s, :item_wrapper_tag => false
|
312
|
+
|
313
|
+
assert_select 'form > input[type=checkbox][value=true]#user_active_true'
|
314
|
+
assert_select 'form > input[type=checkbox][value=false]#user_active_false'
|
315
|
+
end
|
316
|
+
|
271
317
|
test 'collection check box wrap items in a span tag by default' do
|
272
318
|
with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s
|
273
319
|
|
@@ -290,6 +336,19 @@ class BuilderTest < ActionView::TestCase
|
|
290
336
|
end
|
291
337
|
end
|
292
338
|
|
339
|
+
test 'fields for with a hash like model yeilds an instance of FormBuilder' do
|
340
|
+
@hash_backed_author = HashBackedAuthor.new
|
341
|
+
|
342
|
+
with_concat_form_for(:user) do |f|
|
343
|
+
f.simple_fields_for(:author, @hash_backed_author) do |author|
|
344
|
+
assert author.instance_of?(SimpleForm::FormBuilder)
|
345
|
+
author.input :name
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
assert_select "input[name='user[author][name]'][value='hash backed author']"
|
350
|
+
end
|
351
|
+
|
293
352
|
test 'fields for yields an instance of CustomBuilder if main builder is a CustomBuilder' do
|
294
353
|
with_custom_form_for(:user) do |f|
|
295
354
|
f.simple_fields_for(:company) do |company|
|