simple_form 1.3.1 → 1.4.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/.travis.yml +6 -0
- data/CHANGELOG.rdoc +24 -0
- data/Gemfile +3 -8
- data/Gemfile.lock +36 -36
- data/MIT-LICENSE +1 -1
- data/README.rdoc +70 -23
- data/lib/generators/simple_form/templates/_form.html.erb +1 -1
- data/lib/generators/simple_form/templates/_form.html.haml +2 -10
- data/lib/generators/simple_form/templates/_form.html.slim +10 -0
- data/lib/generators/simple_form/templates/simple_form.rb +20 -4
- data/lib/simple_form.rb +28 -1
- data/lib/simple_form/action_view_extensions/builder.rb +9 -10
- data/lib/simple_form/action_view_extensions/form_helper.rb +2 -1
- data/lib/simple_form/components/errors.rb +5 -1
- data/lib/simple_form/components/labels.rb +1 -1
- data/lib/simple_form/form_builder.rb +117 -29
- data/lib/simple_form/inputs/base.rb +42 -6
- data/lib/simple_form/inputs/collection_input.rb +0 -1
- data/lib/simple_form/inputs/mapping_input.rb +0 -6
- data/lib/simple_form/inputs/numeric_input.rb +15 -7
- data/lib/simple_form/inputs/string_input.rb +14 -3
- data/lib/simple_form/map_type.rb +1 -1
- data/lib/simple_form/version.rb +1 -1
- data/test/action_view_extensions/builder_test.rb +39 -7
- data/test/action_view_extensions/form_helper_test.rb +12 -0
- data/test/components/label_test.rb +44 -0
- data/test/discovery_inputs.rb +21 -0
- data/test/form_builder_test.rb +181 -0
- data/test/inputs_test.rb +150 -14
- data/test/support/misc_helpers.rb +12 -0
- data/test/support/models.rb +40 -3
- data/test/test_helper.rb +13 -4
- metadata +10 -6
@@ -1,12 +1,19 @@
|
|
1
1
|
module SimpleForm
|
2
2
|
module Inputs
|
3
3
|
class StringInput < Base
|
4
|
+
extend MapType
|
5
|
+
|
6
|
+
map_type :string, :email, :search, :tel, :url, :to => :text_field
|
7
|
+
map_type :password, :to => :password_field
|
8
|
+
|
4
9
|
def input
|
5
10
|
input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
|
6
|
-
input_html_options[:maxlength] ||= limit if limit
|
7
|
-
|
11
|
+
input_html_options[:maxlength] ||= limit if limit && SimpleForm.html5
|
12
|
+
if password? || SimpleForm.html5
|
13
|
+
input_html_options[:type] ||= input_type unless string?
|
14
|
+
end
|
8
15
|
|
9
|
-
@builder.
|
16
|
+
@builder.send(input_method, attribute_name, input_html_options)
|
10
17
|
end
|
11
18
|
|
12
19
|
def input_html_classes
|
@@ -26,6 +33,10 @@ module SimpleForm
|
|
26
33
|
def string?
|
27
34
|
input_type == :string
|
28
35
|
end
|
36
|
+
|
37
|
+
def password?
|
38
|
+
input_type == :password
|
39
|
+
end
|
29
40
|
end
|
30
41
|
end
|
31
42
|
end
|
data/lib/simple_form/map_type.rb
CHANGED
@@ -10,7 +10,7 @@ module SimpleForm
|
|
10
10
|
def map_type(*types)
|
11
11
|
map_to = types.extract_options![:to]
|
12
12
|
raise ArgumentError, "You need to give :to as option to map_type" unless map_to
|
13
|
-
types.
|
13
|
+
self.mappings = mappings.merge types.each_with_object({}) { |t, m| m[t] = map_to }
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
data/lib/simple_form/version.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class BuilderTest < ActionView::TestCase
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
def with_custom_form_for(object, *args, &block)
|
5
|
+
with_concat_custom_form_for(object) do |f|
|
6
|
+
assert f.instance_of?(CustomFormBuilder)
|
7
|
+
yield f
|
8
|
+
end
|
7
9
|
end
|
8
10
|
|
9
11
|
def with_collection_radio(object, attribute, collection, value_method, text_method, options={}, html_options={})
|
@@ -116,10 +118,17 @@ class BuilderTest < ActionView::TestCase
|
|
116
118
|
assert_select 'form li input[type=radio][value=false]#user_active_false'
|
117
119
|
end
|
118
120
|
|
119
|
-
test 'collection radio
|
121
|
+
test 'collection radio wrap items in a span tag by default' do
|
120
122
|
with_collection_radio @user, :active, [true, false], :to_s, :to_s
|
121
123
|
|
122
|
-
|
124
|
+
assert_select 'form span input[type=radio][value=true]#user_active_true + label'
|
125
|
+
assert_select 'form span input[type=radio][value=false]#user_active_false + label'
|
126
|
+
end
|
127
|
+
|
128
|
+
test 'collection radio does not wrap input inside the label' do
|
129
|
+
with_collection_radio @user, :active, [true, false], :to_s, :to_s
|
130
|
+
|
131
|
+
assert_no_select 'form label input'
|
123
132
|
end
|
124
133
|
|
125
134
|
# COLLECTION CHECK BOX
|
@@ -259,10 +268,17 @@ class BuilderTest < ActionView::TestCase
|
|
259
268
|
assert_select 'form li input[type=checkbox][value=false]#user_active_false'
|
260
269
|
end
|
261
270
|
|
262
|
-
test 'collection check box
|
271
|
+
test 'collection check box wrap items in a span tag by default' do
|
272
|
+
with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s
|
273
|
+
|
274
|
+
assert_select 'form span input[type=checkbox][value=true]#user_active_true + label'
|
275
|
+
assert_select 'form span input[type=checkbox][value=false]#user_active_false + label'
|
276
|
+
end
|
277
|
+
|
278
|
+
test 'collection check box does not wrap input inside the label' do
|
263
279
|
with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s
|
264
280
|
|
265
|
-
assert_no_select 'form
|
281
|
+
assert_no_select 'form label input'
|
266
282
|
end
|
267
283
|
|
268
284
|
# SIMPLE FIELDS
|
@@ -273,4 +289,20 @@ class BuilderTest < ActionView::TestCase
|
|
273
289
|
end
|
274
290
|
end
|
275
291
|
end
|
292
|
+
|
293
|
+
test 'fields for yields an instance of CustomBuilder if main builder is a CustomBuilder' do
|
294
|
+
with_custom_form_for(:user) do |f|
|
295
|
+
f.simple_fields_for(:company) do |company|
|
296
|
+
assert company.instance_of?(CustomFormBuilder)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
test 'fields for yields an instance of FormBuilder if it was set in options' do
|
302
|
+
with_custom_form_for(:user) do |f|
|
303
|
+
f.simple_fields_for(:company, :builder => SimpleForm::FormBuilder) do |company|
|
304
|
+
assert company.instance_of?(SimpleForm::FormBuilder)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
276
308
|
end
|
@@ -13,6 +13,18 @@ class FormHelperTest < ActionView::TestCase
|
|
13
13
|
assert_select 'form.simple_form'
|
14
14
|
end
|
15
15
|
|
16
|
+
test 'simple form should use default browser validations by default' do
|
17
|
+
concat(simple_form_for(:user) do |f| end)
|
18
|
+
assert_no_select 'form[novalidate]'
|
19
|
+
end
|
20
|
+
|
21
|
+
test 'simple form should not use default browser validations if specified in the configuration options' do
|
22
|
+
swap SimpleForm, :browser_validations => false do
|
23
|
+
concat(simple_form_for(:user) do |f| end)
|
24
|
+
assert_select 'form[novalidate="novalidate"]'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
16
28
|
test 'simple form should add object name as css class to form when object is not present' do
|
17
29
|
concat(simple_form_for(:user) do |f| end)
|
18
30
|
assert_select 'form.simple_form.user'
|
@@ -96,6 +96,43 @@ class LabelTest < ActionView::TestCase
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
test 'label should do correct i18n lookup for nested models with nested translation' do
|
100
|
+
@user.company = Company.new(1, 'Empresa')
|
101
|
+
|
102
|
+
store_translations(:en, :simple_form => { :labels => {
|
103
|
+
:user => { :name => 'Usuario', :company => { :name => 'Nome da empresa' } }
|
104
|
+
} } ) do
|
105
|
+
with_concat_form_for @user do |f|
|
106
|
+
concat f.input :name
|
107
|
+
concat(f.simple_fields_for(:company) do |company_form|
|
108
|
+
concat(company_form.input :name)
|
109
|
+
end)
|
110
|
+
end
|
111
|
+
|
112
|
+
assert_select 'label[for=user_name]', /Usuario/
|
113
|
+
assert_select 'label[for=user_company_attributes_name]', /Nome da empresa/
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
test 'label should do correct i18n lookup for nested models with no nested translation' do
|
118
|
+
@user.company = Company.new(1, 'Empresa')
|
119
|
+
|
120
|
+
store_translations(:en, :simple_form => { :labels => {
|
121
|
+
:user => { :name => 'Usuario' },
|
122
|
+
:company => { :name => 'Nome da empresa' }
|
123
|
+
} } ) do
|
124
|
+
with_concat_form_for @user do |f|
|
125
|
+
concat f.input :name
|
126
|
+
concat(f.simple_fields_for(:company) do |company_form|
|
127
|
+
concat(company_form.input :name)
|
128
|
+
end)
|
129
|
+
end
|
130
|
+
|
131
|
+
assert_select 'label[for=user_name]', /Usuario/
|
132
|
+
assert_select 'label[for=user_company_attributes_name]', /Nome da empresa/
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
99
136
|
test 'label should have css class from type' do
|
100
137
|
with_label_for @user, :name, :string
|
101
138
|
assert_select 'label.string'
|
@@ -205,4 +242,11 @@ class LabelTest < ActionView::TestCase
|
|
205
242
|
with_label_for :project, :description, :string, :required => false
|
206
243
|
assert_no_select 'label.required[for=project_description]'
|
207
244
|
end
|
245
|
+
|
246
|
+
test 'label should add chosen label class' do
|
247
|
+
swap SimpleForm, :label_class => :my_custom_class do
|
248
|
+
with_label_for @user, :name, :string
|
249
|
+
assert_select 'label.my_custom_class'
|
250
|
+
end
|
251
|
+
end
|
208
252
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class StringInput < SimpleForm::Inputs::StringInput
|
2
|
+
def input
|
3
|
+
"<section>#{super}</section>".html_safe
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class NumericInput < SimpleForm::Inputs::NumericInput
|
8
|
+
def input
|
9
|
+
"<section>#{super}</section>".html_safe
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class CustomizedInput < SimpleForm::Inputs::StringInput
|
14
|
+
def input
|
15
|
+
"<section>#{super}</section>".html_safe
|
16
|
+
end
|
17
|
+
|
18
|
+
def input_method
|
19
|
+
:text_field
|
20
|
+
end
|
21
|
+
end
|
data/test/form_builder_test.rb
CHANGED
@@ -27,6 +27,12 @@ class FormBuilderTest < ActionView::TestCase
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
def with_full_error_for(object, *args)
|
31
|
+
with_concat_form_for(object) do |f|
|
32
|
+
f.full_error(*args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
30
36
|
def with_hint_for(object, *args)
|
31
37
|
with_concat_form_for(object) do |f|
|
32
38
|
f.hint(*args)
|
@@ -186,6 +192,14 @@ class FormBuilderTest < ActionView::TestCase
|
|
186
192
|
assert_select 'form input#user_avatar.file'
|
187
193
|
end
|
188
194
|
|
195
|
+
test 'builder should generate file for attributes that are real db columns but have file methods' do
|
196
|
+
@user.home_picture = mock("file")
|
197
|
+
@user.home_picture.expects(:respond_to?).with(:mounted_as).returns(true)
|
198
|
+
|
199
|
+
with_form_for @user, :home_picture
|
200
|
+
assert_select 'form input#user_home_picture.file'
|
201
|
+
end
|
202
|
+
|
189
203
|
test 'build should generate select if a collection is given' do
|
190
204
|
with_form_for @user, :age, :collection => 1..60
|
191
205
|
assert_select 'form select#user_age.select'
|
@@ -206,6 +220,13 @@ class FormBuilderTest < ActionView::TestCase
|
|
206
220
|
end
|
207
221
|
|
208
222
|
# COMMON OPTIONS
|
223
|
+
test 'builder should add chosen form class' do
|
224
|
+
swap SimpleForm, :form_class => :my_custom_class do
|
225
|
+
with_form_for @user, :name
|
226
|
+
assert_select 'form.my_custom_class'
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
209
230
|
test 'builder should allow passing options to input' do
|
210
231
|
with_form_for @user, :name, :input_html => { :class => 'my_input', :id => 'my_input' }
|
211
232
|
assert_select 'form input#my_input.my_input.string'
|
@@ -362,6 +383,43 @@ class FormBuilderTest < ActionView::TestCase
|
|
362
383
|
assert_select 'form div.input.required.string.field_with_errors'
|
363
384
|
end
|
364
385
|
|
386
|
+
# ONLY THE INPUT TAG
|
387
|
+
test "builder input_field should only render the input tag, nothing else" do
|
388
|
+
with_concat_form_for(@user) do |f|
|
389
|
+
f.input_field :name
|
390
|
+
end
|
391
|
+
assert_select 'form > input.required.string'
|
392
|
+
assert_no_select 'div.string'
|
393
|
+
assert_no_select 'label'
|
394
|
+
assert_no_select '.hint'
|
395
|
+
end
|
396
|
+
|
397
|
+
test 'builder input_field should allow overriding default input type' do
|
398
|
+
with_concat_form_for(@user) do |f|
|
399
|
+
f.input_field :name, :as => :text
|
400
|
+
end
|
401
|
+
|
402
|
+
assert_no_select 'input#user_name'
|
403
|
+
assert_select 'textarea#user_name.text'
|
404
|
+
end
|
405
|
+
|
406
|
+
test 'builder input_field should allow passing options to input tag' do
|
407
|
+
with_concat_form_for(@user) do |f|
|
408
|
+
f.input_field :name, :id => 'name_input', :class => 'name'
|
409
|
+
end
|
410
|
+
|
411
|
+
assert_select 'input.string.name#name_input'
|
412
|
+
end
|
413
|
+
|
414
|
+
test 'builder input_field should generate an input tag with a clean HTML' do
|
415
|
+
with_concat_form_for(@user) do |f|
|
416
|
+
f.input_field :name, :as => :integer, :class => 'name'
|
417
|
+
end
|
418
|
+
|
419
|
+
assert_no_select 'input.integer[input_html]'
|
420
|
+
assert_no_select 'input.integer[as]'
|
421
|
+
end
|
422
|
+
|
365
423
|
# WITHOUT OBJECT
|
366
424
|
test 'builder should generate properly when object is not present' do
|
367
425
|
with_form_for :project, :name
|
@@ -403,6 +461,22 @@ class FormBuilderTest < ActionView::TestCase
|
|
403
461
|
assert_select 'span.error#name_error', "can't be blank"
|
404
462
|
end
|
405
463
|
|
464
|
+
# FULL ERRORS
|
465
|
+
test 'builder should generate an full error tag for the attribute' do
|
466
|
+
with_full_error_for @user, :name
|
467
|
+
assert_select 'span.error', "Super User Name! can't be blank"
|
468
|
+
end
|
469
|
+
|
470
|
+
test 'builder should generate an full error tag with a clean HTML' do
|
471
|
+
with_full_error_for @user, :name
|
472
|
+
assert_no_select 'span.error[error_html]'
|
473
|
+
end
|
474
|
+
|
475
|
+
test 'builder should allow passing options to full error tag' do
|
476
|
+
with_full_error_for @user, :name, :id => 'name_error', :error_prefix => "Your name"
|
477
|
+
assert_select 'span.error#name_error', "Your name can't be blank"
|
478
|
+
end
|
479
|
+
|
406
480
|
# HINTS
|
407
481
|
test 'builder should generate a hint tag for the attribute' do
|
408
482
|
store_translations(:en, :simple_form => { :hints => { :user => { :name => "Add your name" }}}) do
|
@@ -511,6 +585,36 @@ class FormBuilderTest < ActionView::TestCase
|
|
511
585
|
end
|
512
586
|
end
|
513
587
|
|
588
|
+
test 'builder preloads collection association' do
|
589
|
+
value = @user.tags
|
590
|
+
value.expects(:to_a).returns(value)
|
591
|
+
with_association_for @user, :tags
|
592
|
+
assert_select 'form select.select#user_tag_ids'
|
593
|
+
assert_select 'form select option[value=1]', 'Tag 1'
|
594
|
+
assert_select 'form select option[value=2]', 'Tag 2'
|
595
|
+
assert_select 'form select option[value=3]', 'Tag 3'
|
596
|
+
end
|
597
|
+
|
598
|
+
test 'builder does not preload collection association if preload false' do
|
599
|
+
value = @user.company
|
600
|
+
value.expects(:to_a).never
|
601
|
+
with_association_for @user, :company, :preload => false
|
602
|
+
assert_select 'form select.select#user_company_id'
|
603
|
+
assert_select 'form select option[value=1]', 'Company 1'
|
604
|
+
assert_select 'form select option[value=2]', 'Company 2'
|
605
|
+
assert_select 'form select option[value=3]', 'Company 3'
|
606
|
+
end
|
607
|
+
|
608
|
+
test 'builder does not preload non collection association' do
|
609
|
+
value = @user.company
|
610
|
+
value.expects(:to_a).never
|
611
|
+
with_association_for @user, :company, :preload => false
|
612
|
+
assert_select 'form select.select#user_company_id'
|
613
|
+
assert_select 'form select option[value=1]', 'Company 1'
|
614
|
+
assert_select 'form select option[value=2]', 'Company 2'
|
615
|
+
assert_select 'form select option[value=3]', 'Company 3'
|
616
|
+
end
|
617
|
+
|
514
618
|
# ASSOCIATIONS - BELONGS TO
|
515
619
|
test 'builder creates a select for belongs_to associations' do
|
516
620
|
with_association_for @user, :company
|
@@ -611,4 +715,81 @@ class FormBuilderTest < ActionView::TestCase
|
|
611
715
|
with_custom_form_for @user, :email
|
612
716
|
assert_select 'form input[type=email]#user_email.custom'
|
613
717
|
end
|
718
|
+
|
719
|
+
test 'form with CustomMapTypeFormBuilder should use custom map type builder' do
|
720
|
+
with_concat_custom_mapping_form_for(:user) do |user|
|
721
|
+
assert user.instance_of?(CustomMapTypeFormBuilder)
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
test 'form with CustomMapTypeFormBuilder should use custom mapping' do
|
726
|
+
with_concat_custom_mapping_form_for(:user) do |user|
|
727
|
+
assert_equal SimpleForm::Inputs::StringInput, user.class.mappings[:custom_type]
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
test 'form without CustomMapTypeFormBuilder should not use custom mapping' do
|
732
|
+
with_concat_form_for(:user) do |user|
|
733
|
+
assert_nil user.class.mappings[:custom_type]
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
# DISCOVERY
|
738
|
+
# Setup new inputs and remove them after the test.
|
739
|
+
def discovery(value=false)
|
740
|
+
swap SimpleForm, :cache_discovery => value do
|
741
|
+
begin
|
742
|
+
load "discovery_inputs.rb"
|
743
|
+
yield
|
744
|
+
ensure
|
745
|
+
SimpleForm::FormBuilder.discovery_cache.clear
|
746
|
+
Object.send :remove_const, :StringInput
|
747
|
+
Object.send :remove_const, :NumericInput
|
748
|
+
Object.send :remove_const, :CustomizedInput
|
749
|
+
end
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
753
|
+
test 'builder should not discover new inputs if cached' do
|
754
|
+
with_form_for @user, :name
|
755
|
+
assert_select 'form input#user_name.string'
|
756
|
+
|
757
|
+
discovery(true) do
|
758
|
+
with_form_for @user, :name
|
759
|
+
assert_no_select 'form section input#user_name.string'
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
test 'builder should discover new inputs' do
|
764
|
+
discovery do
|
765
|
+
with_form_for @user, :name, :as => :customized
|
766
|
+
assert_select 'form section input#user_name.string'
|
767
|
+
end
|
768
|
+
end
|
769
|
+
|
770
|
+
test 'builder should not discover new inputs if discovery is off' do
|
771
|
+
with_form_for @user, :name
|
772
|
+
assert_select 'form input#user_name.string'
|
773
|
+
|
774
|
+
swap SimpleForm, :inputs_discovery => false do
|
775
|
+
discovery do
|
776
|
+
with_form_for @user, :name
|
777
|
+
assert_no_select 'form section input#user_name.string'
|
778
|
+
end
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
test 'builder should discover new inputs from mappings if not cached' do
|
783
|
+
discovery do
|
784
|
+
with_form_for @user, :name
|
785
|
+
assert_select 'form section input#user_name.string'
|
786
|
+
end
|
787
|
+
end
|
788
|
+
|
789
|
+
test 'builder should discover new inputs from internal fallbacks if not cached' do
|
790
|
+
discovery do
|
791
|
+
with_form_for @user, :age
|
792
|
+
assert_select 'form section input#user_age.numeric.integer'
|
793
|
+
end
|
794
|
+
end
|
614
795
|
end
|