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.
- data/CHANGELOG +33 -0
- data/Rakefile +1 -1
- data/examples/performance.sql +85 -0
- data/lib/active_record.rb +1 -2
- data/lib/active_record/association_preload.rb +9 -2
- data/lib/active_record/associations.rb +48 -38
- data/lib/active_record/associations/association_collection.rb +15 -11
- data/lib/active_record/associations/association_proxy.rb +16 -6
- data/lib/active_record/associations/belongs_to_association.rb +11 -1
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -10
- data/lib/active_record/associations/has_many_association.rb +5 -0
- data/lib/active_record/associations/has_many_through_association.rb +5 -5
- data/lib/active_record/associations/has_one_association.rb +10 -1
- data/lib/active_record/attribute_methods.rb +5 -1
- data/lib/active_record/autosave_association.rb +66 -35
- data/lib/active_record/base.rb +77 -36
- data/lib/active_record/batches.rb +13 -9
- data/lib/active_record/calculations.rb +6 -3
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +64 -10
- data/lib/active_record/connection_adapters/abstract_adapter.rb +2 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +31 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +31 -66
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -2
- data/lib/active_record/dirty.rb +2 -2
- data/lib/active_record/fixtures.rb +1 -0
- data/lib/active_record/locking/optimistic.rb +34 -1
- data/lib/active_record/migration.rb +5 -0
- data/lib/active_record/nested_attributes.rb +64 -52
- data/lib/active_record/reflection.rb +66 -1
- data/lib/active_record/schema.rb +5 -1
- data/lib/active_record/schema_dumper.rb +3 -0
- data/lib/active_record/serializers/json_serializer.rb +1 -1
- data/lib/active_record/validations.rb +13 -1
- data/lib/active_record/version.rb +1 -1
- data/test/cases/active_schema_test_mysql.rb +22 -0
- data/test/cases/associations/belongs_to_associations_test.rb +13 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +8 -7
- data/test/cases/associations/eager_test.rb +7 -1
- data/test/cases/associations/has_many_associations_test.rb +26 -0
- data/test/cases/associations/inverse_associations_test.rb +566 -0
- data/test/cases/associations_test.rb +10 -0
- data/test/cases/autosave_association_test.rb +86 -10
- data/test/cases/base_test.rb +29 -0
- data/test/cases/batches_test.rb +20 -0
- data/test/cases/calculations_test.rb +2 -3
- data/test/cases/encoding_test.rb +6 -0
- data/test/cases/finder_test.rb +19 -3
- data/test/cases/fixtures_test.rb +5 -0
- data/test/cases/json_serialization_test.rb +14 -0
- data/test/cases/locking_test.rb +48 -3
- data/test/cases/migration_test.rb +115 -0
- data/test/cases/modules_test.rb +28 -0
- data/test/cases/named_scope_test.rb +1 -1
- data/test/cases/nested_attributes_test.rb +239 -7
- data/test/cases/query_cache_test.rb +7 -1
- data/test/cases/reflection_test.rb +47 -7
- data/test/cases/schema_test_postgresql.rb +2 -2
- data/test/cases/validations_i18n_test.rb +6 -36
- data/test/cases/validations_test.rb +33 -1
- data/test/cases/yaml_serialization_test.rb +11 -0
- data/test/fixtures/faces.yml +11 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/interests.yml +33 -0
- data/test/fixtures/men.yml +5 -0
- data/test/fixtures/zines.yml +5 -0
- data/test/models/author.rb +3 -0
- data/test/models/bird.rb +6 -0
- data/test/models/company_in_module.rb +17 -0
- data/test/models/event_author.rb +5 -0
- data/test/models/face.rb +7 -0
- data/test/models/interest.rb +5 -0
- data/test/models/invoice.rb +4 -0
- data/test/models/line_item.rb +3 -0
- data/test/models/man.rb +9 -0
- data/test/models/parrot.rb +6 -0
- data/test/models/pirate.rb +10 -0
- data/test/models/ship.rb +10 -1
- data/test/models/ship_part.rb +3 -1
- data/test/models/zine.rb +3 -0
- data/test/schema/schema.rb +41 -0
- metadata +37 -11
- data/lib/active_record/i18n_interpolation_deprecation.rb +0 -26
data/test/cases/modules_test.rb
CHANGED
@@ -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.
|
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).
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
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
|