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.

Files changed (51) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +9 -1
  3. data/CHANGELOG.rdoc +20 -0
  4. data/Gemfile +5 -4
  5. data/README.rdoc +2 -2
  6. data/Rakefile +1 -1
  7. data/lib/generators/simple_form/install_generator.rb +2 -6
  8. data/lib/generators/simple_form/templates/{simple_form.rb → config/initializers/simple_form.rb} +0 -0
  9. data/lib/generators/simple_form/templates/{en.yml → config/locales/simple_form.en.yml} +0 -0
  10. data/lib/simple_form.rb +1 -1
  11. data/lib/simple_form/action_view_extensions/builder.rb +12 -5
  12. data/lib/simple_form/action_view_extensions/form_helper.rb +13 -8
  13. data/lib/simple_form/components.rb +1 -1
  14. data/lib/simple_form/components/errors.rb +10 -2
  15. data/lib/simple_form/components/hints.rb +10 -0
  16. data/lib/simple_form/components/label_input.rb +5 -3
  17. data/lib/simple_form/components/labels.rb +10 -4
  18. data/lib/simple_form/components/placeholders.rb +10 -4
  19. data/lib/simple_form/error_notification.rb +1 -1
  20. data/lib/simple_form/form_builder.rb +10 -7
  21. data/lib/simple_form/helpers.rb +9 -0
  22. data/lib/simple_form/helpers/has_errors.rb +15 -0
  23. data/lib/simple_form/helpers/maxlength.rb +24 -0
  24. data/lib/simple_form/helpers/pattern.rb +28 -0
  25. data/lib/simple_form/helpers/required.rb +36 -0
  26. data/lib/simple_form/helpers/validators.rb +44 -0
  27. data/lib/simple_form/inputs.rb +5 -2
  28. data/lib/simple_form/inputs/base.rb +24 -47
  29. data/lib/simple_form/inputs/boolean_input.rb +1 -1
  30. data/lib/simple_form/inputs/collection_input.rb +1 -1
  31. data/lib/simple_form/inputs/date_time_input.rb +1 -1
  32. data/lib/simple_form/inputs/file_input.rb +9 -0
  33. data/lib/simple_form/inputs/hidden_input.rb +1 -1
  34. data/lib/simple_form/inputs/numeric_input.rb +20 -13
  35. data/lib/simple_form/inputs/password_input.rb +13 -0
  36. data/lib/simple_form/inputs/range_input.rb +16 -0
  37. data/lib/simple_form/inputs/string_input.rb +7 -24
  38. data/lib/simple_form/inputs/text_input.rb +12 -0
  39. data/lib/simple_form/version.rb +1 -1
  40. data/test/action_view_extensions/builder_test.rb +59 -0
  41. data/test/action_view_extensions/form_helper_test.rb +21 -0
  42. data/test/discovery_inputs.rb +2 -2
  43. data/test/form_builder_test.rb +54 -0
  44. data/test/inputs_test.rb +185 -20
  45. data/test/support/models.rb +23 -2
  46. data/test/test_helper.rb +1 -0
  47. metadata +17 -11
  48. data/Gemfile.lock +0 -54
  49. data/init.rb +0 -1
  50. data/lib/simple_form/has_errors.rb +0 -14
  51. data/lib/simple_form/inputs/mapping_input.rb +0 -29
@@ -47,6 +47,16 @@ class FormHelperTest < ActionView::TestCase
47
47
  assert_select 'form.simple_form.user'
48
48
  end
49
49
 
50
+ test 'simple form should not add object class to form if css_class is specified' do
51
+ concat(simple_form_for(:user, :html => {:class => nil}) do |f| end)
52
+ assert_no_select 'form.user'
53
+ end
54
+
55
+ test 'simple form should add custom class to form if css_class is specified' do
56
+ concat(simple_form_for(:user, :html => {:class => 'my_class'}) do |f| end)
57
+ assert_select 'form.my_class'
58
+ end
59
+
50
60
  test 'pass options to simple form' do
51
61
  concat(simple_form_for(:user, :url => '/account', :html => { :id => 'my_form' }) do |f| end)
52
62
  assert_select 'form#my_form'
