activerecord 2.3.5 → 2.3.6

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 (90) hide show
  1. data/CHANGELOG +33 -0
  2. data/Rakefile +1 -1
  3. data/examples/performance.sql +85 -0
  4. data/lib/active_record.rb +1 -2
  5. data/lib/active_record/association_preload.rb +9 -2
  6. data/lib/active_record/associations.rb +48 -38
  7. data/lib/active_record/associations/association_collection.rb +15 -11
  8. data/lib/active_record/associations/association_proxy.rb +16 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +11 -1
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -10
  11. data/lib/active_record/associations/has_many_association.rb +5 -0
  12. data/lib/active_record/associations/has_many_through_association.rb +5 -5
  13. data/lib/active_record/associations/has_one_association.rb +10 -1
  14. data/lib/active_record/attribute_methods.rb +5 -1
  15. data/lib/active_record/autosave_association.rb +66 -35
  16. data/lib/active_record/base.rb +77 -36
  17. data/lib/active_record/batches.rb +13 -9
  18. data/lib/active_record/calculations.rb +6 -3
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -3
  20. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -7
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +64 -10
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +2 -0
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +31 -1
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +31 -66
  28. data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -2
  29. data/lib/active_record/dirty.rb +2 -2
  30. data/lib/active_record/fixtures.rb +1 -0
  31. data/lib/active_record/locking/optimistic.rb +34 -1
  32. data/lib/active_record/migration.rb +5 -0
  33. data/lib/active_record/nested_attributes.rb +64 -52
  34. data/lib/active_record/reflection.rb +66 -1
  35. data/lib/active_record/schema.rb +5 -1
  36. data/lib/active_record/schema_dumper.rb +3 -0
  37. data/lib/active_record/serializers/json_serializer.rb +1 -1
  38. data/lib/active_record/validations.rb +13 -1
  39. data/lib/active_record/version.rb +1 -1
  40. data/test/cases/active_schema_test_mysql.rb +22 -0
  41. data/test/cases/associations/belongs_to_associations_test.rb +13 -0
  42. data/test/cases/associations/eager_load_nested_include_test.rb +8 -7
  43. data/test/cases/associations/eager_test.rb +7 -1
  44. data/test/cases/associations/has_many_associations_test.rb +26 -0
  45. data/test/cases/associations/inverse_associations_test.rb +566 -0
  46. data/test/cases/associations_test.rb +10 -0
  47. data/test/cases/autosave_association_test.rb +86 -10
  48. data/test/cases/base_test.rb +29 -0
  49. data/test/cases/batches_test.rb +20 -0
  50. data/test/cases/calculations_test.rb +2 -3
  51. data/test/cases/encoding_test.rb +6 -0
  52. data/test/cases/finder_test.rb +19 -3
  53. data/test/cases/fixtures_test.rb +5 -0
  54. data/test/cases/json_serialization_test.rb +14 -0
  55. data/test/cases/locking_test.rb +48 -3
  56. data/test/cases/migration_test.rb +115 -0
  57. data/test/cases/modules_test.rb +28 -0
  58. data/test/cases/named_scope_test.rb +1 -1
  59. data/test/cases/nested_attributes_test.rb +239 -7
  60. data/test/cases/query_cache_test.rb +7 -1
  61. data/test/cases/reflection_test.rb +47 -7
  62. data/test/cases/schema_test_postgresql.rb +2 -2
  63. data/test/cases/validations_i18n_test.rb +6 -36
  64. data/test/cases/validations_test.rb +33 -1
  65. data/test/cases/yaml_serialization_test.rb +11 -0
  66. data/test/fixtures/faces.yml +11 -0
  67. data/test/fixtures/fixture_database.sqlite +0 -0
  68. data/test/fixtures/fixture_database.sqlite3 +0 -0
  69. data/test/fixtures/fixture_database_2.sqlite +0 -0
  70. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  71. data/test/fixtures/interests.yml +33 -0
  72. data/test/fixtures/men.yml +5 -0
  73. data/test/fixtures/zines.yml +5 -0
  74. data/test/models/author.rb +3 -0
  75. data/test/models/bird.rb +6 -0
  76. data/test/models/company_in_module.rb +17 -0
  77. data/test/models/event_author.rb +5 -0
  78. data/test/models/face.rb +7 -0
  79. data/test/models/interest.rb +5 -0
  80. data/test/models/invoice.rb +4 -0
  81. data/test/models/line_item.rb +3 -0
  82. data/test/models/man.rb +9 -0
  83. data/test/models/parrot.rb +6 -0
  84. data/test/models/pirate.rb +10 -0
  85. data/test/models/ship.rb +10 -1
  86. data/test/models/ship_part.rb +3 -1
  87. data/test/models/zine.rb +3 -0
  88. data/test/schema/schema.rb +41 -0
  89. metadata +37 -11
  90. data/lib/active_record/i18n_interpolation_deprecation.rb +0 -26
