activerecord 2.3.4 → 2.3.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (37) hide show
  1. data/CHANGELOG +8 -0
  2. data/Rakefile +1 -1
  3. data/lib/active_record/associations.rb +10 -7
  4. data/lib/active_record/associations/association_proxy.rb +7 -8
  5. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -2
  6. data/lib/active_record/associations/has_one_association.rb +9 -0
  7. data/lib/active_record/autosave_association.rb +32 -23
  8. data/lib/active_record/base.rb +7 -0
  9. data/lib/active_record/connection_adapters/mysql_adapter.rb +6 -2
  10. data/lib/active_record/fixtures.rb +1 -1
  11. data/lib/active_record/locking/optimistic.rb +0 -33
  12. data/lib/active_record/locking/pessimistic.rb +0 -22
  13. data/lib/active_record/nested_attributes.rb +101 -38
  14. data/lib/active_record/validations.rb +35 -35
  15. data/lib/active_record/version.rb +1 -1
  16. data/lib/activerecord.rb +1 -0
  17. data/test/cases/associations/has_many_associations_test.rb +12 -0
  18. data/test/cases/associations/has_many_through_associations_test.rb +22 -0
  19. data/test/cases/associations/has_one_associations_test.rb +21 -0
  20. data/test/cases/autosave_association_test.rb +230 -11
  21. data/test/cases/base_test.rb +2 -0
  22. data/test/cases/connection_test_mysql.rb +8 -0
  23. data/test/cases/fixtures_test.rb +2 -2
  24. data/test/cases/locking_test.rb +0 -18
  25. data/test/cases/nested_attributes_test.rb +109 -37
  26. data/test/cases/reflection_test.rb +3 -3
  27. data/test/cases/validations_i18n_test.rb +8 -0
  28. data/test/cases/validations_test.rb +37 -9
  29. data/test/fixtures/accounts.yml +1 -0
  30. data/test/fixtures/fixture_database.sqlite3 +0 -0
  31. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  32. data/test/models/company.rb +10 -0
  33. data/test/models/pirate.rb +9 -2
  34. data/test/models/treasure.rb +2 -0
  35. data/test/schema/mysql_specific_schema.rb +12 -0
  36. data/test/schema/schema.rb +1 -0
  37. metadata +4 -4
@@ -474,6 +474,7 @@ class BasicsTest < ActiveRecord::TestCase
474
474
  topic = Topic.find(1)
475
475
  assert_equal topic, topic.delete, 'topic.delete did not return self'
476
476
  assert topic.frozen?, 'topic not frozen after delete'
477
+ assert topic.destroyed?, 'topic not marked as being destroyed'
477
478
  assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
478
479
  end
479
480
 
@@ -486,6 +487,7 @@ class BasicsTest < ActiveRecord::TestCase
486
487
  topic = Topic.find(1)
487
488
  assert_equal topic, topic.destroy, 'topic.destroy did not return self'
488
489
  assert topic.frozen?, 'topic not frozen after destroy'
490
+ assert topic.destroyed?, 'topic not marked as being destroyed'
489
491
  assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
490
492
  end
491
493
 
@@ -41,6 +41,14 @@ class MysqlConnectionTest < ActiveRecord::TestCase
41
41
  sleep 2
42
42
  @connection.verify!
43
43
  assert @connection.active?
44
+ end
45
+
46
+ # Test that MySQL allows multiple results for stored procedures
47
+ if Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
48
+ def test_multi_results
49
+ rows = ActiveRecord::Base.connection.select_rows('CALL ten();')
50
+ assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}"
51
+ end
44
52
  end
45
53
 
46
54
  private
@@ -519,8 +519,8 @@ class FoxyFixturesTest < ActiveRecord::TestCase
519
519
  end
520
520
 
521
521
  def test_identifies_consistently