@@ -58,4 +68,15 @@ class FormHelperTest < ActionView::TestCase
58
68
  assert f.instance_of?(SimpleForm::FormBuilder)
59
69
  end)
60
70
  end
71
+
72
+ test 'fields for with a hash like model yeilds an instance of FormBuilder' do
73
+ @hash_backed_author = HashBackedAuthor.new
74
+
75
+ concat(simple_fields_for(:author, @hash_backed_author) do |f|
76
+ assert f.instance_of?(SimpleForm::FormBuilder)
77
+ f.input :name
78
+ end)
79
+
80
+ assert_select "input[name='author[name]'][value='hash backed author']"
81
+ end
61
82
  end
@@ -14,8 +14,8 @@ class CustomizedInput < SimpleForm::Inputs::StringInput
14
14
  def input
15
15
  "<section>#{super}</section>".html_safe
16
16
  end
17
-
17
+
18
18
  def input_method
19
19
  :text_field
20
20
  end
21
- end
21
+ end
@@ -338,6 +338,8 @@ class FormBuilderTest < ActionView::TestCase
338
338
  assert_select 'input.optional#user_name'
339
339
  end
340
340
 
341
+ # VALIDATORS :if :unless
342
+
341
343
  test 'builder input should not be required when ActiveModel::Validations is included and if option is present' do
342
344
  with_form_for @validating_user, :age
343
345
  assert_no_select 'input.required'
@@ -352,7 +354,53 @@ class FormBuilderTest < ActionView::TestCase
352
354
  assert_select 'input.optional#validating_user_amount'
353
355
  end
354
356
 
357
+ # VALIDATORS :on
358
+
359
+ test 'builder input should be required when validation is on create and is not persisted' do
360
+ @validating_user.new_record!
361
+ with_form_for @validating_user, :action
362
+ assert_select 'input.required'
363
+ assert_select 'input[required]'
364
+ assert_select 'input.required[required]#validating_user_action'
365
+ end
366
+
367
+ test 'builder input should not be required when validation is on create and is persisted' do
368
+ with_form_for @validating_user, :action
369
+ assert_no_select 'input.required'
370
+ assert_no_select 'input[required]'
371
+ assert_select 'input.optional#validating_user_action'
372
+ end
373
+
374
+ test 'builder input should be required when validation is on save' do
375
+ with_form_for @validating_user, :credit_limit
376
+ assert_select 'input.required'
377
+ assert_select 'input[required]'
378
+ assert_select 'input.required[required]#validating_user_credit_limit'
379
+
380
+ @validating_user.new_record!
381
+ with_form_for @validating_user, :credit_limit
382
+ assert_select 'input.required'
383
+ assert_select 'input[required]'
384
+ assert_select 'input.required[required]#validating_user_credit_limit'
385
+ end
386
+
387
+ test 'builder input should be required when validation is on update and is persisted' do
388
+ with_form_for @validating_user, :phone_number
389
+ assert_select 'input.required'
390
+ assert_select 'input[required]'
391
+ assert_select 'input.required[required]#validating_user_phone_number'
392
+ end
393
+
394
+ test 'builder input should not be required when validation is on update and is not persisted' do
395
+ @validating_user.new_record!
396
+ with_form_for @validating_user, :phone_number
397
+ assert_no_select 'input.required'
398
+ assert_no_select 'input[required]'
399
+ assert_select 'input.optional#validating_user_phone_number'
400
+ end
401
+
355
402
  # WRAPPERS
403
+
356
404
  test 'builder support wrapping around an specific tag' do
357
405
  swap SimpleForm, :wrapper_tag => :p do
358
406
  with_form_for @user, :name
@@ -361,6 +409,12 @@ class FormBuilderTest < ActionView::TestCase
361
409
  end
362
410
  end
363
411
 
412
+ test 'builder support no wrapping when wrapper is false' do
413
+ with_form_for @user, :name, :wrapper => false
414
+ assert_select 'form > label[for=user_name]'
415
+ assert_select 'form > input#user_name.string'
416
+ end
417
+
364
418
  test 'builder wrapping tag adds default css classes' do