@@ -78,4 +78,32 @@ class ModulesTest < ActiveRecord::TestCase
78
78
  end
79
79
  end
80
80
  end
81
+
82
+ def test_module_table_name_prefix
83
+ assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_prefix'
84
+ assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_prefix'
85
+ assert_equal 'companies', MyApplication::Business::Prefixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed'
86
+ end
87
+
88
+ def test_module_table_name_prefix_with_global_prefix
89
+ classes = [ MyApplication::Business::Company,
90
+ MyApplication::Business::Firm,
91
+ MyApplication::Business::Client,
92
+ MyApplication::Business::Client::Contact,
93
+ MyApplication::Business::Developer,
94
+ MyApplication::Business::Project,
95
+ MyApplication::Business::Prefixed::Company,
96
+ MyApplication::Business::Prefixed::Nested::Company,
97
+ MyApplication::Billing::Account ]
98
+
99
+ ActiveRecord::Base.table_name_prefix = 'global_'
100
+ classes.each(&:reset_table_name)
101
+ assert_equal 'global_companies', MyApplication::Business::Company.table_name, 'inferred table_name for ActiveRecord model in module without table_name_prefix'
102
+ assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_prefix'
103
+ assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_prefix'
104
+ assert_equal 'companies', MyApplication::Business::Prefixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed'
105
+ ensure
106
+ ActiveRecord::Base.table_name_prefix = ''
107
+ classes.each(&:reset_table_name)
108
+ end
81
109
  end
@@ -265,7 +265,7 @@ class NamedScopeTest < ActiveRecord::TestCase
265
265
  end
266
266
 
267
267
  def test_rand_should_select_a_random_object_from_proxy
268
- assert Topic.approved.rand.is_a?(Topic)
268
+ assert Topic.approved.random_element.is_a?(Topic)
269
269
  end
270
270
 
271
271
  def test_should_use_where_in_query_for_named_scope
@@ -1,9 +1,14 @@
1
1
  require "cases/helper"
2
2
  require "models/pirate"
3
3
  require "models/ship"
4
+ require "models/ship_part"
4
5
  require "models/bird"
5
6
  require "models/parrot"
6
7
  require "models/treasure"
8
+ require "models/man"
9
+ require "models/interest"
10
+ require "models/owner"
11
+ require "models/pet"
7
12
 
8
13
  module AssertRaiseWithMessage
9
14
  def assert_raise_with_message(expected_exception, expected_message)
@@ -31,11 +36,30 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
31
36
  end
32
37
 
33
38
  def test_should_add_a_proc_to_nested_attributes_options
39
+ assert_equal ActiveRecord::NestedAttributes::ClassMethods::REJECT_ALL_BLANK_PROC,
40
+ Pirate.nested_attributes_options[:birds_with_reject_all_blank][:reject_if]
41
+
34
42
  [:parrots, :birds].each do |name|
35
43
  assert_instance_of Proc, Pirate.nested_attributes_options[name][:reject_if]
