client_side_validations 3.0.4 → 3.0.5

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.
Files changed (53) hide show
  1. data/client_side_validations.gemspec +2 -3
  2. data/javascript/rails.validations.js +26 -12
  3. data/lib/client_side_validations.rb +2 -0
  4. data/lib/client_side_validations/action_view/form_builder.rb +55 -5
  5. data/lib/client_side_validations/action_view/form_helper.rb +20 -2
  6. data/lib/client_side_validations/active_model.rb +22 -13
  7. data/lib/client_side_validations/active_model/length.rb +12 -10
  8. data/lib/client_side_validations/active_model/numericality.rb +14 -9
  9. data/lib/client_side_validations/active_record/middleware.rb +5 -1
  10. data/lib/client_side_validations/active_record/uniqueness.rb +10 -8
  11. data/lib/client_side_validations/middleware.rb +2 -0
  12. data/lib/client_side_validations/mongo_mapper.rb +9 -0
  13. data/lib/client_side_validations/mongo_mapper/middleware.rb +20 -0
  14. data/lib/client_side_validations/mongo_mapper/uniqueness.rb +28 -0
  15. data/lib/client_side_validations/mongoid/uniqueness.rb +10 -8
  16. data/lib/client_side_validations/version.rb +1 -1
  17. data/test/action_view/cases/helper.rb +7 -1
  18. data/test/action_view/cases/test_helpers.rb +263 -0
  19. data/test/action_view/cases/test_legacy_helpers.rb +11 -0
  20. data/test/active_model/cases/test_validations.rb +21 -8
  21. data/test/active_record/cases/test_middleware.rb +25 -0
  22. data/test/active_record/cases/test_uniqueness_validator.rb +5 -0
  23. data/test/active_record/models/user.rb +4 -0
  24. data/test/formtastic/cases/test_form_helper.rb +1 -1
  25. data/test/javascript/public/test/callbacks/elementAfter.js +1 -1
  26. data/test/javascript/public/test/callbacks/elementBefore.js +1 -1
  27. data/test/javascript/public/test/callbacks/elementFail.js +1 -1
  28. data/test/javascript/public/test/callbacks/elementPass.js +1 -1
  29. data/test/javascript/public/test/callbacks/formAfter.js +1 -1
  30. data/test/javascript/public/test/callbacks/formBefore.js +1 -1
  31. data/test/javascript/public/test/callbacks/formFail.js +1 -1
  32. data/test/javascript/public/test/callbacks/formPass.js +1 -1
  33. data/test/javascript/public/test/form_builders/validateForm.js +1 -1
  34. data/test/javascript/public/test/form_builders/validateFormtastic.js +1 -1
  35. data/test/javascript/public/test/form_builders/validateNestedForm.js +66 -0
  36. data/test/javascript/public/test/form_builders/validateSimpleForm.js +1 -1
  37. data/test/javascript/public/test/validateElement.js +36 -1
  38. data/test/javascript/public/test/validators/length.js +7 -1
  39. data/test/javascript/public/test/validators/numericality.js +7 -0
  40. data/test/javascript/public/test/validators/presence.js +6 -0
  41. data/test/javascript/public/test/validators/uniqueness.js +8 -1
  42. data/test/javascript/server.rb +7 -1
  43. data/test/javascript/views/index.erb +1 -1
  44. data/test/mongo_mapper/cases/helper.rb +9 -0
  45. data/test/mongo_mapper/cases/test_base.rb +15 -0
  46. data/test/mongo_mapper/cases/test_middleware.rb +77 -0
  47. data/test/mongo_mapper/cases/test_uniqueness_validator.rb +50 -0
  48. data/test/mongo_mapper/models/magazine.rb +11 -0
  49. data/test/mongoid/cases/test_middleware.rb +9 -0
  50. data/test/mongoid/cases/test_uniqueness_validator.rb +5 -0
  51. data/test/mongoid/models/book.rb +4 -0
  52. data/test/simple_form/cases/test_form_helper.rb +1 -1
  53. metadata +46 -20