365
419
  swap SimpleForm, :wrapper_tag => :p do
366
420
  with_form_for @user, :name
data/test/inputs_test.rb CHANGED
@@ -141,9 +141,9 @@ class InputTest < ActionView::TestCase
141
141
  assert_select "input#user_password.password[type=password][name='user[password]']"
142
142
  end
143
143
 
144
- test 'input should use default text size for decimal attributes' do
144
+ test 'input should not use size attribute for decimal attributes' do
145
145
  with_input_for @user, :credit_limit, :decimal
146
- assert_select 'input.decimal[size=50]'
146
+ assert_no_select 'input.decimal[size]'
147
147
  end
148
148
 
149
149
  test 'input should get maxlength from column definition for string attributes' do
@@ -151,6 +151,11 @@ class InputTest < ActionView::TestCase
151
151
  assert_select 'input.string[maxlength=100]'
152
152
  end
153
153
 
154
+ test 'input should not get maxlength from column without size definition for string attributes' do
155
+ with_input_for @user, :action, :string
156
+ assert_no_select 'input.string[maxlength]'
157
+ end
158
+
154
159
  test 'input should get size from column definition for string attributes respecting maximum value' do
155
160
  with_input_for @user, :name, :string
156
161
  assert_select 'input.string[size=50]'
@@ -166,6 +171,11 @@ class InputTest < ActionView::TestCase
166
171
  assert_select 'input.password[type=password][maxlength=100]'
167
172
  end
168
173
 
174
+ test 'input should infer maxlength column definition from validation when present' do
175
+ with_input_for @validating_user, :name, :string
176
+ assert_select 'input.string[maxlength=25]'
177
+ end
178
+
169
179
  test 'when not using HTML5, does not show maxlength attribute' do
170
180
  swap SimpleForm, :html5 => false do
171
181
  with_input_for @user, :password, :password
@@ -173,6 +183,13 @@ class InputTest < ActionView::TestCase
173
183
  end
174
184
  end
175
185
 
186
+ test 'when not using HTML5, does not show maxlength attribute with validating lenght attribute' do
187
+ swap SimpleForm, :html5 => false do
188
+ with_input_for @validating_user, :name, :string
189
+ assert_no_select 'input.string[maxlength]'
190
+ end
191
+ end
192
+
176
193
  test 'input should not generate placeholder by default' do
177
194
  with_input_for @user, :name, :string
178
195
  assert_no_select 'input[placeholder]'
@@ -212,6 +229,22 @@ class InputTest < ActionView::TestCase
212
229
  end
213
230
  end
214
231
 
232
+ test 'input should infer pattern from attributes when pattern is true' do
233
+ with_input_for @other_validating_user, :country, :string, :pattern => true
234
+ assert_select 'input[pattern="\w+"]'
235
+ end
236
+
237
+ test 'input should use given pattern from attributes' do
238
+ with_input_for @other_validating_user, :country, :string, :pattern => "\\d+"
239
+ assert_select 'input[pattern="\d+"]'
240
+ end
241
+
242
+ test 'input should fail if pattern is true but no pattern exists' do
243
+ assert_raise RuntimeError do
244
+ with_input_for @other_validating_user, :name, :string, :pattern => true
245
+ end
246
+ end
247
+
215
248
  # NumericInput
216
249
  test 'input should generate an integer text field for integer attributes ' do
217
250
  with_input_for @user, :age, :integer
@@ -345,16 +378,28 @@ class InputTest < ActionView::TestCase
345
378
  end
346
379
  end
347
380
 
381
+ [:integer, :float, :decimal].each do |type|
382
+ test "#{type} input should infer min value from attributes with greater than or equal validation" do
383
+ with_input_for @validating_user, :age, type
384
+ assert_select 'input[min=18]'
385
+ end
386
+
387
+ test "#{type} input should infer the max value from attributes with less than or equal to validation" do
388
+ with_input_for @validating_user, :age, type
389
+ assert_select 'input[max=99]'
390
+ end
391
+ end
392
+
348
393
  # Numeric input but HTML5 disabled
349
- test ' when not using HTML5 input should not generate field with type number and use text instead' do
394
+ test 'when not using HTML5 input should not generate field with type number and use text instead' do
350
395
  swap SimpleForm, :html5 => false do