36
44
  end
37
45
  end
38
46
 
47
+ def test_should_not_build_a_new_record_if_reject_all_blank_returns_false
48
+ pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
49
+ pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => ''}]
50
+ pirate.save!
51
+
52
+ assert pirate.birds_with_reject_all_blank.empty?
53
+ end
54
+
55
+ def test_should_build_a_new_record_if_reject_all_blank_does_not_return_false
56
+ pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
57
+ pirate.birds_with_reject_all_blank_attributes = [{:name => 'Tweetie', :color => ''}]
58
+ pirate.save!
59
+
60
+ assert_equal 1, pirate.birds_with_reject_all_blank.count
61
+ end
62
+
39
63
  def test_should_raise_an_ArgumentError_for_non_existing_associations
40
64
  assert_raise_with_message ArgumentError, "No association found for name `honesty'. Has it been defined yet?" do
41
65
  Pirate.accepts_nested_attributes_for :honesty
@@ -60,12 +84,6 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
60
84
  assert ship._destroy
61
85
  end
62
86
 
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
87
  def test_reject_if_method_without_arguments
70
88
  Pirate.accepts_nested_attributes_for :ship, :reject_if => :new_record?
71
89
 
@@ -157,6 +175,12 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
157
175
  assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
158
176
  end
159
177
 
178
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
179
+ assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Ship with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
180
+ @pirate.ship_attributes = { :id => 1234567890 }
181
+ end
182
+ end
183
+
160
184
  def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
161
185
  @pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' }
162
186
 
@@ -226,9 +250,32 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
226
250
  def test_should_automatically_enable_autosave_on_the_association
227
251
  assert Pirate.reflect_on_association(:ship).options[:autosave]
228
252
  end
253
+
254
+ def test_should_accept_update_only_option
255
+ @pirate.update_attribute(:update_only_ship_attributes, { :id => @pirate.ship.id, :name => 'Mayflower' })
256
+ end
257
+
258
+ def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
259
+ @ship.delete
260
+ assert_difference('Ship.count', 1) do
261
+ @pirate.reload.update_attribute(:update_only_ship_attributes, { :name => 'Mayflower' })
262
+ end
263
+ end
264
+
265
+ def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
266
+ @ship.delete
267
+ @ship = @pirate.create_update_only_ship(:name => 'Nights Dirty Lightning')
268
+
269
+ assert_no_difference('Ship.count') do
270
+ @pirate.update_attributes(:update_only_ship_attributes => { :name => 'Mayflower' })
271
+ end
272
+ assert_equal 'Mayflower', @ship.reload.name
273
+ end
229
274
  end
230
275
 
231
276
  class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
277
+ include AssertRaiseWithMessage
278
+
232
279
  def setup
233
280
  @ship = Ship.new(:name => 'Nights Dirty Lightning')
234
281
  @pirate = @ship.build_pirate(:catchphrase => 'Aye')
@@ -283,6 +330,12 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
283
330
  assert_equal 'Arr', @ship.pirate.catchphrase
284
331
  end
285
332
 
333
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
334
+ assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Pirate with ID=1234567890 for Ship with ID=#{@ship.id}" do
335
+ @ship.pirate_attributes = { :id => 1234567890 }
336
+ end
337
+ end
338
+
286
339
  def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
287
340
  @ship.reload.pirate_attributes = { 'id' => @pirate.id, 'catchphrase' => 'Arr' }
288
341
 
@@ -343,6 +396,23 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
343
396
  def test_should_automatically_enable_autosave_on_the_association
344
397
  assert Ship.reflect_on_association(:pirate).options[:autosave]
345
398
  end