@@ -0,0 +1,28 @@
1
+ module ClientSideValidations::MongoMapper
2
+ module Uniqueness
3
+ def client_side_hash(model, attribute)
4
+ hash = {}
5
+ hash[:message] = model.errors.generate_message(attribute, message_type, options.except(:scope))
6
+ hash[:case_sensitive] = options[:case_sensitive] if options.key?(:case_sensitive)
7
+ hash[:id] = model.id unless model.new_record?
8
+ if options.key?(:scope) && options[:scope].present?
9
+ hash[:scope] = Array.wrap(options[:scope]).inject({}) do |scope_hash, scope_item|
10
+ scope_hash.merge!(scope_item => model.send(scope_item))
11
+ end
12
+ end
13
+
14
+ unless model.class.name.demodulize == model.class.name
15
+ hash[:class] = model.class.name.underscore
16
+ end
17
+
18
+ hash
19
+ end
20
+
21
+ private
22
+
23
+ def message_type
24
+ :taken
25
+ end
26
+ end
27
+ end
28
+
@@ -1,16 +1,18 @@
1
1
  module ClientSideValidations::Mongoid
2
2
  module Uniqueness
3
3
  def client_side_hash(model, attribute)
4
- extra_options = options.except(*::ActiveModel::Errors::CALLBACKS_OPTIONS - [:on, :allow_blank])
5
- hash = { :message => model.errors.generate_message(attribute, message_type, extra_options.except(:case_sensitive, :scope)) }
6
- hash = hash.merge(extra_options).merge(model.new_record? ? {} : { :id => model.id })
7
-
8
- if hash[:scope].present?
9
- hash[:scope] = Array.wrap(hash[:scope]).inject({}) do |scope_hash, scope_item|
4
+ hash = {}
5
+ hash[:message] = model.errors.generate_message(attribute, message_type, options.except(:scope))
6
+ hash[:case_sensitive] = options[:case_sensitive] if options.key?(:case_sensitive)
7
+ hash[:id] = model.id unless model.new_record?
8
+ if options.key?(:scope) && options[:scope].present?
9
+ hash[:scope] = Array.wrap(options[:scope]).inject({}) do |scope_hash, scope_item|
10
10
  scope_hash.merge!(scope_item => model.send(scope_item))
11
11
  end
12
- else
13
- hash.delete(:scope)
12
+ end
13
+
14
+ unless model.class.name.demodulize == model.class.name
15
+ hash[:class] = model.class.name.underscore
14
16
  end
15
17
 
16
18
  hash
@@ -1,3 +1,3 @@
1
1
  module ClientSideValidations
2
- VERSION = "3.0.4"
2
+ VERSION = "3.0.5"
3
3
  end
@@ -67,6 +67,8 @@ module ActionViewTestSetup
67
67
  @post.body = "Back to the hill and over it again!"
68
68
  @post.secret = 1
69
69
  @post.written_on = Date.new(2004, 6, 15)
70
+
71
+ @_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
70
72
  end
71
73
 
72
74
  def url_for(object)
@@ -108,12 +110,16 @@ module ActionViewTestSetup
108
110
  html = form_text(action, id, html_class, remote, validators) + snowman(method) + (contents || "") + "</form>"
109
111
 
110
112
  if options.is_a?(Hash) && options[:validators]