522
- assert_equal 1281023246, Fixtures.identify(:ruby)
523
- assert_equal 2140105598, Fixtures.identify(:sapphire_2)
522
+ assert_equal 207281424, Fixtures.identify(:ruby)
523
+ assert_equal 1066363776, Fixtures.identify(:sapphire_2)
524
524
  end
525
525
 
526
526
  TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on)
@@ -38,24 +38,6 @@ class OptimisticLockingTest < ActiveRecord::TestCase
38
38
  assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
39
39
  end
40
40
 
41
- def test_lock_destroy
42
- p1 = Person.find(1)
43
- p2 = Person.find(1)
44
- assert_equal 0, p1.lock_version
45
- assert_equal 0, p2.lock_version
46
-
47
- p1.first_name = 'stu'
48
- p1.save!
49
- assert_equal 1, p1.lock_version
50
- assert_equal 0, p2.lock_version
51
-
52
- assert_raises(ActiveRecord::StaleObjectError) { p2.destroy }
53
-
54
- assert p1.destroy
55
- assert_equal true, p1.frozen?
56
- assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) }
57
- end
58
-
59
41
  def test_lock_repeating
60
42
  p1 = Person.find(1)
61
43
  p2 = Person.find(1)
@@ -26,13 +26,13 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
26
26
  Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
27
27
  end
28
28
 
29
- def test_base_should_have_an_empty_reject_new_nested_attributes_procs
30
- assert_equal Hash.new, ActiveRecord::Base.reject_new_nested_attributes_procs
29
+ def test_base_should_have_an_empty_nested_attributes_options
30
+ assert_equal Hash.new, ActiveRecord::Base.nested_attributes_options
31
31
  end
32
32
 
33
- def test_should_add_a_proc_to_reject_new_nested_attributes_procs
33
+ def test_should_add_a_proc_to_nested_attributes_options
34
34
  [:parrots, :birds].each do |name|
35
- assert_instance_of Proc, Pirate.reject_new_nested_attributes_procs[name]
35
+ assert_instance_of Proc, Pirate.nested_attributes_options[name][:reject_if]
36
36
  end
37
37
  end
38
38
 
@@ -49,24 +49,66 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
49
49
  ship = pirate.create_ship(:name => 'Nights Dirty Lightning')
50
50
 
51
51
  assert_no_difference('Ship.count') do
52
- pirate.update_attributes(:ship_attributes => { '_delete' => true })
52
+ pirate.update_attributes(:ship_attributes => { '_destroy' => true })
53
53
  end
54
54
  end
55
55
 
56
- def test_a_model_should_respond_to_underscore_delete_and_return_if_it_is_marked_for_destruction
56
+ def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction
57
57
  ship = Ship.create!(:name => 'Nights Dirty Lightning')
58
- assert !ship._delete
58
+ assert !ship._destroy
59
59
  ship.mark_for_destruction
60
- assert ship._delete
60
+ assert ship._destroy
61
+ end
62
+
63
+ def test_underscore_delete_is_deprecated
64
+ ActiveSupport::Deprecation.expects(:warn)
65
+ ship = Ship.create!(:name => 'Nights Dirty Lightning')
66
+ ship._delete
67
+ end
68
+
69
+ def test_reject_if_method_without_arguments
70
+ Pirate.accepts_nested_attributes_for :ship, :reject_if => :new_record?
71
+
72
+ pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
73
+ pirate.ship_attributes = { :name => 'Black Pearl' }
74
+ assert_no_difference('Ship.count') { pirate.save! }
75
+ end
76
+
77
+ def test_reject_if_method_with_arguments
78
+ Pirate.accepts_nested_attributes_for :ship, :reject_if => :reject_empty_ships_on_create
79
+
80
+ pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
81
+ pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
82
+ assert_no_difference('Ship.count') { pirate.save! }
83
+
84
+ # pirate.reject_empty_ships_on_create returns false for saved records
85
+ pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
86
+ assert_difference('Ship.count') { pirate.save! }
87
+ end
88
+
89
+ def test_reject_if_with_indifferent_keys
90
+ Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| attributes[:name].blank? }
91
+
92
+ pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
93
+ pirate.ship_attributes = { :name => 'Hello Pearl' }
94
+ assert_difference('Ship.count') { pirate.save! }
61
95
  end
