activerecord 2.0.5 → 2.1.0
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 +168 -6
- data/README +27 -22
- data/RUNNING_UNIT_TESTS +7 -4
- data/Rakefile +22 -25
- data/lib/active_record.rb +8 -2
- data/lib/active_record/aggregations.rb +21 -12
- data/lib/active_record/association_preload.rb +277 -0
- data/lib/active_record/associations.rb +481 -295
- data/lib/active_record/associations/association_collection.rb +162 -37
- data/lib/active_record/associations/association_proxy.rb +71 -7
- data/lib/active_record/associations/belongs_to_association.rb +5 -3
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -6
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -64
- data/lib/active_record/associations/has_many_association.rb +8 -73
- data/lib/active_record/associations/has_many_through_association.rb +68 -117
- data/lib/active_record/associations/has_one_association.rb +7 -5
- data/lib/active_record/associations/has_one_through_association.rb +28 -0
- data/lib/active_record/attribute_methods.rb +69 -19
- data/lib/active_record/base.rb +496 -275
- data/lib/active_record/calculations.rb +28 -21
- data/lib/active_record/callbacks.rb +9 -38
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -2
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +232 -45
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +141 -27
- data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -13
- data/lib/active_record/connection_adapters/mysql_adapter.rb +57 -24
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +143 -42
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +18 -10
- data/lib/active_record/dirty.rb +158 -0
- data/lib/active_record/fixtures.rb +121 -156
- data/lib/active_record/locking/optimistic.rb +14 -11
- data/lib/active_record/locking/pessimistic.rb +2 -2
- data/lib/active_record/migration.rb +157 -77
- data/lib/active_record/named_scope.rb +163 -0
- data/lib/active_record/observer.rb +19 -5
- data/lib/active_record/reflection.rb +34 -14
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +4 -4
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/serializers/json_serializer.rb +37 -28
- data/lib/active_record/serializers/xml_serializer.rb +52 -29
- data/lib/active_record/test_case.rb +36 -0
- data/lib/active_record/timestamp.rb +4 -4
- data/lib/active_record/transactions.rb +3 -3
- data/lib/active_record/validations.rb +182 -248
- data/lib/active_record/version.rb +2 -2
- data/test/{fixtures → assets}/example.log +0 -0
- data/test/{fixtures → assets}/flowers.jpg +0 -0
- data/test/cases/aaa_create_tables_test.rb +24 -0
- data/test/cases/active_schema_test_mysql.rb +95 -0
- data/test/cases/active_schema_test_postgresql.rb +24 -0
- data/test/{adapter_test.rb → cases/adapter_test.rb} +15 -14
- data/test/{adapter_test_sqlserver.rb → cases/adapter_test_sqlserver.rb} +95 -95
- data/test/{aggregations_test.rb → cases/aggregations_test.rb} +20 -20
- data/test/{ar_schema_test.rb → cases/ar_schema_test.rb} +6 -6
- data/test/cases/associations/belongs_to_associations_test.rb +412 -0
- data/test/{associations → cases/associations}/callbacks_test.rb +24 -10
- data/test/{associations → cases/associations}/cascaded_eager_loading_test.rb +18 -17
- data/test/cases/associations/eager_load_nested_include_test.rb +83 -0
- data/test/{associations → cases/associations}/eager_singularization_test.rb +5 -5
- data/test/{associations → cases/associations}/eager_test.rb +216 -51
- data/test/{associations → cases/associations}/extension_test.rb +8 -8
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +684 -0
- data/test/cases/associations/has_many_associations_test.rb +932 -0
- data/test/cases/associations/has_many_through_associations_test.rb +190 -0
- data/test/cases/associations/has_one_associations_test.rb +323 -0
- data/test/cases/associations/has_one_through_associations_test.rb +74 -0
- data/test/{associations → cases/associations}/inner_join_association_test.rb +20 -20
- data/test/{associations → cases/associations}/join_model_test.rb +175 -35
- data/test/cases/associations_test.rb +262 -0
- data/test/{attribute_methods_test.rb → cases/attribute_methods_test.rb} +103 -11
- data/test/{base_test.rb → cases/base_test.rb} +338 -191
- data/test/{binary_test.rb → cases/binary_test.rb} +6 -4
- data/test/{calculations_test.rb → cases/calculations_test.rb} +35 -23
- data/test/{callbacks_test.rb → cases/callbacks_test.rb} +7 -7
- data/test/{class_inheritable_attributes_test.rb → cases/class_inheritable_attributes_test.rb} +3 -3
- data/test/{column_alias_test.rb → cases/column_alias_test.rb} +3 -3
- data/test/{connection_test_firebird.rb → cases/connection_test_firebird.rb} +2 -2
- data/test/{connection_test_mysql.rb → cases/connection_test_mysql.rb} +2 -2
- data/test/{copy_table_test_sqlite.rb → cases/copy_table_test_sqlite.rb} +13 -13
- data/test/{datatype_test_postgresql.rb → cases/datatype_test_postgresql.rb} +8 -8
- data/test/{date_time_test.rb → cases/date_time_test.rb} +5 -5
- data/test/{default_test_firebird.rb → cases/default_test_firebird.rb} +3 -3
- data/test/{defaults_test.rb → cases/defaults_test.rb} +8 -6
- data/test/{deprecated_finder_test.rb → cases/deprecated_finder_test.rb} +3 -3
- data/test/cases/dirty_test.rb +163 -0
- data/test/cases/finder_respond_to_test.rb +76 -0
- data/test/{finder_test.rb → cases/finder_test.rb} +266 -33
- data/test/{fixtures_test.rb → cases/fixtures_test.rb} +88 -72
- data/test/cases/helper.rb +47 -0
- data/test/{inheritance_test.rb → cases/inheritance_test.rb} +61 -17
- data/test/cases/invalid_date_test.rb +24 -0
- data/test/{json_serialization_test.rb → cases/json_serialization_test.rb} +36 -11
- data/test/{lifecycle_test.rb → cases/lifecycle_test.rb} +16 -13
- data/test/{locking_test.rb → cases/locking_test.rb} +17 -10
- data/test/{method_scoping_test.rb → cases/method_scoping_test.rb} +75 -39
- data/test/{migration_test.rb → cases/migration_test.rb} +420 -80
- data/test/{migration_test_firebird.rb → cases/migration_test_firebird.rb} +3 -3
- data/test/{mixin_test.rb → cases/mixin_test.rb} +7 -6
- data/test/{modules_test.rb → cases/modules_test.rb} +11 -6
- data/test/{multiple_db_test.rb → cases/multiple_db_test.rb} +5 -5
- data/test/cases/named_scope_test.rb +157 -0
- data/test/{pk_test.rb → cases/pk_test.rb} +10 -10
- data/test/{query_cache_test.rb → cases/query_cache_test.rb} +12 -10
- data/test/{readonly_test.rb → cases/readonly_test.rb} +11 -11
- data/test/{reflection_test.rb → cases/reflection_test.rb} +15 -14
- data/test/{reserved_word_test_mysql.rb → cases/reserved_word_test_mysql.rb} +4 -5
- data/test/{schema_authorization_test_postgresql.rb → cases/schema_authorization_test_postgresql.rb} +5 -5
- data/test/cases/schema_dumper_test.rb +138 -0
- data/test/cases/schema_test_postgresql.rb +102 -0
- data/test/{serialization_test.rb → cases/serialization_test.rb} +7 -7
- data/test/{synonym_test_oracle.rb → cases/synonym_test_oracle.rb} +5 -5
- data/test/{table_name_test_sqlserver.rb → cases/table_name_test_sqlserver.rb} +3 -3
- data/test/{threaded_connections_test.rb → cases/threaded_connections_test.rb} +7 -7
- data/test/{transactions_test.rb → cases/transactions_test.rb} +31 -5
- data/test/{unconnected_test.rb → cases/unconnected_test.rb} +2 -2
- data/test/{validations_test.rb → cases/validations_test.rb} +141 -39
- data/test/{xml_serialization_test.rb → cases/xml_serialization_test.rb} +12 -12
- data/test/config.rb +5 -0
- data/test/connections/native_db2/connection.rb +1 -1
- data/test/connections/native_firebird/connection.rb +1 -1
- data/test/connections/native_frontbase/connection.rb +1 -1
- data/test/connections/native_mysql/connection.rb +1 -1
- data/test/connections/native_openbase/connection.rb +1 -1
- data/test/connections/native_oracle/connection.rb +1 -1
- data/test/connections/native_postgresql/connection.rb +1 -3
- data/test/connections/native_sqlite/connection.rb +2 -2
- data/test/connections/native_sqlite3/connection.rb +2 -2
- data/test/connections/native_sqlite3/in_memory_connection.rb +3 -3
- data/test/connections/native_sybase/connection.rb +1 -1
- data/test/fixtures/author_addresses.yml +5 -0
- data/test/fixtures/authors.yml +2 -0
- data/test/fixtures/clubs.yml +6 -0
- data/test/fixtures/jobs.yml +7 -0
- data/test/fixtures/members.yml +4 -0
- data/test/fixtures/memberships.yml +20 -0
- data/test/fixtures/owners.yml +7 -0
- data/test/fixtures/people.yml +4 -1
- data/test/fixtures/pets.yml +14 -0
- data/test/fixtures/posts.yml +1 -0
- data/test/fixtures/price_estimates.yml +7 -0
- data/test/fixtures/readers.yml +5 -0
- data/test/fixtures/references.yml +17 -0
- data/test/fixtures/sponsors.yml +9 -0
- data/test/fixtures/subscribers.yml +7 -0
- data/test/fixtures/subscriptions.yml +12 -0
- data/test/fixtures/taggings.yml +4 -1
- data/test/fixtures/topics.yml +22 -2
- data/test/fixtures/warehouse-things.yml +3 -0
- data/test/{fixtures/migrations_with_decimal → migrations/decimal}/1_give_me_big_numbers.rb +0 -0
- data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/1_people_have_last_names.rb +1 -1
- data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/2_we_need_reminders.rb +1 -1
- data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/3_foo.rb +0 -0
- data/test/{fixtures/migrations → migrations/duplicate}/3_innocent_jointable.rb +0 -0
- data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
- data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
- data/test/{fixtures/migrations_with_duplicate → migrations/interleaved/pass_1}/3_innocent_jointable.rb +0 -0
- data/test/{fixtures/migrations → migrations/interleaved/pass_2}/1_people_have_last_names.rb +1 -1
- data/test/{fixtures/migrations_with_missing_versions/4_innocent_jointable.rb → migrations/interleaved/pass_2/3_innocent_jointable.rb} +0 -0
- data/test/{fixtures/migrations_with_missing_versions → migrations/interleaved/pass_3}/1_people_have_last_names.rb +1 -1
- data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
- data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
- data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/1000_people_have_middle_names.rb +1 -1
- data/test/migrations/missing/1_people_have_last_names.rb +9 -0
- data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/3_we_need_reminders.rb +1 -1
- data/test/migrations/missing/4_innocent_jointable.rb +12 -0
- data/test/migrations/valid/1_people_have_last_names.rb +9 -0
- data/test/{fixtures/migrations → migrations/valid}/2_we_need_reminders.rb +1 -1
- data/test/migrations/valid/3_innocent_jointable.rb +12 -0
- data/test/{fixtures → models}/author.rb +28 -4
- data/test/{fixtures → models}/auto_id.rb +0 -0
- data/test/{fixtures → models}/binary.rb +0 -0
- data/test/{fixtures → models}/book.rb +0 -0
- data/test/{fixtures → models}/categorization.rb +0 -0
- data/test/{fixtures → models}/category.rb +8 -5
- data/test/{fixtures → models}/citation.rb +0 -0
- data/test/models/club.rb +7 -0
- data/test/{fixtures → models}/column_name.rb +0 -0
- data/test/{fixtures → models}/comment.rb +5 -3
- data/test/{fixtures → models}/company.rb +15 -6
- data/test/{fixtures → models}/company_in_module.rb +5 -3
- data/test/{fixtures → models}/computer.rb +0 -1
- data/test/{fixtures → models}/contact.rb +1 -1
- data/test/{fixtures → models}/course.rb +0 -0
- data/test/{fixtures → models}/customer.rb +8 -8
- data/test/{fixtures → models}/default.rb +0 -0
- data/test/{fixtures → models}/developer.rb +14 -10
- data/test/{fixtures → models}/edge.rb +0 -0
- data/test/{fixtures → models}/entrant.rb +0 -0
- data/test/models/guid.rb +2 -0
- data/test/{fixtures → models}/item.rb +0 -0
- data/test/models/job.rb +5 -0
- data/test/{fixtures → models}/joke.rb +0 -0
- data/test/{fixtures → models}/keyboard.rb +0 -0
- data/test/{fixtures → models}/legacy_thing.rb +0 -0
- data/test/{fixtures → models}/matey.rb +0 -0
- data/test/models/member.rb +9 -0
- data/test/models/membership.rb +9 -0
- data/test/{fixtures → models}/minimalistic.rb +0 -0
- data/test/{fixtures → models}/mixed_case_monkey.rb +0 -0
- data/test/{fixtures → models}/movie.rb +0 -0
- data/test/{fixtures → models}/order.rb +2 -2
- data/test/models/owner.rb +4 -0
- data/test/{fixtures → models}/parrot.rb +0 -0
- data/test/models/person.rb +10 -0
- data/test/models/pet.rb +4 -0
- data/test/models/pirate.rb +9 -0
- data/test/{fixtures → models}/post.rb +23 -2
- data/test/models/price_estimate.rb +3 -0
- data/test/{fixtures → models}/project.rb +1 -0
- data/test/{fixtures → models}/reader.rb +0 -0
- data/test/models/reference.rb +4 -0
- data/test/{fixtures → models}/reply.rb +7 -5
- data/test/{fixtures → models}/ship.rb +0 -0
- data/test/models/sponsor.rb +4 -0
- data/test/{fixtures → models}/subject.rb +0 -0
- data/test/{fixtures → models}/subscriber.rb +2 -0
- data/test/models/subscription.rb +4 -0
- data/test/{fixtures → models}/tag.rb +0 -0
- data/test/{fixtures → models}/tagging.rb +0 -0
- data/test/{fixtures → models}/task.rb +0 -0
- data/test/{fixtures → models}/topic.rb +32 -4
- data/test/{fixtures → models}/treasure.rb +2 -0
- data/test/{fixtures → models}/vertex.rb +0 -0
- data/test/models/warehouse_thing.rb +5 -0
- data/test/schema/mysql_specific_schema.rb +12 -0
- data/test/schema/postgresql_specific_schema.rb +103 -0
- data/test/schema/schema.rb +421 -0
- data/test/schema/schema2.rb +6 -0
- data/test/schema/sqlite_specific_schema.rb +25 -0
- data/test/schema/sqlserver_specific_schema.rb +5 -0
- metadata +192 -176
- data/test/aaa_create_tables_test.rb +0 -72
- data/test/abstract_unit.rb +0 -84
- data/test/active_schema_test_mysql.rb +0 -46
- data/test/all.sh +0 -8
- data/test/association_inheritance_reload.rb +0 -14
- data/test/associations_test.rb +0 -2177
- data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +0 -1
- data/test/fixtures/bad_fixtures/attr_with_spaces +0 -1
- data/test/fixtures/bad_fixtures/blank_line +0 -3
- data/test/fixtures/bad_fixtures/duplicate_attributes +0 -3
- data/test/fixtures/bad_fixtures/missing_value +0 -1
- data/test/fixtures/db_definitions/db2.drop.sql +0 -33
- data/test/fixtures/db_definitions/db2.sql +0 -235
- data/test/fixtures/db_definitions/db22.drop.sql +0 -2
- data/test/fixtures/db_definitions/db22.sql +0 -5
- data/test/fixtures/db_definitions/firebird.drop.sql +0 -65
- data/test/fixtures/db_definitions/firebird.sql +0 -310
- data/test/fixtures/db_definitions/firebird2.drop.sql +0 -2
- data/test/fixtures/db_definitions/firebird2.sql +0 -6
- data/test/fixtures/db_definitions/frontbase.drop.sql +0 -33
- data/test/fixtures/db_definitions/frontbase.sql +0 -273
- data/test/fixtures/db_definitions/frontbase2.drop.sql +0 -1
- data/test/fixtures/db_definitions/frontbase2.sql +0 -4
- data/test/fixtures/db_definitions/openbase.drop.sql +0 -2
- data/test/fixtures/db_definitions/openbase.sql +0 -318
- data/test/fixtures/db_definitions/openbase2.drop.sql +0 -2
- data/test/fixtures/db_definitions/openbase2.sql +0 -7
- data/test/fixtures/db_definitions/oracle.drop.sql +0 -67
- data/test/fixtures/db_definitions/oracle.sql +0 -330
- data/test/fixtures/db_definitions/oracle2.drop.sql +0 -2
- data/test/fixtures/db_definitions/oracle2.sql +0 -6
- data/test/fixtures/db_definitions/postgresql.drop.sql +0 -44
- data/test/fixtures/db_definitions/postgresql.sql +0 -292
- data/test/fixtures/db_definitions/postgresql2.drop.sql +0 -2
- data/test/fixtures/db_definitions/postgresql2.sql +0 -4
- data/test/fixtures/db_definitions/schema.rb +0 -354
- data/test/fixtures/db_definitions/schema2.rb +0 -11
- data/test/fixtures/db_definitions/sqlite.drop.sql +0 -33
- data/test/fixtures/db_definitions/sqlite.sql +0 -219
- data/test/fixtures/db_definitions/sqlite2.drop.sql +0 -2
- data/test/fixtures/db_definitions/sqlite2.sql +0 -5
- data/test/fixtures/db_definitions/sybase.drop.sql +0 -35
- data/test/fixtures/db_definitions/sybase.sql +0 -222
- data/test/fixtures/db_definitions/sybase2.drop.sql +0 -4
- data/test/fixtures/db_definitions/sybase2.sql +0 -5
- data/test/fixtures/developers_projects/david_action_controller +0 -3
- data/test/fixtures/developers_projects/david_active_record +0 -3
- data/test/fixtures/developers_projects/jamis_active_record +0 -2
- data/test/fixtures/person.rb +0 -4
- data/test/fixtures/pirate.rb +0 -5
- data/test/fixtures/subscribers/first +0 -2
- data/test/fixtures/subscribers/second +0 -2
- data/test/schema_dumper_test.rb +0 -131
- data/test/schema_test_postgresql.rb +0 -64
@@ -0,0 +1,190 @@
|
|
1
|
+
require "cases/helper"
|
2
|
+
require 'models/post'
|
3
|
+
require 'models/person'
|
4
|
+
require 'models/reader'
|
5
|
+
|
6
|
+
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
|
7
|
+
fixtures :posts, :readers, :people
|
8
|
+
|
9
|
+
def test_associate_existing
|
10
|
+
assert_queries(2) { posts(:thinking);people(:david) }
|
11
|
+
|
12
|
+
assert_queries(1) do
|
13
|
+
posts(:thinking).people << people(:david)
|
14
|
+
end
|
15
|
+
|
16
|
+
assert_queries(1) do
|
17
|
+
assert posts(:thinking).people.include?(people(:david))
|
18
|
+
end
|
19
|
+
|
20
|
+
assert posts(:thinking).reload.people(true).include?(people(:david))
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_associating_new
|
24
|
+
assert_queries(1) { posts(:thinking) }
|
25
|
+
new_person = nil # so block binding catches it
|
26
|
+
|
27
|
+
assert_queries(0) do
|
28
|
+
new_person = Person.new :first_name => 'bob'
|
29
|
+
end
|
30
|
+
|
31
|
+
# Associating new records always saves them
|
32
|
+
# Thus, 1 query for the new person record, 1 query for the new join table record
|
33
|
+
assert_queries(2) do
|
34
|
+
posts(:thinking).people << new_person
|
35
|
+
end
|
36
|
+
|
37
|
+
assert_queries(1) do
|
38
|
+
assert posts(:thinking).people.include?(new_person)
|
39
|
+
end
|
40
|
+
|
41
|
+
assert posts(:thinking).reload.people(true).include?(new_person)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_associate_new_by_building
|
45
|
+
assert_queries(1) { posts(:thinking) }
|
46
|
+
|
47
|
+
assert_queries(0) do
|
48
|
+
posts(:thinking).people.build(:first_name=>"Bob")
|
49
|
+
posts(:thinking).people.new(:first_name=>"Ted")
|
50
|
+
end
|
51
|
+
|
52
|
+
# Should only need to load the association once
|
53
|
+
assert_queries(1) do
|
54
|
+
assert posts(:thinking).people.collect(&:first_name).include?("Bob")
|
55
|
+
assert posts(:thinking).people.collect(&:first_name).include?("Ted")
|
56
|
+
end
|
57
|
+
|
58
|
+
# 2 queries for each new record (1 to save the record itself, 1 for the join model)
|
59
|
+
# * 2 new records = 4
|
60
|
+
# + 1 query to save the actual post = 5
|
61
|
+
assert_queries(5) do
|
62
|
+
posts(:thinking).body += '-changed'
|
63
|
+
posts(:thinking).save
|
64
|
+
end
|
65
|
+
|
66
|
+
assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Bob")
|
67
|
+
assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Ted")
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_delete_association
|
71
|
+
assert_queries(2){posts(:welcome);people(:michael); }
|
72
|
+
|
73
|
+
assert_queries(1) do
|
74
|
+
posts(:welcome).people.delete(people(:michael))
|
75
|
+
end
|
76
|
+
|
77
|
+
assert_queries(1) do
|
78
|
+
assert posts(:welcome).people.empty?
|
79
|
+
end
|
80
|
+
|
81
|
+
assert posts(:welcome).reload.people(true).empty?
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_replace_association
|
85
|
+
assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)}
|
86
|
+
|
87
|
+
# 1 query to delete the existing reader (michael)
|
88
|
+
# 1 query to associate the new reader (david)
|
89
|
+
assert_queries(2) do
|
90
|
+
posts(:welcome).people = [people(:david)]
|
91
|
+
end
|
92
|
+
|
93
|
+
assert_queries(0){
|
94
|
+
assert posts(:welcome).people.include?(people(:david))
|
95
|
+
assert !posts(:welcome).people.include?(people(:michael))
|
96
|
+
}
|
97
|
+
|
98
|
+
assert posts(:welcome).reload.people(true).include?(people(:david))
|
99
|
+
assert !posts(:welcome).reload.people(true).include?(people(:michael))
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_associate_with_create
|
103
|
+
assert_queries(1) { posts(:thinking) }
|
104
|
+
|
105
|
+
# 1 query for the new record, 1 for the join table record
|
106
|
+
# No need to update the actual collection yet!
|
107
|
+
assert_queries(2) do
|
108
|
+
posts(:thinking).people.create(:first_name=>"Jeb")
|
109
|
+
end
|
110
|
+
|
111
|
+
# *Now* we actually need the collection so it's loaded
|
112
|
+
assert_queries(1) do
|
113
|
+
assert posts(:thinking).people.collect(&:first_name).include?("Jeb")
|
114
|
+
end
|
115
|
+
|
116
|
+
assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Jeb")
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_associate_with_create_and_no_options
|
120
|
+
peeps = posts(:thinking).people.count
|
121
|
+
posts(:thinking).people.create(:first_name => 'foo')
|
122
|
+
assert_equal peeps + 1, posts(:thinking).people.count
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_associate_with_create_exclamation_and_no_options
|
126
|
+
peeps = posts(:thinking).people.count
|
127
|
+
posts(:thinking).people.create!(:first_name => 'foo')
|
128
|
+
assert_equal peeps + 1, posts(:thinking).people.count
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_clear_associations
|
132
|
+
assert_queries(2) { posts(:welcome);posts(:welcome).people(true) }
|
133
|
+
|
134
|
+
assert_queries(1) do
|
135
|
+
posts(:welcome).people.clear
|
136
|
+
end
|
137
|
+
|
138
|
+
assert_queries(0) do
|
139
|
+
assert posts(:welcome).people.empty?
|
140
|
+
end
|
141
|
+
|
142
|
+
assert posts(:welcome).reload.people(true).empty?
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_association_callback_ordering
|
146
|
+
Post.reset_log
|
147
|
+
log = Post.log
|
148
|
+
post = posts(:thinking)
|
149
|
+
|
150
|
+
post.people_with_callbacks << people(:michael)
|
151
|
+
assert_equal [
|
152
|
+
[:added, :before, "Michael"],
|
153
|
+
[:added, :after, "Michael"]
|
154
|
+
], log.last(2)
|
155
|
+
|
156
|
+
post.people_with_callbacks.push(people(:david), Person.create!(:first_name => "Bob"), Person.new(:first_name => "Lary"))
|
157
|
+
assert_equal [
|
158
|
+
[:added, :before, "David"],
|
159
|
+
[:added, :after, "David"],
|
160
|
+
[:added, :before, "Bob"],
|
161
|
+
[:added, :after, "Bob"],
|
162
|
+
[:added, :before, "Lary"],
|
163
|
+
[:added, :after, "Lary"]
|
164
|
+
],log.last(6)
|
165
|
+
|
166
|
+
post.people_with_callbacks.build(:first_name => "Ted")
|
167
|
+
assert_equal [
|
168
|
+
[:added, :before, "Ted"],
|
169
|
+
[:added, :after, "Ted"]
|
170
|
+
], log.last(2)
|
171
|
+
|
172
|
+
post.people_with_callbacks.create(:first_name => "Sam")
|
173
|
+
assert_equal [
|
174
|
+
[:added, :before, "Sam"],
|
175
|
+
[:added, :after, "Sam"]
|
176
|
+
], log.last(2)
|
177
|
+
|
178
|
+
post.people_with_callbacks = [people(:michael),people(:david), Person.new(:first_name => "Julian"), Person.create!(:first_name => "Roger")]
|
179
|
+
assert_equal (%w(Ted Bob Sam Lary) * 2).sort, log[-12..-5].collect(&:last).sort
|
180
|
+
assert_equal [
|
181
|
+
[:added, :before, "Julian"],
|
182
|
+
[:added, :after, "Julian"],
|
183
|
+
[:added, :before, "Roger"],
|
184
|
+
[:added, :after, "Roger"]
|
185
|
+
], log.last(4)
|
186
|
+
|
187
|
+
post.people_with_callbacks.clear
|
188
|
+
assert_equal (%w(Michael David Julian Roger) * 2).sort, log.last(8).collect(&:last).sort
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,323 @@
|
|
1
|
+
require "cases/helper"
|
2
|
+
require 'models/developer'
|
3
|
+
require 'models/project'
|
4
|
+
require 'models/company'
|
5
|
+
|
6
|
+
class HasOneAssociationsTest < ActiveRecord::TestCase
|
7
|
+
fixtures :accounts, :companies, :developers, :projects, :developers_projects
|
8
|
+
|
9
|
+
def setup
|
10
|
+
Account.destroyed_account_ids.clear
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_has_one
|
14
|
+
assert_equal companies(:first_firm).account, Account.find(1)
|
15
|
+
assert_equal Account.find(1).credit_limit, companies(:first_firm).account.credit_limit
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_has_one_cache_nils
|
19
|
+
firm = companies(:another_firm)
|
20
|
+
assert_queries(1) { assert_nil firm.account }
|
21
|
+
assert_queries(0) { assert_nil firm.account }
|
22
|
+
|
23
|
+
firms = Firm.find(:all, :include => :account)
|
24
|
+
assert_queries(0) { firms.each(&:account) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_with_select
|
28
|
+
assert_equal Firm.find(1).account_with_select.attributes.size, 2
|
29
|
+
assert_equal Firm.find(1, :include => :account_with_select).account_with_select.attributes.size, 2
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_can_marshal_has_one_association_with_nil_target
|
33
|
+
firm = Firm.new
|
34
|
+
assert_nothing_raised do
|
35
|
+
assert_equal firm.attributes, Marshal.load(Marshal.dump(firm)).attributes
|
36
|
+
end
|
37
|
+
|
38
|
+
firm.account
|
39
|
+
assert_nothing_raised do
|
40
|
+
assert_equal firm.attributes, Marshal.load(Marshal.dump(firm)).attributes
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_proxy_assignment
|
45
|
+
company = companies(:first_firm)
|
46
|
+
assert_nothing_raised { company.account = company.account }
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_triple_equality
|
50
|
+
assert Account === companies(:first_firm).account
|
51
|
+
assert companies(:first_firm).account === Account
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_type_mismatch
|
55
|
+
assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = 1 }
|
56
|
+
assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = Project.find(1) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_natural_assignment
|
60
|
+
apple = Firm.create("name" => "Apple")
|
61
|
+
citibank = Account.create("credit_limit" => 10)
|
62
|
+
apple.account = citibank
|
63
|
+
assert_equal apple.id, citibank.firm_id
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_natural_assignment_to_nil
|
67
|
+
old_account_id = companies(:first_firm).account.id
|
68
|
+
companies(:first_firm).account = nil
|
69
|
+
companies(:first_firm).save
|
70
|
+
assert_nil companies(:first_firm).account
|
71
|
+
# account is dependent, therefore is destroyed when reference to owner is lost
|
72
|
+
assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_assignment_without_replacement
|
76
|
+
apple = Firm.create("name" => "Apple")
|
77
|
+
citibank = Account.create("credit_limit" => 10)
|
78
|
+
apple.account = citibank
|
79
|
+
assert_equal apple.id, citibank.firm_id
|
80
|
+
|
81
|
+
hsbc = apple.build_account({ :credit_limit => 20}, false)
|
82
|
+
assert_equal apple.id, hsbc.firm_id
|
83
|
+
hsbc.save
|
84
|
+
assert_equal apple.id, citibank.firm_id
|
85
|
+
|
86
|
+
nykredit = apple.create_account({ :credit_limit => 30}, false)
|
87
|
+
assert_equal apple.id, nykredit.firm_id
|
88
|
+
assert_equal apple.id, citibank.firm_id
|
89
|
+
assert_equal apple.id, hsbc.firm_id
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_assignment_without_replacement_on_create
|
93
|
+
apple = Firm.create("name" => "Apple")
|
94
|
+
citibank = Account.create("credit_limit" => 10)
|
95
|
+
apple.account = citibank
|
96
|
+
assert_equal apple.id, citibank.firm_id
|
97
|
+
|
98
|
+
hsbc = apple.create_account({:credit_limit => 10}, false)
|
99
|
+
assert_equal apple.id, hsbc.firm_id
|
100
|
+
hsbc.save
|
101
|
+
assert_equal apple.id, citibank.firm_id
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_dependence
|
105
|
+
num_accounts = Account.count
|
106
|
+
|
107
|
+
firm = Firm.find(1)
|
108
|
+
assert !firm.account.nil?
|
109
|
+
account_id = firm.account.id
|
110
|
+
assert_equal [], Account.destroyed_account_ids[firm.id]
|
111
|
+
|
112
|
+
firm.destroy
|
113
|
+
assert_equal num_accounts - 1, Account.count
|
114
|
+
assert_equal [account_id], Account.destroyed_account_ids[firm.id]
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_exclusive_dependence
|
118
|
+
num_accounts = Account.count
|
119
|
+
|
120
|
+
firm = ExclusivelyDependentFirm.find(9)
|
121
|
+
assert !firm.account.nil?
|
122
|
+
account_id = firm.account.id
|
123
|
+
assert_equal [], Account.destroyed_account_ids[firm.id]
|
124
|
+
|
125
|
+
firm.destroy
|
126
|
+
assert_equal num_accounts - 1, Account.count
|
127
|
+
assert_equal [], Account.destroyed_account_ids[firm.id]
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_dependence_with_nil_associate
|
131
|
+
firm = DependentFirm.new(:name => 'nullify')
|
132
|
+
firm.save!
|
133
|
+
assert_nothing_raised { firm.destroy }
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_succesful_build_association
|
137
|
+
firm = Firm.new("name" => "GlobalMegaCorp")
|
138
|
+
firm.save
|
139
|
+
|
140
|
+
account = firm.build_account("credit_limit" => 1000)
|
141
|
+
assert account.save
|
142
|
+
assert_equal account, firm.account
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_failing_build_association
|
146
|
+
firm = Firm.new("name" => "GlobalMegaCorp")
|
147
|
+
firm.save
|
148
|
+
|
149
|
+
account = firm.build_account
|
150
|
+
assert !account.save
|
151
|
+
assert_equal "can't be empty", account.errors.on("credit_limit")
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_build_association_twice_without_saving_affects_nothing
|
155
|
+
count_of_account = Account.count
|
156
|
+
firm = Firm.find(:first)
|
157
|
+
account1 = firm.build_account("credit_limit" => 1000)
|
158
|
+
account2 = firm.build_account("credit_limit" => 2000)
|
159
|
+
|
160
|
+
assert_equal count_of_account, Account.count
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_create_association
|
164
|
+
firm = Firm.create(:name => "GlobalMegaCorp")
|
165
|
+
account = firm.create_account(:credit_limit => 1000)
|
166
|
+
assert_equal account, firm.reload.account
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_build
|
170
|
+
firm = Firm.new("name" => "GlobalMegaCorp")
|
171
|
+
firm.save
|
172
|
+
|
173
|
+
firm.account = account = Account.new("credit_limit" => 1000)
|
174
|
+
assert_equal account, firm.account
|
175
|
+
assert account.save
|
176
|
+
assert_equal account, firm.account
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_build_before_child_saved
|
180
|
+
firm = Firm.find(1)
|
181
|
+
|
182
|
+
account = firm.account.build("credit_limit" => 1000)
|
183
|
+
assert_equal account, firm.account
|
184
|
+
assert account.new_record?
|
185
|
+
assert firm.save
|
186
|
+
assert_equal account, firm.account
|
187
|
+
assert !account.new_record?
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_build_before_either_saved
|
191
|
+
firm = Firm.new("name" => "GlobalMegaCorp")
|
192
|
+
|
193
|
+
firm.account = account = Account.new("credit_limit" => 1000)
|
194
|
+
assert_equal account, firm.account
|
195
|
+
assert account.new_record?
|
196
|
+
assert firm.save
|
197
|
+
assert_equal account, firm.account
|
198
|
+
assert !account.new_record?
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_failing_build_association
|
202
|
+
firm = Firm.new("name" => "GlobalMegaCorp")
|
203
|
+
firm.save
|
204
|
+
|
205
|
+
firm.account = account = Account.new
|
206
|
+
assert_equal account, firm.account
|
207
|
+
assert !account.save
|
208
|
+
assert_equal account, firm.account
|
209
|
+
assert_equal "can't be empty", account.errors.on("credit_limit")
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_create
|
213
|
+
firm = Firm.new("name" => "GlobalMegaCorp")
|
214
|
+
firm.save
|
215
|
+
firm.account = account = Account.create("credit_limit" => 1000)
|
216
|
+
assert_equal account, firm.account
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_create_before_save
|
220
|
+
firm = Firm.new("name" => "GlobalMegaCorp")
|
221
|
+
firm.account = account = Account.create("credit_limit" => 1000)
|
222
|
+
assert_equal account, firm.account
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_dependence_with_missing_association
|
226
|
+
Account.destroy_all
|
227
|
+
firm = Firm.find(1)
|
228
|
+
assert firm.account.nil?
|
229
|
+
firm.destroy
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_dependence_with_missing_association_and_nullify
|
233
|
+
Account.destroy_all
|
234
|
+
firm = DependentFirm.find(:first)
|
235
|
+
assert firm.account.nil?
|
236
|
+
firm.destroy
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_assignment_before_parent_saved
|
240
|
+
firm = Firm.new("name" => "GlobalMegaCorp")
|
241
|
+
firm.account = a = Account.find(1)
|
242
|
+
assert firm.new_record?
|
243
|
+
assert_equal a, firm.account
|
244
|
+
assert firm.save
|
245
|
+
assert_equal a, firm.account
|
246
|
+
assert_equal a, firm.account(true)
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_finding_with_interpolated_condition
|
250
|
+
firm = Firm.find(:first)
|
251
|
+
superior = firm.clients.create(:name => 'SuperiorCo')
|
252
|
+
superior.rating = 10
|
253
|
+
superior.save
|
254
|
+
assert_equal 10, firm.clients_with_interpolated_conditions.first.rating
|
255
|
+
end
|
256
|
+
|
257
|
+
def test_assignment_before_child_saved
|
258
|
+
firm = Firm.find(1)
|
259
|
+
firm.account = a = Account.new("credit_limit" => 1000)
|
260
|
+
assert !a.new_record?
|
261
|
+
assert_equal a, firm.account
|
262
|
+
assert_equal a, firm.account
|
263
|
+
assert_equal a, firm.account(true)
|
264
|
+
end
|
265
|
+
|
266
|
+
def test_save_fails_for_invalid_has_one
|
267
|
+
firm = Firm.find(:first)
|
268
|
+
assert firm.valid?
|
269
|
+
|
270
|
+
firm.account = Account.new
|
271
|
+
|
272
|
+
assert !firm.account.valid?
|
273
|
+
assert !firm.valid?
|
274
|
+
assert !firm.save
|
275
|
+
assert_equal "is invalid", firm.errors.on("account")
|
276
|
+
end
|
277
|
+
|
278
|
+
def test_assignment_before_either_saved
|
279
|
+
firm = Firm.new("name" => "GlobalMegaCorp")
|
280
|
+
firm.account = a = Account.new("credit_limit" => 1000)
|
281
|
+
assert firm.new_record?
|
282
|
+
assert a.new_record?
|
283
|
+
assert_equal a, firm.account
|
284
|
+
assert firm.save
|
285
|
+
assert !firm.new_record?
|
286
|
+
assert !a.new_record?
|
287
|
+
assert_equal a, firm.account
|
288
|
+
assert_equal a, firm.account(true)
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_not_resaved_when_unchanged
|
292
|
+
firm = Firm.find(:first, :include => :account)
|
293
|
+
firm.name += '-changed'
|
294
|
+
assert_queries(1) { firm.save! }
|
295
|
+
|
296
|
+
firm = Firm.find(:first)
|
297
|
+
firm.account = Account.find(:first)
|
298
|
+
assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }
|
299
|
+
|
300
|
+
firm = Firm.find(:first).clone
|
301
|
+
firm.account = Account.find(:first)
|
302
|
+
assert_queries(2) { firm.save! }
|
303
|
+
|
304
|
+
firm = Firm.find(:first).clone
|
305
|
+
firm.account = Account.find(:first).clone
|
306
|
+
assert_queries(2) { firm.save! }
|
307
|
+
end
|
308
|
+
|
309
|
+
def test_save_still_works_after_accessing_nil_has_one
|
310
|
+
jp = Company.new :name => 'Jaded Pixel'
|
311
|
+
jp.dummy_account.nil?
|
312
|
+
|
313
|
+
assert_nothing_raised do
|
314
|
+
jp.save!
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def test_cant_save_readonly_association
|
319
|
+
assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_firm).readonly_account.save! }
|
320
|
+
assert companies(:first_firm).readonly_account.readonly?
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|