351
396
  with_input_for @user, :age, :integer
352
397
  assert_no_select "input[type=number]"
353
- assert_no_select "input#user_age[text]"
398
+ assert_select "input#user_age[type=text]"
354
399
  end
355
400
  end
356
401
 
357
- test 'when not using HTML5 input should not use min or max or step attributes' do
402
+ test 'when not using HTML5 input should not use min or max or step attributes for numeric type' do
358
403
  swap SimpleForm, :html5 => false do
359
404
  with_input_for @validating_user, :age, :integer
360
405
  assert_no_select "input[min]"
@@ -363,15 +408,63 @@ class InputTest < ActionView::TestCase
363
408
  end
364
409
  end
365
410
 
366
- [:integer, :float, :decimal].each do |type|
367
- test "#{type} input should infer min value from attributes with greater than or equal validation" do
368
- with_input_for @validating_user, :age, type
369
- assert_select 'input[min=18]'
411
+ # RangeInput
412
+ test 'range input generates a input type range, based on numeric input' do
413
+ with_input_for @user, :age, :range
414
+ assert_select "input#user_age.range[type=range]"
415
+ end
416
+
417
+ test 'range input does not generate placeholder' do
418
+ with_input_for @user, :age, :range, :placeholder => "Select your age"
419
+ assert_select "input[type=range]"
420
+ assert_no_select "input[placeholder]"
421
+ end
422
+
423
+ test 'range input allows givin min and max attributes' do
424
+ with_input_for @user, :age, :range, :input_html => { :min => 10, :max => 50 }
425
+ assert_select "input[type=range][min=10][max=50]"
426
+ end
427
+
428
+ test 'range input infers min and max attributes from validations' do
429
+ with_input_for @validating_user, :age, :range
430
+ assert_select "input[type=range][min=18][max=99]"
431
+ end
432
+
433
+ test 'range input add default step attribute' do
434
+ with_input_for @validating_user, :age, :range
435
+ assert_select "input[type=range][step=1]"
436
+ end
437
+
438
+ test 'range input allows givin a step through input html options' do
439
+ with_input_for @validating_user, :age, :range, :input_html => { :step => 2 }
440
+ assert_select "input[type=range][step=2]"
441
+ end
442
+
443
+ test 'range input should not generate min attribute by default' do
444
+ with_input_for @user, :age, :range
445
+ assert_no_select 'input[min]'
446
+ end
447
+
448
+ test 'range input should not generate max attribute by default' do
449
+ with_input_for @user, :age, :range
450
+ assert_no_select 'input[max]'
451
+ end
452
+
453
+ # RangeInput iwth HTML5 disabled
454
+ test 'when not using HTML5, range input does not generate field with range type, and use text instead' do
455
+ swap SimpleForm, :html5 => false do
456
+ with_input_for @user, :age, :range
457
+ assert_no_select "input[type=number]"
458
+ assert_select "input[type=text]"
370
459
  end
460
+ end
371
461
 
372
- test "#{type} input should infer the max value from attributes with less than or equal to validation" do
373
- with_input_for @validating_user, :age, type
374
- assert_select 'input[max=99]'
462
+ test 'when not using HTML5, range input should not use min or max or step attributes' do
463
+ swap SimpleForm, :html5 => false do
464
+ with_input_for @validating_user, :age, :range
465
+ assert_no_select "input[min]"
466
+ assert_no_select "input[max]"
467
+ assert_no_select "input[step]"
375
468
  end
376
469
  end
377
470
 
@@ -393,6 +486,30 @@ class InputTest < ActionView::TestCase
393
486
  assert_select 'textarea.text[placeholder=Put in some text]'
394
487
  end
395
488
 