111
- html + %Q{<script>var #{id} = #{client_side_form_settings_helper.merge(:validators => validators).to_json};</script>}
113
+ build_script_tag(html, id, options[:validators])
112
114
  else
113
115
  html
114
116
  end
115
117
  end
116
118
 
119
+ def build_script_tag(html, id, validators)
120
+ (html || "") + %Q{<script>window['#{id}'] = #{client_side_form_settings_helper.merge(:validators => validators).to_json};</script>}
121
+ end
122
+
117
123
  protected
118
124
  def comments_path(post)
119
125
  "/posts/#{post.id}/comments"
@@ -296,6 +296,18 @@ class ClientSideValidations::ActionViewHelpersTest < ActionView::TestCase
296
296
  assert_equal expected, output_buffer
297
297
  end
298
298
 
299
+ def test_select_multiple
300
+ form_for(@post, :validate => true) do |f|
301
+ concat f.select(:cost, [], {}, :multiple => true)
302
+ end
303
+
304
+ validators = {'post[cost][]' => {:presence => {:message => "can't be blank"}}}
305
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", :method => "put", :validators => validators) do
306
+ %{<select data-validate="true" id="post_cost" multiple="multiple" name="post[cost][]"></select>}
307
+ end
308
+ assert_equal expected, output_buffer
309
+ end
310
+
299
311
  def test_collection_select
300
312
  form_for(@post, :validate => true) do |f|
301
313
  concat f.collection_select(:cost, [], :id, :name)
@@ -333,5 +345,256 @@ class ClientSideValidations::ActionViewHelpersTest < ActionView::TestCase
333
345
  end
334
346
  assert_equal expected, output_buffer
335
347
  end
348
+
349
+ def test_conditional_validator_filters
350
+ hash = {
351
+ :cost => {
352
+ :presence => {
353
+ :message => "can't be blank",
354
+ :unless => :cannot_validate?
355
+ }
356
+ },
357
+ :title => {
358
+ :presence => {
359
+ :message => "can't be blank",
360
+ :if => :can_validate?
361
+ }
362
+ }
363
+ }
364
+
365
+ @post.title = nil
366
+ @post.stubs(:cannot_validate?).returns(false)
367
+ @post.stubs(:can_validate?).returns(true)
368
+ @post.stubs(:client_side_validation_hash).returns(hash)
369
+ form_for(@post, :validate => true) do |f|
370
+ concat f.text_field(:cost)
371
+ concat f.text_field(:title)
372
+ end
373
+
374
+ validators = {}
375
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", :method => "put", :validators => validators) do
376
+ %{<input id="post_cost" name="post[cost]" size="30" type="text" />} +
377
+ %{<input id="post_title" name="post[title]" size="30" type="text" />}
378
+ end
379
+ assert_equal expected, output_buffer
380
+ end
381
+
382
+ def test_conditional_validators_should_be_filtered
383
+ hash = {
384
+ :cost => {
385
+ :presence => {
386
+ :message => "can't be blank",
387
+ :unless => :cannot_validate?
388
+ }
389
+ },
390
+ :title => {
391
+ :presence => {
392
+ :message => "can't be blank",
393
+ :if => :can_validate?
394
+ }
395
+ }
396
+ }
397
+
398
+ @post.title = nil
399
+ @post.stubs(:cannot_validate?).returns(false)
400
+ @post.stubs(:can_validate?).returns(true)
401
+ @post.stubs(:client_side_validation_hash).returns(hash)
402
+ form_for(@post, :validate => true) do |f|
403
+ concat f.text_field(:cost)
404
+ concat f.text_field(:title)
405
+ end
406
+
407
+ validators = {}
408
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", :method => "put", :validators => validators) do
409
+ %{<input id="post_cost" name="post[cost]" size="30" type="text" />} +
410
+ %{<input id="post_title" name="post[title]" size="30" type="text" />}
411
+ end
412
+ assert_equal expected, output_buffer
413
+ end
414
+
415
+ def test_conditional_validator_filters_being_forced
416
+ hash = {
417
+ :cost => {
418
+ :presence => {
419
+ :message => "can't be blank",
420
+ :unless => :cannot_validate?
421
+ }
422
+ },
423
+ :title => {
424
+ :presence => {
425
+ :message => "can't be blank",
426
+ :if => :can_validate?
427
+ }
428
+ }
429
+ }
430
+
431
+ @post.title = nil
432
+ @post.stubs(:cannot_validate?).returns(false)
433
+ @post.stubs(:can_validate?).returns(true)
434
+ @post.stubs(:client_side_validation_hash).returns(hash)
435
+ form_for(@post, :validate => true) do |f|
436
+ concat f.text_field(:cost, :validate => true)
437
+ concat f.text_field(:title, :validate => true)
438
+ end
439
+
440
+ validators = {
441
+ 'post[cost]' => {:presence => {:message => "can't be blank"}},
442
+ 'post[title]' => {:presence => {:message => "can't be blank"}}
443
+ }
444
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", :method => "put", :validators => validators) do
445
+ %{<input data-validate="true" id="post_cost" name="post[cost]" size="30" type="text" />} +
446
+ %{<input data-validate="true" id="post_title" name="post[title]" size="30" type="text" />}
447
+ end
448
+ assert_equal expected, output_buffer
449
+ end
450
+
451
+ def test_conditional_validator_filters_being_forced_and_not_meeting_condition
452
+ hash = {
453
+ :cost => {
454
+ :presence => {
455
+ :message => "can't be blank",
456
+ :unless => :cannot_validate?
457
+ }
458
+ },
459
+ :title => {
460
+ :presence => {
461
+ :message => "can't be blank",
462
+ :if => :can_validate?
463
+ }
464
+ }
465
+ }
466
+
467
+ @post.title = nil
468
+ @post.stubs(:cannot_validate?).returns(true)
469
+ @post.stubs(:can_validate?).returns(false)
470
+ @post.stubs(:client_side_validation_hash).returns(hash)
471
+ form_for(@post, :validate => true) do |f|
472
+ concat f.text_field(:cost, :validate => true)
473
+ concat f.text_field(:title, :validate => true)
474
+ end
475
+
476
+ validators = {}
477
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", :method => "put", :validators => validators) do
478
+ %{<input id="post_cost" name="post[cost]" size="30" type="text" />} +
479
+ %{<input id="post_title" name="post[title]" size="30" type="text" />}
480
+ end
481
+ assert_equal expected, output_buffer
482
+ end
483
+
484
+ def test_conditional_validator_filters_being_forced_individually
485
+ hash = {
486
+ :cost => {
487
+ :presence => {
488
+ :message => "can't be blank",
489
+ :unless => :cannot_validate?
490
+ }
491
+ },
492
+ :title => {
493
+ :presence => {
494
+ :message => "can't be blank",
495
+ :if => :can_validate?
496
+ }
497
+ }
498
+ }
499
+
500
+ @post.title = nil
501
+ @post.stubs(:cannot_validate?).returns(false)
502
+ @post.stubs(:can_validate?).returns(true)
503
+ @post.stubs(:client_side_validation_hash).returns(hash)
504
+ form_for(@post, :validate => true) do |f|
505
+ concat f.text_field(:cost, :validate => { :presence => true })
506
+ concat f.text_field(:title, :validate => { :presence => true })
507
+ end
508
+
509
+ validators = {
510
+ 'post[cost]' => {:presence => {:message => "can't be blank"}},
511
+ 'post[title]' => {:presence => {:message => "can't be blank"}}
512
+ }
513
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", :method => "put", :validators => validators) do
514
+ %{<input data-validate="true" id="post_cost" name="post[cost]" size="30" type="text" />} +
515
+ %{<input data-validate="true" id="post_title" name="post[title]" size="30" type="text" />}
516
+ end
517
+ assert_equal expected, output_buffer
518
+ end
519
+
520
+ def test_conditional_validator_filters_being_forced_and_not_meeting_condition_individually
521
+ hash = {
522
+ :cost => {
523
+ :presence => {
524
+ :message => "can't be blank",
525
+ :unless => :cannot_validate?
526
+ }
527
+ },
528
+ :title => {
529
+ :presence => {
530
+ :message => "can't be blank",
531
+ :if => :can_validate?
532
+ }
533
+ }
534
+ }
535
+
536
+ @post.title = nil
537
+ @post.stubs(:cannot_validate?).returns(true)
538
+ @post.stubs(:can_validate?).returns(false)
539
+ @post.stubs(:client_side_validation_hash).returns(hash)
540
+ form_for(@post, :validate => true) do |f|
541
+ concat f.text_field(:cost, :validate => { :presence => true })
542
+ concat f.text_field(:title, :validate => { :presence => true })
543
+ end
544
+
545
+ validators = {}
546
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", :method => "put", :validators => validators) do
547
+ %{<input id="post_cost" name="post[cost]" size="30" type="text" />} +
548
+ %{<input id="post_title" name="post[title]" size="30" type="text" />}
549
+ end
550
+ assert_equal expected, output_buffer
551
+ end
552
+
553
+ def test_conditional_validator_filters_being_forced_with_procs
554
+ hash = {
555
+ :cost => {
556
+ :presence => {
557
+ :message => "can't be blank",
558
+ :unless => Proc.new { |post| post.cannot_validate? }
559
+ }
560
+ },
561
+ :title => {
562
+ :presence => {
563
+ :message => "can't be blank",
564
+ :if => Proc.new { |post| post.can_validate? }
565
+ }
566
+ }
567
+ }
568
+
569
+ @post.title = nil
570
+ @post.stubs(:cannot_validate?).returns(false)
571
+ @post.stubs(:can_validate?).returns(true)
572
+ @post.stubs(:client_side_validation_hash).returns(hash)
573
+ form_for(@post, :validate => true) do |f|
574
+ concat f.text_field(:cost, :validate => true)
575
+ concat f.text_field(:title, :validate => true)
576
+ end
577
+
578
+ validators = {
579
+ 'post[cost]' => {:presence => {:message => "can't be blank"}},
580
+ 'post[title]' => {:presence => {:message => "can't be blank"}}
581
+ }
582
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", :method => "put", :validators => validators) do
583
+ %{<input data-validate="true" id="post_cost" name="post[cost]" size="30" type="text" />} +
584
+ %{<input data-validate="true" id="post_title" name="post[title]" size="30" type="text" />}
585
+ end
586
+ assert_equal expected, output_buffer
587
+ end
588
+
589
+ def test_pushing_script_to_content_for
590
+ form_for(@post, :validate => :post) do |f|
591
+ concat f.text_field(:cost)
592
+ end
593
+
594
+ validators = {'post[cost]' => {:presence => {:message => "can't be blank"}}}
595
+ expected = %{<form accept-charset="UTF-8" action="/posts/123" class="edit_post" data-validate="true" id="edit_post_123" method="post" novalidate="novalidate"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /><input name="_method" type="hidden" value="put" /></div><input data-validate="true" id="post_cost" name="post[cost]" size="30" type="text" /></form>}
596
+ assert_equal expected, output_buffer
597
+ assert_equal build_script_tag(nil, "edit_post_123", validators), content_for(:post)
598
+ end
336
599
  end
337
600
 
@@ -168,6 +168,17 @@ class ClientSideValidations::LegacyActionViewHelpersTest < ActionView::TestCase
168
168
  assert_equal expected, output_buffer
169
169
  end
170
170
 
171
+ def test_select_multiple
172
+ form_for(@post) do |f|
173
+ concat f.select(:cost, [], {}, :multiple => true)
174
+ end
175
+
176
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", "put") do
177
+ %{<select id="post_cost" multiple="multiple" name="post[cost][]"></select>}
178
+ end
179
+ assert_equal expected, output_buffer
180
+ end
181
+
171
182
  def test_collection_select
172
183
  form_for(@post) do |f|
173
184
  concat f.collection_select(:cost, [], :id, :name)
@@ -138,24 +138,37 @@ class ActiveModel::ValidationsTest < ClientSideValidations::ActiveModelTestBase
138
138
  assert_equal expected_hash, person.client_side_validation_hash
139
139
  end
140
140
 
141
- def test_validators_with_if_or_unless_are_ignored
141
+ def test_generic_block_validators_should_be_ignored
142
142
  person = new_person do |p|
143
- p.validates_presence_of :first_name, :if => Proc.new { |p| true }
144
- p.validates_presence_of :last_name, :unless => Proc.new { |p| true }
143
+ p.validates_each(:first_name) do |record, attr, value|
144
+ record.errors.add(:first_name, "failed")
145
+ end
145
146
  end
146
147
 
147
148
  expected_hash = {}
148
149
  assert_equal expected_hash, person.client_side_validation_hash
149
150
  end
150
151
 
151
- def test_generic_block_validators_should_be_ignored
152
+ def test_conditionals_persist_on_validator
152
153
  person = new_person do |p|
153
- p.validates_each(:first_name) do |record, attr, value|
154
- record.errors.add(:first_name, "failed")
155
- end
154
+ p.validates :first_name, :presence => { :if => :can_validate? }
155
+ p.validates :last_name, :presence => { :unless => :cannot_validate? }
156
156
  end
157
157
 
158
- expected_hash = {}
158
+ expected_hash = {
159
+ :first_name => {
160
+ :presence => {
161
+ :message => "can't be blank",
162
+ :if => :can_validate?
163
+ }
164
+ },
165
+ :last_name => {
166
+ :presence => {
167
+ :message => "can't be blank",
168
+ :unless => :cannot_validate?
169
+ }
170
+ }
171
+ }
159
172
  assert_equal expected_hash, person.client_side_validation_hash
160
173
  end
161
174
  end
@@ -59,6 +59,24 @@ class ClientSideValidationsActiveRecordMiddlewareTest < Test::Unit::TestCase
59
59
  assert last_response.not_found?
60
60
  end
61
61
 
62
+ def test_mysql_adapter_uniqueness_when_id_is_given
63
+ user = User.create(:email => 'user@test.com')
64
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.
65
+ any_instance.expects(:instance_variable_get).
66
+ with("@config").
67
+ returns({:adapter => "mysql"})
68
+
69
+ sql_without_binary = "#{User.arel_table["email"].eq(user.email).to_sql} AND #{User.arel_table.primary_key.not_eq(user.id).to_sql}"
70
+ relation = Arel::Nodes::SqlLiteral.new("BINARY #{sql_without_binary}")
71
+
72
+ #NOTE: Stubs User#where because SQLite3 don't know BINARY
73
+ result = User.where(sql_without_binary)
74
+ User.expects(:where).with(relation).returns(result)
75
+
76
+ get '/validators/uniqueness.json', { 'user[email]' => user.email, 'case_sensitive' => true, 'id' => user.id}
77
+ assert_equal 'true', last_response.body
78
+ end
79
+
62
80
  def test_uniqueness_when_scope_is_given
63
81
  User.create(:email => 'user@test.com', :age => 25)
64
82
  get '/validators/uniqueness.json', { 'user[email]' => 'user@test.com', 'scope' => { 'age' => 30 }, 'case_sensitive' => true }
@@ -146,5 +164,12 @@ class ClientSideValidationsActiveRecordMiddlewareTest < Test::Unit::TestCase
146
164
  assert last_response.ok?
147
165
  end
148
166
 
167
+ def test_uniqueness_when_resource_is_a_nested_module
168
+ ActiveRecordTestModule::User2.create(:email => 'user@test.com')
169
+ get '/validators/uniqueness.json', { 'active_record_test_module/user2[email]' => 'user@test.com', 'case_sensitive' => true }
170
+
171
+ assert_equal 'false', last_response.body
172
+ assert last_response.ok?
173
+ end
149
174
  end
150
175