399
+
400
+ def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
401
+ @pirate.delete
402
+ assert_difference('Pirate.count', 1) do
403
+ @ship.reload.update_attribute(:update_only_pirate_attributes, { :catchphrase => 'Arr' })
404
+ end
405
+ end
406
+
407
+ def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
408
+ @pirate.delete
409
+ @pirate = @ship.create_update_only_pirate(:catchphrase => 'Aye')
410
+
411
+ assert_no_difference('Pirate.count') do
412
+ @ship.update_attributes(:update_only_pirate_attributes => { :catchphrase => 'Arr' })
413
+ end
414
+ assert_equal 'Arr', @pirate.reload.catchphrase
415
+ end
346
416
  end
347
417
 
348
418
  module NestedAttributesOnACollectionAssociationTests
@@ -352,6 +422,15 @@ module NestedAttributesOnACollectionAssociationTests
352
422
  assert_respond_to @pirate, association_setter
353
423
  end
354
424
 
425
+ def test_should_save_only_one_association_on_create
426
+ pirate = Pirate.create!({
427
+ :catchphrase => 'Arr',
428
+ association_getter => { 'foo' => { :name => 'Grace OMalley' } }
429
+ })
430
+
431
+ assert_equal 1, pirate.reload.send(@association_name).count
432
+ end
433
+
355
434
  def test_should_take_a_hash_with_string_keys_and_assign_the_attributes_to_the_associated_models
356
435
  @alternate_params[association_getter].stringify_keys!
357
436
  @pirate.update_attributes @alternate_params
@@ -376,6 +455,16 @@ module NestedAttributesOnACollectionAssociationTests
376
455
  assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
377
456
  end
378
457
 
458
+ def test_should_not_load_association_when_updating_existing_records
459
+ @pirate.reload
460
+ @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
461
+ assert ! @pirate.send(@association_name).loaded?
462
+
463
+ @pirate.save
464
+ assert ! @pirate.send(@association_name).loaded?
465
+ assert_equal 'Grace OMalley', @child_1.reload.name
466
+ end
467
+
379
468
  def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
380
469
  @child_1.stubs(:id).returns('ABC1X')
381
470
  @child_2.stubs(:id).returns('ABC2X')
@@ -390,6 +479,12 @@ module NestedAttributesOnACollectionAssociationTests
390
479
  assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
391
480
  end
392
481
 
482
+ def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
483
+ assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
484
+ @pirate.attributes = { association_getter => [{ :id => 1234567890 }] }
485
+ end
486
+ end
487
+
393
488
  def test_should_automatically_build_new_associated_models_for_each_entry_in_a_hash_where_the_id_is_missing
394
489
  @pirate.send(@association_name).destroy_all