489
+ test 'input should get maxlength from column definition for text attributes' do
490
+ with_input_for @user, :description, :text
491
+ assert_select 'textarea.text[maxlength=200]'
492
+ end
493
+
494
+ test 'input should infer maxlength column definition from validation when present for text attributes' do
495
+ with_input_for @validating_user, :description, :text
496
+ assert_select 'textarea.text[maxlength=50]'
497
+ end
498
+
499
+ test 'when not using HTML5, does not show maxlength attribute for text attributes' do
500
+ swap SimpleForm, :html5 => false do
501
+ with_input_for @user, :description, :text
502
+ assert_no_select 'textarea.text[maxlength]'
503
+ end
504
+ end
505
+
506
+ test 'when not using HTML5, does not show maxlength attribute with validating lenght text attribute' do
507
+ swap SimpleForm, :html5 => false do
508
+ with_input_for @validating_user, :name, :string
509
+ assert_no_select 'input.string[maxlength]'
510
+ end
511
+ end
512
+
396
513
  test 'input should generate a file field' do
397
514
  with_input_for @user, :name, :file
398
515
  assert_select 'input#user_name[type=file]'
@@ -403,14 +520,6 @@ class InputTest < ActionView::TestCase
403
520
  assert_no_select 'input[placeholder]'
404
521
  end
405
522
 
406
- test 'mapping input should generate an error if type is not found' do
407
- with_concat_form_for(@user) do |f|
408
- assert_raise(RuntimeError, "Could not find method for nil") do
409
- SimpleForm::Inputs::MappingInput.new(f, "unknown", nil, nil, {}).input
410
- end
411
- end
412
- end
413
-
414
523
  # HiddenInput
415
524
  test 'input should generate a hidden field' do
416
525
  with_input_for @user, :name, :hidden
@@ -674,6 +783,62 @@ class InputTest < ActionView::TestCase
674
783
  assert_select 'select option[value=2]', 'Carlos'
675
784
  end
676
785
 
786
+ test 'input should disable the anothers components when the option is a object' do
787
+ with_input_for @user, :description, :select, :collection => ["Jose", "Carlos"], :disabled => true
788
+ assert_no_select 'select option[value=Jose][disabled=disabled]', 'Jose'
789
+ assert_no_select 'select option[value=Carlos][disabled=disabled]', 'Carlos'
790
+ assert_select 'select[disabled=disabled]'
791
+ assert_select 'div.disabled'
792
+ end
793
+
794
+ test 'input should not disable the anothers components when the option is a object' do
795
+ with_input_for @user, :description, :select, :collection => ["Jose", "Carlos"], :disabled => 'Jose'
796
+ assert_select 'select option[value=Jose][disabled=disabled]', 'Jose'
797
+ assert_no_select 'select option[value=Carlos][disabled=disabled]', 'Carlos'
798
+ assert_no_select 'select[disabled=disabled]'
799
+ assert_no_select 'div.disabled'
800
+ end
801
+
802
+ test 'input should allow disabled options with a lambda for collection select' do
803
+ with_input_for @user, :name, :select, :collection => ["Carlos", "Antonio"],
804
+ :disabled => lambda { |x| x == "Carlos" }
805
+ assert_select 'select option[value=Carlos][disabled=disabled]', 'Carlos'
806
+ assert_select 'select option[value=Antonio]', 'Antonio'
807
+ assert_no_select 'select option[value=Antonio][disabled]'
808
+ end
809
+
810
+ test 'input should allow disabled and label method with lambdas for collection select' do
811
+ with_input_for @user, :name, :select, :collection => ["Carlos", "Antonio"],
812
+ :disabled => lambda { |x| x == "Carlos" }, :label_method => lambda { |x| x.upcase }
813
+ assert_select 'select option[value=Carlos][disabled=disabled]', 'CARLOS'
814
+ assert_select 'select option[value=Antonio]', 'ANTONIO'
815
+ assert_no_select 'select option[value=Antonio][disabled]'
816
+ end
817
+
818
+ test 'input should allow a non lambda disabled option with lambda label method for collections' do
819
+ with_input_for @user, :name, :select, :collection => ["Carlos", "Antonio"],
820
+ :disabled => "Carlos", :label_method => lambda { |x| x.upcase }
821
+ assert_select 'select option[value=Carlos][disabled=disabled]', 'CARLOS'
822
+ assert_select 'select option[value=Antonio]', 'ANTONIO'
823
+ assert_no_select 'select option[value=Antonio][disabled]'
824
+ end
825
+
826
+ test 'input should allow selected and label method with lambdas for collection select' do
827
+ with_input_for @user, :name, :select, :collection => ["Carlos", "Antonio"],
828
+ :selected => lambda { |x| x == "Carlos" }, :label_method => lambda { |x| x.upcase }
829
+ assert_select 'select option[value=Carlos][selected=selected]', 'CARLOS'
830
+ assert_select 'select option[value=Antonio]', 'ANTONIO'
831
+ assert_no_select 'select option[value=Antonio][selected]'
832
+ end
833
+
834
+ test 'input should allow a non lambda selected option with lambda label method for collection select' do
835
+ with_input_for @user, :name, :select, :collection => ["Carlos", "Antonio"],
836
+ :selected => "Carlos", :label_method => lambda { |x| x.upcase }
837
+ assert_select 'select option[value=Carlos][selected=selected]', 'CARLOS'
838
+ assert_select 'select option[value=Antonio]', 'ANTONIO'
839
+ assert_no_select 'select option[value=Antonio][selected]'
840
+ end
841
+
677
842
  test 'input should allow overriding collection for radio types' do