62
96
  end
63
97
 
64
98
  class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
99
+ include AssertRaiseWithMessage
100
+
65
101
  def setup
66
102
  @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
67
103
  @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
68
104
  end
69
105
 
106
+ def test_should_raise_argument_error_if_trying_to_build_polymorphic_belongs_to
107
+ assert_raise_with_message ArgumentError, "Cannot build association looter. Are you trying to build a polymorphic one-to-one association?" do
108
+ Treasure.new(:name => 'pearl', :looter_attributes => {:catchphrase => "Arrr"})
109
+ end
110
+ end
111
+
70
112
  def test_should_define_an_attribute_writer_method_for_the_association
71
113
  assert_respond_to @pirate, :ship_attributes=
72
114
  end
@@ -79,9 +121,9 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
79
121
  assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
80
122
  end
81
123
 
82
- def test_should_not_build_a_new_record_if_there_is_no_id_and_delete_is_truthy
124
+ def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
83
125
  @ship.destroy
84
- @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_delete => '1' }
126
+ @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
85
127
 
86
128
  assert_nil @pirate.ship
87
129
  end
@@ -101,8 +143,8 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
101
143
  assert_equal 'Nights Dirty Lightning', @ship.name
102
144
  end
103
145
 
104
- def test_should_not_replace_an_existing_record_if_there_is_no_id_and_delete_is_truthy
105
- @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_delete => '1' }
146
+ def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
147
+ @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
106
148
 
107
149
  assert_equal @ship, @pirate.ship
108
150
  assert_equal 'Nights Dirty Lightning', @pirate.ship.name
@@ -129,29 +171,29 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
129
171
  assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
130
172
  end
131
173
 
132
- def test_should_delete_an_existing_record_if_there_is_a_matching_id_and_delete_is_truthy
174
+ def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
133
175
  @pirate.ship.destroy
134
176
  [1, '1', true, 'true'].each do |truth|
135
177
  @pirate.reload.create_ship(:name => 'Mister Pablo')
136
178
  assert_difference('Ship.count', -1) do
137
- @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => truth })
179
+ @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => truth })
138
180
  end
139
181
  end
140
182
  end
141
183
 
142
- def test_should_not_delete_an_existing_record_if_delete_is_not_truthy
184
+ def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
143
185
  [nil, '0', 0, 'false', false].each do |not_truth|
144
186
  assert_no_difference('Ship.count') do
145
- @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => not_truth })
187
+ @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => not_truth })
146
188
  end
147
189
  end
148
190
  end
149
191
 
150
- def test_should_not_delete_an_existing_record_if_allow_destroy_is_false
192
+ def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
151
193
  Pirate.accepts_nested_attributes_for :ship, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? }
152
194
 
153
195
  assert_no_difference('Ship.count') do
154
- @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => '1' })
196
+ @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => '1' })
155
197
  end
156
198
 
157
199
  Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
@@ -174,7 +216,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
174
216
 
175
217
  def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
176
218
  assert_no_difference('Ship.count') do
177
- @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_delete => '1' } }
219
+ @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_destroy => '1' } }
178
220
  end
179
221
  assert_difference('Ship.count', -1) do
180
222
  @pirate.save
@@ -205,9 +247,9 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
205
247
  assert_equal 'Arr', @ship.pirate.catchphrase
206
248
  end
207
249
 
208
- def test_should_not_build_a_new_record_if_there_is_no_id_and_delete_is_truthy
250
+ def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
209
251
  @pirate.destroy
210
- @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_delete => '1' }
252
+ @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
211
253
 