395
490
  @pirate.reload.attributes = {
@@ -424,7 +519,7 @@ module NestedAttributesOnACollectionAssociationTests
424
519
 
425
520
  def test_should_ignore_new_associated_records_if_a_reject_if_proc_returns_false
426
521
  @alternate_params[association_getter]['baz'] = {}
427
- assert_no_difference("@pirate.send(@association_name).length") do
522
+ assert_no_difference("@pirate.send(@association_name).count") do
428
523
  @pirate.attributes = @alternate_params
429
524
  end
430
525
  end
@@ -495,6 +590,41 @@ module NestedAttributesOnACollectionAssociationTests
495
590
  assert Pirate.reflect_on_association(@association_name).options[:autosave]
496
591
  end
497
592
 
593
+ def test_validate_presence_of_parent_works_with_inverse_of
594
+ Man.accepts_nested_attributes_for(:interests)
595
+ assert_equal :man, Man.reflect_on_association(:interests).options[:inverse_of]
596
+ assert_equal :interests, Interest.reflect_on_association(:man).options[:inverse_of]
597
+
598
+ repair_validations(Interest) do
599
+ Interest.validates_presence_of(:man)
600
+ assert_difference 'Man.count' do
601
+ assert_difference 'Interest.count', 2 do
602
+ man = Man.create!(:name => 'John',
603
+ :interests_attributes => [{:topic=>'Cars'}, {:topic=>'Sports'}])
604
+ assert_equal 2, man.interests.count
605
+ end
606
+ end
607
+ end
608
+ end
609
+
610
+ def test_validate_presence_of_parent_fails_without_inverse_of
611
+ Man.accepts_nested_attributes_for(:interests)
612
+ Man.reflect_on_association(:interests).options.delete(:inverse_of)
613
+ Interest.reflect_on_association(:man).options.delete(:inverse_of)
614
+
615
+ repair_validations(Interest) do
616
+ Interest.validates_presence_of(:man)
617
+ assert_no_difference ['Man.count', 'Interest.count'] do
618
+ man = Man.create(:name => 'John',
619
+ :interests_attributes => [{:topic=>'Cars'}, {:topic=>'Sports'}])
620
+ assert !man.errors[:'interests.man'].empty?
621
+ end
622
+ end
623
+ # restore :inverse_of
624
+ Man.reflect_on_association(:interests).options[:inverse_of] = :man
625
+ Interest.reflect_on_association(:man).options[:inverse_of] = :interests
626
+ end
627
+
498
628
  private
499
629
 
500
630
  def association_setter
@@ -579,3 +709,105 @@ class TestNestedAttributesLimit < ActiveRecord::TestCase
579
709
  end
580
710
  end
581
711
  end
712
+
713
+ class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase
714
+ fixtures :owners, :pets
715
+
716
+ def setup
717
+ Owner.accepts_nested_attributes_for :pets
718
+
719
+ @owner = owners(:ashley)
720
+ @pet1, @pet2 = pets(:chew), pets(:mochi)
721
+
722
+ @params = {
723
+ :pets_attributes => {
724
+ '0' => { :id => @pet1.id, :name => 'Foo' },
725
+ '1' => { :id => @pet2.id, :name => 'Bar' }
726
+ }
727
+ }
728
+ end
729
+
730
+ def test_should_update_existing_records_with_non_standard_primary_key
731
+ @owner.update_attributes(@params)
732
+ assert_equal ['Foo', 'Bar'], @owner.pets.map(&:name)
733
+ end
734
+ end
735
+
736
+ class TestHasOneAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
737
+ self.use_transactional_fixtures = false
738
+
739
+ def setup
740
+ @pirate = Pirate.create!(:catchphrase => "My baby takes tha mornin' train!")
741
+ @ship = @pirate.create_ship(:name => "The good ship Dollypop")
742
+ @part = @ship.parts.create!(:name => "Mast")
743
+ @trinket = @part.trinkets.create!(:name => "Necklace")
744
+ end
745
+
746
+ test "when great-grandchild changed in memory, saving parent should save great-grandchild" do
747
+ @trinket.name = "changed"
748
+ @pirate.save
749
+ assert_equal "changed", @trinket.reload.name
750
+ end
751
+
752
+ test "when great-grandchild changed via attributes, saving parent should save great-grandchild" do
753
+ @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :name => "changed"}]}]}}
754
+ @pirate.save
755
+ assert_equal "changed", @trinket.reload.name
756
+ end
757
+
758
+ test "when great-grandchild marked_for_destruction via attributes, saving parent should destroy great-grandchild" do
759
+ @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :_destroy => true}]}]}}
760
+ assert_difference('@part.trinkets.count', -1) { @pirate.save }
761
+ end
762
+
763
+ test "when great-grandchild added via attributes, saving parent should create great-grandchild" do
764
+ @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:name => "created"}]}]}}
765
+ assert_difference('@part.trinkets.count', 1) { @pirate.save }
766
+ end
767
+
768
+ test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do
769
+ @trinket.name = "changed"
770
+ Ship.create!(:pirate => @pirate, :name => "The Black Rock")
771
+ ShipPart.create!(:ship => @ship, :name => "Stern")
772
+ assert_no_queries { @pirate.valid? }
773
+ end
774
+ end
775
+
776
+ class TestHasManyAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
777
+ self.use_transactional_fixtures = false
778
+
779
+ def setup
780
+ @ship = Ship.create!(:name => "The good ship Dollypop")
781
+ @part = @ship.parts.create!(:name => "Mast")
782
+ @trinket = @part.trinkets.create!(:name => "Necklace")
783
+ end
784
+
785
+ test "when grandchild changed in memory, saving parent should save grandchild" do
786
+ @trinket.name = "changed"
787
+ @ship.save
788
+ assert_equal "changed", @trinket.reload.name
789
+ end
790
+
791
+ test "when grandchild changed via attributes, saving parent should save grandchild" do
792
+ @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :name => "changed"}]}]}
793
+ @ship.save
794
+ assert_equal "changed", @trinket.reload.name
795
+ end
796
+
797
+ test "when grandchild marked_for_destruction via attributes, saving parent should destroy grandchild" do
798
+ @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :_destroy => true}]}]}
799
+ assert_difference('@part.trinkets.count', -1) { @ship.save }
800
+ end
801
+
802
+ test "when grandchild added via attributes, saving parent should create grandchild" do
803
+ @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:name => "created"}]}]}
804
+ assert_difference('@part.trinkets.count', 1) { @ship.save }
805
+ end
806
+
807
+ test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do
808
+ @trinket.name = "changed"
809
+ Ship.create!(:name => "The Black Rock")
810
+ ShipPart.create!(:ship => @ship, :name => "Stern")
811
+ assert_no_queries { @ship.valid? }
812
+ end
813
+ end
@@ -49,8 +49,14 @@ class QueryCacheTest < ActiveRecord::TestCase
49
49
  end