678
843
  with_input_for @user, :name, :radio, :collection => ['Jose', 'Carlos']
679
844
  assert_select 'input[type=radio][value=Jose]'
@@ -42,7 +42,7 @@ class User
42
42
  :description, :created_at, :updated_at, :credit_limit, :password, :url,
43
43
  :delivery_time, :born_at, :special_company_id, :country, :tags, :tag_ids,
44
44
  :avatar, :home_picture, :email, :status, :residence_country, :phone_number,
45
- :post_count, :lock_version, :amount, :attempts
45
+ :post_count, :lock_version, :amount, :attempts, :action
46
46
 
47
47
  def initialize(options={})
48
48
  options.each do |key, value|
@@ -67,7 +67,7 @@ class User
67
67
  def column_for_attribute(attribute)
68
68
  column_type, limit = case attribute.to_sym
69
69
  when :name, :status, :password then [:string, 100]
70
- when :description then :text
70
+ when :description then [:text, 200]
71
71
  when :age then :integer
72
72
  when :credit_limit then [:decimal, 15]
73
73
  when :active then :boolean
@@ -79,6 +79,7 @@ class User
79
79
  when :home_picture then :string
80
80
  when :amount then :integer
81
81
  when :attempts then :integer
82
+ when :action then :string
82
83
  end
83
84
  Column.new(attribute, column_type, limit)
84
85
  end
@@ -129,6 +130,11 @@ class ValidatingUser < User
129
130
  validates :company, :presence => true
130
131
  validates :age, :presence => true, :if => Proc.new { |user| user.name }
131
132
  validates :amount, :presence => true, :unless => Proc.new { |user| user.age }
133
+
134
+ validates :action, :presence => true, :on => :create
135
+ validates :credit_limit, :presence => true, :on => :save
136
+ validates :phone_number, :presence => true, :on => :update
137
+
132
138
  validates_numericality_of :age,
133
139
  :greater_than_or_equal_to => 18,
134
140
  :less_than_or_equal_to => 99,
@@ -141,6 +147,8 @@ class ValidatingUser < User
141
147
  :greater_than_or_equal_to => :min_attempts,
142
148
  :less_than_or_equal_to => :max_attempts,
143
149
  :only_integer => true
150
+ validates_length_of :name, :maximum => 25
151
+ validates_length_of :description, :maximum => 50
144
152
 
145
153
  def min_amount
146
154
  10
@@ -173,4 +181,17 @@ class OtherValidatingUser < User
173
181
  :greater_than_or_equal_to => Proc.new { |user| user.age },
174
182
  :less_than_or_equal_to => Proc.new { |user| user.age + 100},
175
183
  :only_integer => true
184
+
185
+ validates_format_of :country, :with => /\w+/
186
+ end
187
+
188
+ class HashBackedAuthor < Hash
189
+ extend ActiveModel::Naming
190
+ include ActiveModel::Conversion
191
+
192
+ def persisted?; false; end
193
+
194
+ def name
195
+ 'hash backed author'
196
+ end
176
197
  end