212
254
  assert_nil @ship.pirate
213
255
  end
@@ -227,8 +269,8 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
227
269
  assert_equal 'Aye', @pirate.catchphrase
228
270
  end
229
271
 
230
- def test_should_not_replace_an_existing_record_if_there_is_no_id_and_delete_is_truthy
231
- @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_delete => '1' }
272
+ def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
273
+ @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
232
274
 
233
275
  assert_equal @pirate, @ship.pirate
234
276
  assert_equal 'Aye', @ship.pirate.catchphrase
@@ -255,29 +297,29 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
255
297
  assert_equal 'Arr', @ship.pirate.catchphrase
256
298
  end
257
299
 
258
- def test_should_delete_an_existing_record_if_there_is_a_matching_id_and_delete_is_truthy
300
+ def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
259
301
  @ship.pirate.destroy
260
302
  [1, '1', true, 'true'].each do |truth|
261
303
  @ship.reload.create_pirate(:catchphrase => 'Arr')
262
304
  assert_difference('Pirate.count', -1) do
263
- @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => truth })
305
+ @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => truth })
264
306
  end
265
307
  end
266
308
  end
267
309
 
268
- def test_should_not_delete_an_existing_record_if_delete_is_not_truthy
310
+ def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
269
311
  [nil, '0', 0, 'false', false].each do |not_truth|
270
312
  assert_no_difference('Pirate.count') do
271
- @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => not_truth })
313
+ @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => not_truth })
272
314
  end
273
315
  end
274
316
  end
275
317
 
276
- def test_should_not_delete_an_existing_record_if_allow_destroy_is_false
318
+ def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
277
319
  Ship.accepts_nested_attributes_for :pirate, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? }
278
320
 
279
321
  assert_no_difference('Pirate.count') do
280
- @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => '1' })
322
+ @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => '1' })
281
323
  end
282
324
 
283
325
  Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
@@ -293,7 +335,7 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
293
335
 
294
336
  def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
295
337
  assert_no_difference('Pirate.count') do
296
- @ship.attributes = { :pirate_attributes => { :id => @ship.pirate.id, '_delete' => true } }
338
+ @ship.attributes = { :pirate_attributes => { :id => @ship.pirate.id, '_destroy' => true } }
297
339
  end
298
340
  assert_difference('Pirate.count', -1) { @ship.save }
299
341
  end
@@ -361,18 +403,18 @@ module NestedAttributesOnACollectionAssociationTests
361
403
  assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
362
404
  end
363
405
 
364
- def test_should_not_assign_delete_key_to_a_record
406
+ def test_should_not_assign_destroy_key_to_a_record
365
407
  assert_nothing_raised ActiveRecord::UnknownAttributeError do
366
- @pirate.send(association_setter, { 'foo' => { '_delete' => '0' }})
408
+ @pirate.send(association_setter, { 'foo' => { '_destroy' => '0' }})
367
409
  end
368
410
  end
369
411
 
370
- def test_should_ignore_new_associated_records_with_truthy_delete_attribute
412
+ def test_should_ignore_new_associated_records_with_truthy_destroy_attribute
371
413
  @pirate.send(@association_name).destroy_all
372
414
  @pirate.reload.attributes = {
373
415
  association_getter => {
374
416
  'foo' => { :name => 'Grace OMalley' },
375
- 'bar' => { :name => 'Privateers Greed', '_delete' => '1' }
417
+ 'bar' => { :name => 'Privateers Greed', '_destroy' => '1' }
376
418
  }
377
419
  }
378
420
 
@@ -424,7 +466,7 @@ module NestedAttributesOnACollectionAssociationTests
424
466
  ['1', 1, 'true', true].each do |true_variable|
425
467
  record = @pirate.reload.send(@association_name).create!(:name => 'Grace OMalley')