50
50
 
51
51
  def test_cache_does_not_wrap_string_results_in_arrays
52
+ require 'sqlite3/version' if current_adapter?(:SQLite3Adapter)
53
+
52
54
  Task.cache do
53
- assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
55
+ if current_adapter?(:SQLite3Adapter) && SQLite3::Version::VERSION > '1.2.5'
56
+ assert_instance_of Fixnum, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
57
+ else
58
+ assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
59
+ end
54
60
  end
55
61
  end
56
62
  end
@@ -4,9 +4,12 @@ require 'models/customer'
4
4
  require 'models/company'
5
5
  require 'models/company_in_module'
6
6
  require 'models/subscriber'
7
+ require 'models/ship'
7
8
  require 'models/pirate'
8
9
 
9
10
  class ReflectionTest < ActiveRecord::TestCase
11
+ include ActiveRecord::Reflection
12
+
10
13
  fixtures :topics, :customers, :companies, :subscribers
11
14
 
12
15
  def setup
@@ -62,22 +65,22 @@ class ReflectionTest < ActiveRecord::TestCase
62
65
  end
63
66
 
64
67
  def test_reflection_klass_for_nested_class_name
65
- reflection = ActiveRecord::Reflection::MacroReflection.new(nil, nil, { :class_name => 'MyApplication::Business::Company' }, nil)
68
+ reflection = MacroReflection.new(nil, nil, { :class_name => 'MyApplication::Business::Company' }, nil)
66
69
  assert_nothing_raised do
67
70
  assert_equal MyApplication::Business::Company, reflection.klass
68
71
  end
69
72
  end
70
73
 
71
74
  def test_aggregation_reflection
