simple_form 1.3.1 → 1.4.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.
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
|