426
468
  @pirate.send(association_setter,
427
- @alternate_params[association_getter].merge('baz' => { :id => record.id, '_delete' => true_variable })
469
+ @alternate_params[association_getter].merge('baz' => { :id => record.id, '_destroy' => true_variable })
428
470
  )
429
471
 
430
472
  assert_difference('@pirate.send(@association_name).count', -1) do
@@ -435,7 +477,7 @@ module NestedAttributesOnACollectionAssociationTests
435
477
 
436
478
  def test_should_not_destroy_the_associated_model_with_a_non_truthy_argument
437
479
  [nil, '', '0', 0, 'false', false].each do |false_variable|
438
- @alternate_params[association_getter]['foo']['_delete'] = false_variable
480
+ @alternate_params[association_getter]['foo']['_destroy'] = false_variable
439
481
  assert_no_difference('@pirate.send(@association_name).count') do
440
482
  @pirate.update_attributes(@alternate_params)
441
483
  end
@@ -444,7 +486,7 @@ module NestedAttributesOnACollectionAssociationTests
444
486
 
445
487
  def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
446
488
  assert_no_difference('@pirate.send(@association_name).count') do
447
- @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_delete' => true }))
489
+ @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_destroy' => true }))
448
490
  end
449
491
  assert_difference('@pirate.send(@association_name).count', -1) { @pirate.save }
450
492
  end
@@ -507,3 +549,33 @@ class TestNestedAttributesOnAHasAndBelongsToManyAssociation < ActiveRecord::Test
507
549
 
508
550
  include NestedAttributesOnACollectionAssociationTests
509
551
  end
552
+
553
+ class TestNestedAttributesLimit < ActiveRecord::TestCase
554
+ def setup
555
+ Pirate.accepts_nested_attributes_for :parrots, :limit => 2
556
+
557
+ @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
558
+ end
559
+
560
+ def teardown
561
+ Pirate.accepts_nested_attributes_for :parrots, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
562
+ end
563
+
564
+ def test_limit_with_less_records
565
+ @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Big Big Love' } } }
566
+ assert_difference('Parrot.count') { @pirate.save! }
567
+ end
568
+
569
+ def test_limit_with_number_exact_records
570
+ @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Lovely Day' }, 'bar' => { :name => 'Blown Away' } } }
571
+ assert_difference('Parrot.count', 2) { @pirate.save! }
572
+ end
573
+
574
+ def test_limit_with_exceeding_records
575
+ assert_raises(ActiveRecord::NestedAttributes::TooManyRecords) do
576
+ @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Lovely Day' },
577
+ 'bar' => { :name => 'Blown Away' },
578
+ 'car' => { :name => 'The Happening' }} }
579
+ end
580
+ end
581
+ end
@@ -170,9 +170,9 @@ class ReflectionTest < ActiveRecord::TestCase
170
170
 
171
171
  def test_reflection_of_all_associations
172
172
  # FIXME these assertions bust a lot
173
- assert_equal 30, Firm.reflect_on_all_associations.size
174
- assert_equal 23, Firm.reflect_on_all_associations(:has_many).size
175
- assert_equal 7, Firm.reflect_on_all_associations(:has_one).size
173
+ assert_equal 36, Firm.reflect_on_all_associations.size
174
+ assert_equal 26, Firm.reflect_on_all_associations(:has_many).size
175
+ assert_equal 10, Firm.reflect_on_all_associations(:has_one).size
176
176
  assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
177
177
  end
178
178
 
@@ -651,6 +651,14 @@ class ActiveRecordErrorI18nTests < ActiveSupport::TestCase
651
651
  assert_full_message 'Title is kaputt?!', :title, :kaputt, :message => :broken
652
652
  end
653
653
 