72
- reflection_for_address = ActiveRecord::Reflection::AggregateReflection.new(
75
+ reflection_for_address = AggregateReflection.new(
73
76
  :composed_of, :address, { :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer
74
77
  )
75
78
 
76
- reflection_for_balance = ActiveRecord::Reflection::AggregateReflection.new(
79
+ reflection_for_balance = AggregateReflection.new(
77
80
  :composed_of, :balance, { :class_name => "Money", :mapping => %w(balance amount) }, Customer
78
81
  )
79
82
 
80
- reflection_for_gps_location = ActiveRecord::Reflection::AggregateReflection.new(
83
+ reflection_for_gps_location = AggregateReflection.new(
81
84
  :composed_of, :gps_location, { }, Customer
82
85
  )
83
86
 
@@ -102,7 +105,7 @@ class ReflectionTest < ActiveRecord::TestCase
102
105
  end
103
106
 
104
107
  def test_has_many_reflection
105
- reflection_for_clients = ActiveRecord::Reflection::AssociationReflection.new(:has_many, :clients, { :order => "id", :dependent => :destroy }, Firm)
108
+ reflection_for_clients = AssociationReflection.new(:has_many, :clients, { :order => "id", :dependent => :destroy }, Firm)
106
109
 
107
110
  assert_equal reflection_for_clients, Firm.reflect_on_association(:clients)
108
111
 
@@ -114,7 +117,7 @@ class ReflectionTest < ActiveRecord::TestCase
114
117
  end
115
118
 
116
119
  def test_has_one_reflection
117
- reflection_for_account = ActiveRecord::Reflection::AssociationReflection.new(:has_one, :account, { :foreign_key => "firm_id", :dependent => :destroy }, Firm)
120
+ reflection_for_account = AssociationReflection.new(:has_one, :account, { :foreign_key => "firm_id", :dependent => :destroy }, Firm)
118
121
  assert_equal reflection_for_account, Firm.reflect_on_association(:account)
119
122
 
120
123
  assert_equal Account, Firm.reflect_on_association(:account).klass
@@ -181,7 +184,44 @@ class ReflectionTest < ActiveRecord::TestCase
181
184
  end
182
185
 
183
186
  def test_has_many_through_reflection
184
- assert_kind_of ActiveRecord::Reflection::ThroughReflection, Subscriber.reflect_on_association(:books)
187
+ assert_kind_of ThroughReflection, Subscriber.reflect_on_association(:books)
188
+ end
189
+
190
+ def test_collection_association
191
+ assert Pirate.reflect_on_association(:birds).collection?
192
+ assert Pirate.reflect_on_association(:parrots).collection?
193
+
194
+ assert !Pirate.reflect_on_association(:ship).collection?
195
+ assert !Ship.reflect_on_association(:pirate).collection?
196
+ end
197
+
198
+ def test_default_association_validation
199
+ assert AssociationReflection.new(:has_many, :clients, {}, Firm).validate?
200
+
201
+ assert !AssociationReflection.new(:has_one, :client, {}, Firm).validate?
202
+ assert !AssociationReflection.new(:belongs_to, :client, {}, Firm).validate?
203
+ assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, {}, Firm).validate?
204
+ end
205
+
206
+ def test_always_validate_association_if_explicit
207
+ assert AssociationReflection.new(:has_one, :client, { :validate => true }, Firm).validate?
208
+ assert AssociationReflection.new(:belongs_to, :client, { :validate => true }, Firm).validate?
209
+ assert AssociationReflection.new(:has_many, :clients, { :validate => true }, Firm).validate?
210
+ assert AssociationReflection.new(:has_and_belongs_to_many, :clients, { :validate => true }, Firm).validate?
211
+ end
212
+
213
+ def test_validate_association_if_autosave
214
+ assert AssociationReflection.new(:has_one, :client, { :autosave => true }, Firm).validate?
215
+ assert AssociationReflection.new(:belongs_to, :client, { :autosave => true }, Firm).validate?
216
+ assert AssociationReflection.new(:has_many, :clients, { :autosave => true }, Firm).validate?
217
+ assert AssociationReflection.new(:has_and_belongs_to_many, :clients, { :autosave => true }, Firm).validate?
218
+ end
219
+
220
+ def test_never_validate_association_if_explicit
221
+ assert !AssociationReflection.new(:has_one, :client, { :autosave => true, :validate => false }, Firm).validate?
222
+ assert !AssociationReflection.new(:belongs_to, :client, { :autosave => true, :validate => false }, Firm).validate?
223
+ assert !AssociationReflection.new(:has_many, :clients, { :autosave => true, :validate => false }, Firm).validate?
224
+ assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, { :autosave => true, :validate => false }, Firm).validate?
185
225
  end
186
226
 
187
227
  private