654
+ test "#full_message with different scope" do
655
+ store_translations(:my_errors => { :messages => { :kaputt => 'is kaputt' } })
656
+ assert_full_message 'Title is kaputt', :title, :kaputt, :scope => [:activerecord, :my_errors]
657
+
658
+ store_translations(:my_errors => { :full_messages => { :kaputt => '{{attribute}} {{message}}!' } })
659
+ assert_full_message 'Title is kaputt!', :title, :kaputt, :scope => [:activerecord, :my_errors]
660
+ end
661
+
654
662
  # switch locales
655
663
 
656
664
  test "#message allows to switch locales" do
@@ -28,6 +28,12 @@ class ProtectedPerson < ActiveRecord::Base
28
28
  set_table_name 'people'
29
29
  attr_accessor :addon
30
30
  attr_protected :first_name
31
+
32
+ def special_error
33
+ this_method_does_not_exist!
34
+ rescue
35
+ errors.add(:special_error, "This method does not exist")
36
+ end
31
37
  end
32
38
 
33
39
  class UniqueReply < Reply
@@ -172,6 +178,14 @@ class ValidationsTest < ActiveRecord::TestCase
172
178
  end
173
179
  end
174
180
 
181
+ def test_values_are_not_retrieved_unless_needed
182
+ assert_nothing_raised do
183
+ person = ProtectedPerson.new
184
+ person.special_error
185
+ assert_equal "This method does not exist", person.errors[:special_error]
186
+ end
187
+ end
188
+
175
189
  def test_single_error_per_attr_iteration
176
190
  r = Reply.new
177
191
  r.save
@@ -905,14 +919,18 @@ class ValidationsTest < ActiveRecord::TestCase
905
919
  end
906
920
 
907
921
  def test_validates_length_with_globally_modified_error_message
908
- ActiveSupport::Deprecation.silence do
909
- ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}'
910
- end
922
+ defaults = ActiveSupport::Deprecation.silence { ActiveRecord::Errors.default_error_messages }
923
+ original_message = defaults[:too_short]
924
+ defaults[:too_short] = 'tu est trops petit hombre {{count}}'
925
+
911
926
  Topic.validates_length_of :title, :minimum => 10
912
927
  t = Topic.create(:title => 'too short')
913
928
  assert !t.valid?
914
929
 
915
930
  assert_equal 'tu est trops petit hombre 10', t.errors['title']
931
+
932
+ ensure
933
+ defaults[:too_short] = original_message
916
934
  end
917
935
 
918
936
  def test_validates_size_of_association
@@ -1432,12 +1450,22 @@ class ValidationsTest < ActiveRecord::TestCase
1432
1450
  end
1433
1451
 
1434
1452
  def test_validation_order
1435
- Topic.validates_presence_of :title
1436
- Topic.validates_length_of :title, :minimum => 2
1437
-
1438
- t = Topic.new("title" => "")
1439
- assert !t.valid?
1440
- assert_equal "can't be blank", t.errors.on("title").first
1453
+ Topic.validates_presence_of :title, :author_name
1454
+ Topic.validate {|topic| topic.errors.add('author_email_address', 'will never be valid')}
1455
+ Topic.validates_length_of :title, :content, :minimum => 2
1456
+
1457
+ t = Topic.new :title => ''
1458
+ t.valid?
1459
+ e = t.errors.instance_variable_get '@errors'
1460
+ assert_equal 'title', key = e.keys.first
1461
+ assert_equal "can't be blank", t.errors.on(key).first
1462
+ assert_equal 'is too short (minimum is 2 characters)', t.errors.on(key).second
1463
+ assert_equal 'author_name', key = e.keys.second
1464
+ assert_equal "can't be blank", t.errors.on(key)
1465
+ assert_equal 'author_email_address', key = e.keys.third
1466
+ assert_equal 'will never be valid', t.errors.on(key)
1467
+ assert_equal 'content', key = e.keys.fourth
1468
+ assert_equal 'is too short (minimum is 2 characters)', t.errors.on(key)
1441
1469
  end
1442
1470
 
1443
1471
  def test_invalid_should_be_the_opposite_of_valid