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.

Files changed (289) hide show
  1. data/CHANGELOG +168 -6
  2. data/README +27 -22
  3. data/RUNNING_UNIT_TESTS +7 -4
  4. data/Rakefile +22 -25
  5. data/lib/active_record.rb +8 -2
  6. data/lib/active_record/aggregations.rb +21 -12
  7. data/lib/active_record/association_preload.rb +277 -0
  8. data/lib/active_record/associations.rb +481 -295
  9. data/lib/active_record/associations/association_collection.rb +162 -37
  10. data/lib/active_record/associations/association_proxy.rb +71 -7
  11. data/lib/active_record/associations/belongs_to_association.rb +5 -3
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -6
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -64
  14. data/lib/active_record/associations/has_many_association.rb +8 -73
  15. data/lib/active_record/associations/has_many_through_association.rb +68 -117
  16. data/lib/active_record/associations/has_one_association.rb +7 -5
  17. data/lib/active_record/associations/has_one_through_association.rb +28 -0
  18. data/lib/active_record/attribute_methods.rb +69 -19
  19. data/lib/active_record/base.rb +496 -275
  20. data/lib/active_record/calculations.rb +28 -21
  21. data/lib/active_record/callbacks.rb +9 -38
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -2
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +232 -45
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +141 -27
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -13
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +57 -24
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +143 -42
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  31. data/lib/active_record/connection_adapters/sqlite_adapter.rb +18 -10
  32. data/lib/active_record/dirty.rb +158 -0
  33. data/lib/active_record/fixtures.rb +121 -156
  34. data/lib/active_record/locking/optimistic.rb +14 -11
  35. data/lib/active_record/locking/pessimistic.rb +2 -2
  36. data/lib/active_record/migration.rb +157 -77
  37. data/lib/active_record/named_scope.rb +163 -0
  38. data/lib/active_record/observer.rb +19 -5
  39. data/lib/active_record/reflection.rb +34 -14
  40. data/lib/active_record/schema.rb +7 -14
  41. data/lib/active_record/schema_dumper.rb +4 -4
  42. data/lib/active_record/serialization.rb +5 -5
  43. data/lib/active_record/serializers/json_serializer.rb +37 -28
  44. data/lib/active_record/serializers/xml_serializer.rb +52 -29
  45. data/lib/active_record/test_case.rb +36 -0
  46. data/lib/active_record/timestamp.rb +4 -4
  47. data/lib/active_record/transactions.rb +3 -3
  48. data/lib/active_record/validations.rb +182 -248
  49. data/lib/active_record/version.rb +2 -2
  50. data/test/{fixtures → assets}/example.log +0 -0
  51. data/test/{fixtures → assets}/flowers.jpg +0 -0
  52. data/test/cases/aaa_create_tables_test.rb +24 -0
  53. data/test/cases/active_schema_test_mysql.rb +95 -0
  54. data/test/cases/active_schema_test_postgresql.rb +24 -0
  55. data/test/{adapter_test.rb → cases/adapter_test.rb} +15 -14
  56. data/test/{adapter_test_sqlserver.rb → cases/adapter_test_sqlserver.rb} +95 -95
  57. data/test/{aggregations_test.rb → cases/aggregations_test.rb} +20 -20
  58. data/test/{ar_schema_test.rb → cases/ar_schema_test.rb} +6 -6
  59. data/test/cases/associations/belongs_to_associations_test.rb +412 -0
  60. data/test/{associations → cases/associations}/callbacks_test.rb +24 -10
  61. data/test/{associations → cases/associations}/cascaded_eager_loading_test.rb +18 -17
  62. data/test/cases/associations/eager_load_nested_include_test.rb +83 -0
  63. data/test/{associations → cases/associations}/eager_singularization_test.rb +5 -5
  64. data/test/{associations → cases/associations}/eager_test.rb +216 -51
  65. data/test/{associations → cases/associations}/extension_test.rb +8 -8
  66. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +684 -0
  67. data/test/cases/associations/has_many_associations_test.rb +932 -0
  68. data/test/cases/associations/has_many_through_associations_test.rb +190 -0
  69. data/test/cases/associations/has_one_associations_test.rb +323 -0
  70. data/test/cases/associations/has_one_through_associations_test.rb +74 -0
  71. data/test/{associations → cases/associations}/inner_join_association_test.rb +20 -20
  72. data/test/{associations → cases/associations}/join_model_test.rb +175 -35
  73. data/test/cases/associations_test.rb +262 -0
  74. data/test/{attribute_methods_test.rb → cases/attribute_methods_test.rb} +103 -11
  75. data/test/{base_test.rb → cases/base_test.rb} +338 -191
  76. data/test/{binary_test.rb → cases/binary_test.rb} +6 -4
  77. data/test/{calculations_test.rb → cases/calculations_test.rb} +35 -23
  78. data/test/{callbacks_test.rb → cases/callbacks_test.rb} +7 -7
  79. data/test/{class_inheritable_attributes_test.rb → cases/class_inheritable_attributes_test.rb} +3 -3
  80. data/test/{column_alias_test.rb → cases/column_alias_test.rb} +3 -3
  81. data/test/{connection_test_firebird.rb → cases/connection_test_firebird.rb} +2 -2
  82. data/test/{connection_test_mysql.rb → cases/connection_test_mysql.rb} +2 -2
  83. data/test/{copy_table_test_sqlite.rb → cases/copy_table_test_sqlite.rb} +13 -13
  84. data/test/{datatype_test_postgresql.rb → cases/datatype_test_postgresql.rb} +8 -8
  85. data/test/{date_time_test.rb → cases/date_time_test.rb} +5 -5
  86. data/test/{default_test_firebird.rb → cases/default_test_firebird.rb} +3 -3
  87. data/test/{defaults_test.rb → cases/defaults_test.rb} +8 -6
  88. data/test/{deprecated_finder_test.rb → cases/deprecated_finder_test.rb} +3 -3
  89. data/test/cases/dirty_test.rb +163 -0
  90. data/test/cases/finder_respond_to_test.rb +76 -0
  91. data/test/{finder_test.rb → cases/finder_test.rb} +266 -33
  92. data/test/{fixtures_test.rb → cases/fixtures_test.rb} +88 -72
  93. data/test/cases/helper.rb +47 -0
  94. data/test/{inheritance_test.rb → cases/inheritance_test.rb} +61 -17
  95. data/test/cases/invalid_date_test.rb +24 -0
  96. data/test/{json_serialization_test.rb → cases/json_serialization_test.rb} +36 -11
  97. data/test/{lifecycle_test.rb → cases/lifecycle_test.rb} +16 -13
  98. data/test/{locking_test.rb → cases/locking_test.rb} +17 -10
  99. data/test/{method_scoping_test.rb → cases/method_scoping_test.rb} +75 -39
  100. data/test/{migration_test.rb → cases/migration_test.rb} +420 -80
  101. data/test/{migration_test_firebird.rb → cases/migration_test_firebird.rb} +3 -3
  102. data/test/{mixin_test.rb → cases/mixin_test.rb} +7 -6
  103. data/test/{modules_test.rb → cases/modules_test.rb} +11 -6
  104. data/test/{multiple_db_test.rb → cases/multiple_db_test.rb} +5 -5
  105. data/test/cases/named_scope_test.rb +157 -0
  106. data/test/{pk_test.rb → cases/pk_test.rb} +10 -10
  107. data/test/{query_cache_test.rb → cases/query_cache_test.rb} +12 -10
  108. data/test/{readonly_test.rb → cases/readonly_test.rb} +11 -11
  109. data/test/{reflection_test.rb → cases/reflection_test.rb} +15 -14
  110. data/test/{reserved_word_test_mysql.rb → cases/reserved_word_test_mysql.rb} +4 -5
  111. data/test/{schema_authorization_test_postgresql.rb → cases/schema_authorization_test_postgresql.rb} +5 -5
  112. data/test/cases/schema_dumper_test.rb +138 -0
  113. data/test/cases/schema_test_postgresql.rb +102 -0
  114. data/test/{serialization_test.rb → cases/serialization_test.rb} +7 -7
  115. data/test/{synonym_test_oracle.rb → cases/synonym_test_oracle.rb} +5 -5
  116. data/test/{table_name_test_sqlserver.rb → cases/table_name_test_sqlserver.rb} +3 -3
  117. data/test/{threaded_connections_test.rb → cases/threaded_connections_test.rb} +7 -7
  118. data/test/{transactions_test.rb → cases/transactions_test.rb} +31 -5
  119. data/test/{unconnected_test.rb → cases/unconnected_test.rb} +2 -2
  120. data/test/{validations_test.rb → cases/validations_test.rb} +141 -39
  121. data/test/{xml_serialization_test.rb → cases/xml_serialization_test.rb} +12 -12
  122. data/test/config.rb +5 -0
  123. data/test/connections/native_db2/connection.rb +1 -1
  124. data/test/connections/native_firebird/connection.rb +1 -1
  125. data/test/connections/native_frontbase/connection.rb +1 -1
  126. data/test/connections/native_mysql/connection.rb +1 -1
  127. data/test/connections/native_openbase/connection.rb +1 -1
  128. data/test/connections/native_oracle/connection.rb +1 -1
  129. data/test/connections/native_postgresql/connection.rb +1 -3
  130. data/test/connections/native_sqlite/connection.rb +2 -2
  131. data/test/connections/native_sqlite3/connection.rb +2 -2
  132. data/test/connections/native_sqlite3/in_memory_connection.rb +3 -3
  133. data/test/connections/native_sybase/connection.rb +1 -1
  134. data/test/fixtures/author_addresses.yml +5 -0
  135. data/test/fixtures/authors.yml +2 -0
  136. data/test/fixtures/clubs.yml +6 -0
  137. data/test/fixtures/jobs.yml +7 -0
  138. data/test/fixtures/members.yml +4 -0
  139. data/test/fixtures/memberships.yml +20 -0
  140. data/test/fixtures/owners.yml +7 -0
  141. data/test/fixtures/people.yml +4 -1
  142. data/test/fixtures/pets.yml +14 -0
  143. data/test/fixtures/posts.yml +1 -0
  144. data/test/fixtures/price_estimates.yml +7 -0
  145. data/test/fixtures/readers.yml +5 -0
  146. data/test/fixtures/references.yml +17 -0
  147. data/test/fixtures/sponsors.yml +9 -0
  148. data/test/fixtures/subscribers.yml +7 -0
  149. data/test/fixtures/subscriptions.yml +12 -0
  150. data/test/fixtures/taggings.yml +4 -1
  151. data/test/fixtures/topics.yml +22 -2
  152. data/test/fixtures/warehouse-things.yml +3 -0
  153. data/test/{fixtures/migrations_with_decimal → migrations/decimal}/1_give_me_big_numbers.rb +0 -0
  154. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/1_people_have_last_names.rb +1 -1
  155. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/2_we_need_reminders.rb +1 -1
  156. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/3_foo.rb +0 -0
  157. data/test/{fixtures/migrations → migrations/duplicate}/3_innocent_jointable.rb +0 -0
  158. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  159. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  160. data/test/{fixtures/migrations_with_duplicate → migrations/interleaved/pass_1}/3_innocent_jointable.rb +0 -0
  161. data/test/{fixtures/migrations → migrations/interleaved/pass_2}/1_people_have_last_names.rb +1 -1
  162. data/test/{fixtures/migrations_with_missing_versions/4_innocent_jointable.rb → migrations/interleaved/pass_2/3_innocent_jointable.rb} +0 -0
  163. data/test/{fixtures/migrations_with_missing_versions → migrations/interleaved/pass_3}/1_people_have_last_names.rb +1 -1
  164. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  165. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  166. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/1000_people_have_middle_names.rb +1 -1
  167. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  168. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/3_we_need_reminders.rb +1 -1
  169. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  170. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  171. data/test/{fixtures/migrations → migrations/valid}/2_we_need_reminders.rb +1 -1
  172. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  173. data/test/{fixtures → models}/author.rb +28 -4
  174. data/test/{fixtures → models}/auto_id.rb +0 -0
  175. data/test/{fixtures → models}/binary.rb +0 -0
  176. data/test/{fixtures → models}/book.rb +0 -0
  177. data/test/{fixtures → models}/categorization.rb +0 -0
  178. data/test/{fixtures → models}/category.rb +8 -5
  179. data/test/{fixtures → models}/citation.rb +0 -0
  180. data/test/models/club.rb +7 -0
  181. data/test/{fixtures → models}/column_name.rb +0 -0
  182. data/test/{fixtures → models}/comment.rb +5 -3
  183. data/test/{fixtures → models}/company.rb +15 -6
  184. data/test/{fixtures → models}/company_in_module.rb +5 -3
  185. data/test/{fixtures → models}/computer.rb +0 -1
  186. data/test/{fixtures → models}/contact.rb +1 -1
  187. data/test/{fixtures → models}/course.rb +0 -0
  188. data/test/{fixtures → models}/customer.rb +8 -8
  189. data/test/{fixtures → models}/default.rb +0 -0
  190. data/test/{fixtures → models}/developer.rb +14 -10
  191. data/test/{fixtures → models}/edge.rb +0 -0
  192. data/test/{fixtures → models}/entrant.rb +0 -0
  193. data/test/models/guid.rb +2 -0
  194. data/test/{fixtures → models}/item.rb +0 -0
  195. data/test/models/job.rb +5 -0
  196. data/test/{fixtures → models}/joke.rb +0 -0
  197. data/test/{fixtures → models}/keyboard.rb +0 -0
  198. data/test/{fixtures → models}/legacy_thing.rb +0 -0
  199. data/test/{fixtures → models}/matey.rb +0 -0
  200. data/test/models/member.rb +9 -0
  201. data/test/models/membership.rb +9 -0
  202. data/test/{fixtures → models}/minimalistic.rb +0 -0
  203. data/test/{fixtures → models}/mixed_case_monkey.rb +0 -0
  204. data/test/{fixtures → models}/movie.rb +0 -0
  205. data/test/{fixtures → models}/order.rb +2 -2
  206. data/test/models/owner.rb +4 -0
  207. data/test/{fixtures → models}/parrot.rb +0 -0
  208. data/test/models/person.rb +10 -0
  209. data/test/models/pet.rb +4 -0
  210. data/test/models/pirate.rb +9 -0
  211. data/test/{fixtures → models}/post.rb +23 -2
  212. data/test/models/price_estimate.rb +3 -0
  213. data/test/{fixtures → models}/project.rb +1 -0
  214. data/test/{fixtures → models}/reader.rb +0 -0
  215. data/test/models/reference.rb +4 -0
  216. data/test/{fixtures → models}/reply.rb +7 -5
  217. data/test/{fixtures → models}/ship.rb +0 -0
  218. data/test/models/sponsor.rb +4 -0
  219. data/test/{fixtures → models}/subject.rb +0 -0
  220. data/test/{fixtures → models}/subscriber.rb +2 -0
  221. data/test/models/subscription.rb +4 -0
  222. data/test/{fixtures → models}/tag.rb +0 -0
  223. data/test/{fixtures → models}/tagging.rb +0 -0
  224. data/test/{fixtures → models}/task.rb +0 -0
  225. data/test/{fixtures → models}/topic.rb +32 -4
  226. data/test/{fixtures → models}/treasure.rb +2 -0
  227. data/test/{fixtures → models}/vertex.rb +0 -0
  228. data/test/models/warehouse_thing.rb +5 -0
  229. data/test/schema/mysql_specific_schema.rb +12 -0
  230. data/test/schema/postgresql_specific_schema.rb +103 -0
  231. data/test/schema/schema.rb +421 -0
  232. data/test/schema/schema2.rb +6 -0
  233. data/test/schema/sqlite_specific_schema.rb +25 -0
  234. data/test/schema/sqlserver_specific_schema.rb +5 -0
  235. metadata +192 -176
  236. data/test/aaa_create_tables_test.rb +0 -72
  237. data/test/abstract_unit.rb +0 -84
  238. data/test/active_schema_test_mysql.rb +0 -46
  239. data/test/all.sh +0 -8
  240. data/test/association_inheritance_reload.rb +0 -14
  241. data/test/associations_test.rb +0 -2177
  242. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +0 -1
  243. data/test/fixtures/bad_fixtures/attr_with_spaces +0 -1
  244. data/test/fixtures/bad_fixtures/blank_line +0 -3
  245. data/test/fixtures/bad_fixtures/duplicate_attributes +0 -3
  246. data/test/fixtures/bad_fixtures/missing_value +0 -1
  247. data/test/fixtures/db_definitions/db2.drop.sql +0 -33
  248. data/test/fixtures/db_definitions/db2.sql +0 -235
  249. data/test/fixtures/db_definitions/db22.drop.sql +0 -2
  250. data/test/fixtures/db_definitions/db22.sql +0 -5
  251. data/test/fixtures/db_definitions/firebird.drop.sql +0 -65
  252. data/test/fixtures/db_definitions/firebird.sql +0 -310
  253. data/test/fixtures/db_definitions/firebird2.drop.sql +0 -2
  254. data/test/fixtures/db_definitions/firebird2.sql +0 -6
  255. data/test/fixtures/db_definitions/frontbase.drop.sql +0 -33
  256. data/test/fixtures/db_definitions/frontbase.sql +0 -273
  257. data/test/fixtures/db_definitions/frontbase2.drop.sql +0 -1
  258. data/test/fixtures/db_definitions/frontbase2.sql +0 -4
  259. data/test/fixtures/db_definitions/openbase.drop.sql +0 -2
  260. data/test/fixtures/db_definitions/openbase.sql +0 -318
  261. data/test/fixtures/db_definitions/openbase2.drop.sql +0 -2
  262. data/test/fixtures/db_definitions/openbase2.sql +0 -7
  263. data/test/fixtures/db_definitions/oracle.drop.sql +0 -67
  264. data/test/fixtures/db_definitions/oracle.sql +0 -330
  265. data/test/fixtures/db_definitions/oracle2.drop.sql +0 -2
  266. data/test/fixtures/db_definitions/oracle2.sql +0 -6
  267. data/test/fixtures/db_definitions/postgresql.drop.sql +0 -44
  268. data/test/fixtures/db_definitions/postgresql.sql +0 -292
  269. data/test/fixtures/db_definitions/postgresql2.drop.sql +0 -2
  270. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  271. data/test/fixtures/db_definitions/schema.rb +0 -354
  272. data/test/fixtures/db_definitions/schema2.rb +0 -11
  273. data/test/fixtures/db_definitions/sqlite.drop.sql +0 -33
  274. data/test/fixtures/db_definitions/sqlite.sql +0 -219
  275. data/test/fixtures/db_definitions/sqlite2.drop.sql +0 -2
  276. data/test/fixtures/db_definitions/sqlite2.sql +0 -5
  277. data/test/fixtures/db_definitions/sybase.drop.sql +0 -35
  278. data/test/fixtures/db_definitions/sybase.sql +0 -222
  279. data/test/fixtures/db_definitions/sybase2.drop.sql +0 -4
  280. data/test/fixtures/db_definitions/sybase2.sql +0 -5
  281. data/test/fixtures/developers_projects/david_action_controller +0 -3
  282. data/test/fixtures/developers_projects/david_active_record +0 -3
  283. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  284. data/test/fixtures/person.rb +0 -4
  285. data/test/fixtures/pirate.rb +0 -5
  286. data/test/fixtures/subscribers/first +0 -2
  287. data/test/fixtures/subscribers/second +0 -2
  288. data/test/schema_dumper_test.rb +0 -131
  289. data/test/schema_test_postgresql.rb +0 -64
@@ -0,0 +1,74 @@
1
+ require "cases/helper"
2
+ require 'models/club'
3
+ require 'models/member'
4
+ require 'models/membership'
5
+ require 'models/sponsor'
6
+
7
+ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
8
+ fixtures :members, :clubs, :memberships, :sponsors
9
+
10
+ def setup
11
+ @member = members(:groucho)
12
+ end
13
+
14
+ def test_has_one_through_with_has_one
15
+ assert_equal clubs(:boring_club), @member.club
16
+ end
17
+
18
+ def test_has_one_through_with_has_many
19
+ assert_equal clubs(:moustache_club), @member.favourite_club
20
+ end
21
+
22
+ def test_creating_association_creates_through_record
23
+ new_member = Member.create(:name => "Chris")
24
+ new_member.club = Club.create(:name => "LRUG")
25
+ assert_not_nil new_member.current_membership
26
+ assert_not_nil new_member.club
27
+ end
28
+
29
+ def test_replace_target_record
30
+ new_club = Club.create(:name => "Marx Bros")
31
+ @member.club = new_club
32
+ @member.reload
33
+ assert_equal new_club, @member.club
34
+ end
35
+
36
+ def test_replacing_target_record_deletes_old_association
37
+ assert_no_difference "Membership.count" do
38
+ new_club = Club.create(:name => "Bananarama")
39
+ @member.club = new_club
40
+ @member.reload
41
+ end
42
+ end
43
+
44
+ def test_has_one_through_polymorphic
45
+ assert_equal clubs(:moustache_club), @member.sponsor_club
46
+ end
47
+
48
+ def has_one_through_to_has_many
49
+ assert_equal 2, @member.fellow_members.size
50
+ end
51
+
52
+ def test_has_one_through_eager_loading
53
+ members = Member.find(:all, :include => :club, :conditions => ["name = ?", "Groucho Marx"])
54
+ assert_equal 1, members.size
55
+ assert_not_nil assert_no_queries {members[0].club}
56
+ end
57
+
58
+ def test_has_one_through_eager_loading_through_polymorphic
59
+ members = Member.find(:all, :include => :sponsor_club, :conditions => ["name = ?", "Groucho Marx"])
60
+ assert_equal 1, members.size
61
+ assert_not_nil assert_no_queries {members[0].sponsor_club}
62
+ end
63
+
64
+ def test_has_one_through_polymorphic_with_source_type
65
+ assert_equal members(:groucho), clubs(:moustache_club).sponsored_member
66
+ end
67
+
68
+ def test_eager_has_one_through_polymorphic_with_source_type
69
+ clubs = Club.find(:all, :include => :sponsored_member, :conditions => ["name = ?","Moustache and Eyebrow Fancier Club"])
70
+ # Only the eyebrow fanciers club has a sponsored_member
71
+ assert_not_nil assert_no_queries {clubs[0].sponsored_member}
72
+ end
73
+
74
+ end
@@ -1,39 +1,39 @@
1
- require 'abstract_unit'
2
- require 'fixtures/post'
3
- require 'fixtures/comment'
4
- require 'fixtures/author'
5
- require 'fixtures/category'
6
- require 'fixtures/categorization'
1
+ require "cases/helper"
2
+ require 'models/post'
3
+ require 'models/comment'
4
+ require 'models/author'
5
+ require 'models/category'
6
+ require 'models/categorization'
7
7
 
8
- class InnerJoinAssociationTest < Test::Unit::TestCase
8
+ class InnerJoinAssociationTest < ActiveRecord::TestCase
9
9
  fixtures :authors, :posts, :comments, :categories, :categories_posts, :categorizations
10
10
 
11
11
  def test_construct_finder_sql_creates_inner_joins
12
12
  sql = Author.send(:construct_finder_sql, :joins => :posts)
13
- assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql
13
+ assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql
14
14
  end
15
-
15
+
16
16
  def test_construct_finder_sql_cascades_inner_joins
17
17
  sql = Author.send(:construct_finder_sql, :joins => {:posts => :comments})
18
- assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql
19
- assert_match /INNER JOIN `?comments`? ON `?comments`?.post_id = posts.id/, sql
18
+ assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql
19
+ assert_match /INNER JOIN .?comments.? ON .?comments.?.post_id = posts.id/, sql
20
20
  end
21
-
21
+
22
22
  def test_construct_finder_sql_inner_joins_through_associations
23
23
  sql = Author.send(:construct_finder_sql, :joins => :categorized_posts)
24
- assert_match /INNER JOIN `?categorizations`?.*INNER JOIN `?posts`?/, sql
24
+ assert_match /INNER JOIN .?categorizations.?.*INNER JOIN .?posts.?/, sql
25
25
  end
26
-
26
+
27
27
  def test_construct_finder_sql_applies_association_conditions
28
28
  sql = Author.send(:construct_finder_sql, :joins => :categories_like_general, :conditions => "TERMINATING_MARKER")
29
- assert_match /INNER JOIN `?categories`? ON.*AND.*`?General`?.*TERMINATING_MARKER/, sql
29
+ assert_match /INNER JOIN .?categories.? ON.*AND.*.?General.?.*TERMINATING_MARKER/, sql
30
30
  end
31
31
 
32
32
  def test_construct_finder_sql_unpacks_nested_joins
33
33
  sql = Author.send(:construct_finder_sql, :joins => {:posts => [[:comments]]})
34
34
  assert_no_match /inner join.*inner join.*inner join/i, sql, "only two join clauses should be present"
35
- assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql
36
- assert_match /INNER JOIN `?comments`? ON `?comments`?.post_id = `?posts`?.id/, sql
35
+ assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql
36
+ assert_match /INNER JOIN .?comments.? ON .?comments.?.post_id = .?posts.?.id/, sql
37
37
  end
38
38
 
39
39
  def test_construct_finder_sql_ignores_empty_joins_hash
@@ -51,7 +51,7 @@ class InnerJoinAssociationTest < Test::Unit::TestCase
51
51
  assert !authors.empty?, "expected authors to be non-empty"
52
52
  assert authors.all? {|a| a.readonly? }, "expected all authors to be readonly"
53
53
  end
54
-
54
+
55
55
  def test_find_with_implicit_inner_joins_honors_readonly_with_select
56
56
  authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
57
57
  assert !authors.empty?, "expected authors to be non-empty"
@@ -67,9 +67,9 @@ class InnerJoinAssociationTest < Test::Unit::TestCase
67
67
  def test_find_with_implicit_inner_joins_does_not_set_associations
68
68
  authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
69
69
  assert !authors.empty?, "expected authors to be non-empty"
70
- assert authors.all? {|a| !a.send(:instance_variables).include?("@posts")}, "expected no authors to have the @posts association loaded"
70
+ assert authors.all? {|a| !a.send(:instance_variable_names).include?("@posts")}, "expected no authors to have the @posts association loaded"
71
71
  end
72
-
72
+
73
73
  def test_count_honors_implicit_inner_joins
74
74
  real_count = Author.find(:all).sum{|a| a.posts.count }
75
75
  assert_equal real_count, Author.count(:joins => :posts), "plain inner join count should match the number of referenced posts records"
@@ -1,18 +1,18 @@
1
- require 'abstract_unit'
2
- require 'fixtures/tag'
3
- require 'fixtures/tagging'
4
- require 'fixtures/post'
5
- require 'fixtures/item'
6
- require 'fixtures/comment'
7
- require 'fixtures/author'
8
- require 'fixtures/category'
9
- require 'fixtures/categorization'
10
- require 'fixtures/vertex'
11
- require 'fixtures/edge'
12
- require 'fixtures/book'
13
- require 'fixtures/citation'
14
-
15
- class AssociationsJoinModelTest < Test::Unit::TestCase
1
+ require "cases/helper"
2
+ require 'models/tag'
3
+ require 'models/tagging'
4
+ require 'models/post'
5
+ require 'models/item'
6
+ require 'models/comment'
7
+ require 'models/author'
8
+ require 'models/category'
9
+ require 'models/categorization'
10
+ require 'models/vertex'
11
+ require 'models/edge'
12
+ require 'models/book'
13
+ require 'models/citation'
14
+
15
+ class AssociationsJoinModelTest < ActiveRecord::TestCase
16
16
  self.use_transactional_fixtures = false
17
17
  fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :vertices, :items, :books
18
18
 
@@ -32,7 +32,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
32
32
  assert_equal 2, authors(:mary).categorized_posts.size
33
33
  assert_equal 1, authors(:mary).unique_categorized_posts.size
34
34
  end
35
-
35
+
36
36
  def test_has_many_uniq_through_count
37
37
  author = authors(:mary)
38
38
  assert !authors(:mary).unique_categorized_posts.loaded?
@@ -42,6 +42,14 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
42
42
  assert !authors(:mary).unique_categorized_posts.loaded?
43
43
  end
44
44
 
45
+ def test_has_many_uniq_through_find
46
+ assert_equal 1, authors(:mary).unique_categorized_posts.find(:all).size
47
+ end
48
+
49
+ def test_has_many_uniq_through_dynamic_find
50
+ assert_equal 1, authors(:mary).unique_categorized_posts.find_all_by_title("So I was thinking").size
51
+ end
52
+
45
53
  def test_polymorphic_has_many
46
54
  assert posts(:welcome).taggings.include?(taggings(:welcome_general))
47
55
  end
@@ -107,7 +115,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
107
115
  def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class
108
116
  post = SubStiPost.create :title => 'SubStiPost', :body => 'SubStiPost body'
109
117
  assert_instance_of SubStiPost, post
110
-
118
+
111
119
  tagging = tags(:misc).taggings.create(:taggable => post)
112
120
  assert_equal "SubStiPost", tagging.taggable_type
113
121
  end
@@ -123,7 +131,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
123
131
  def test_polymorphic_has_many_create_model_with_inheritance
124
132
  post = posts(:thinking)
125
133
  assert_instance_of SpecialPost, post
126
-
134
+
127
135
  tagging = tags(:misc).taggings.create(:taggable => post)
128
136
  assert_equal "Post", tagging.taggable_type
129
137
  end
@@ -151,7 +159,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
151
159
  assert_equal "Post", tagging.taggable_type
152
160
  assert_equal old_count+1, posts(:welcome).taggings.count
153
161
  end
154
-
162
+
155
163
  def test_create_bang_polymorphic_with_has_many_scope
156
164
  old_count = posts(:welcome).taggings.count
157
165
  tagging = posts(:welcome).taggings.create!(:tag => tags(:misc))
@@ -241,7 +249,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
241
249
  assert_equal tagging, post.tagging
242
250
  end
243
251
  end
244
-
252
+
245
253
  def test_include_polymorphic_has_one_defined_in_abstract_parent
246
254
  item = Item.find_by_id(items(:dvd).id, :include => :tagging)
247
255
  tagging = taggings(:godfather)
@@ -249,7 +257,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
249
257
  assert_equal tagging, item.tagging
250
258
  end
251
259
  end
252
-
260
+
253
261
  def test_include_polymorphic_has_many_through
254
262
  posts = Post.find(:all, :order => 'posts.id')
255
263
  posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
@@ -271,7 +279,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
271
279
  def test_has_many_find_all
272
280
  assert_equal [categories(:general)], authors(:david).categories.find(:all)
273
281
  end
274
-
282
+
275
283
  def test_has_many_find_first
276
284
  assert_equal categories(:general), authors(:david).categories.find(:first)
277
285
  end
@@ -279,7 +287,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
279
287
  def test_has_many_with_hash_conditions
280
288
  assert_equal categories(:general), authors(:david).categories_like_general.find(:first)
281
289
  end
282
-
290
+
283
291
  def test_has_many_find_conditions
284
292
  assert_equal categories(:general), authors(:david).categories.find(:first, :conditions => "categories.name = 'General'")
285
293
  assert_equal nil, authors(:david).categories.find(:first, :conditions => "categories.name = 'Technology'")
@@ -290,10 +298,23 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
290
298
  assert_equal nil, authors(:david).categories.find_by_name('Technology')
291
299
  end
292
300
 
301
+ def test_has_many_array_methods_called_by_method_missing
302
+ assert true, authors(:david).categories.any? { |category| category.name == 'General' }
303
+ assert_nothing_raised { authors(:david).categories.sort }
304
+ end
305
+
293
306
  def test_has_many_going_through_join_model_with_custom_foreign_key
294
307
  assert_equal [], posts(:thinking).authors
295
308
  assert_equal [authors(:mary)], posts(:authorless).authors
296
309
  end
310
+
311
+ def test_both_scoped_and_explicit_joins_should_be_respected
312
+ assert_nothing_raised do
313
+ Post.send(:with_scope, :find => {:joins => "left outer join comments on comments.id = posts.id"}) do
314
+ Post.find :all, :select => "comments.id, authors.id", :joins => "left outer join authors on authors.id = posts.author_id"
315
+ end
316
+ end
317
+ end
297
318
 
298
319
  def test_belongs_to_polymorphic_with_counter_cache
299
320
  assert_equal 0, posts(:welcome)[:taggings_count]
@@ -317,10 +338,10 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
317
338
  assert_equal posts(:welcome, :thinking), tags(:general).taggables
318
339
  end
319
340
  assert_raise ActiveRecord::EagerLoadPolymorphicError do
320
- assert_equal posts(:welcome, :thinking), tags(:general).taggings.find(:all, :include => :taggable)
341
+ assert_equal posts(:welcome, :thinking), tags(:general).taggings.find(:all, :include => :taggable, :conditions => 'bogus_table.column = 1')
321
342
  end
322
343
  end
323
-
344
+
324
345
  def test_has_many_polymorphic_with_source_type
325
346
  assert_equal posts(:welcome, :thinking), tags(:general).tagged_posts
326
347
  end
@@ -331,6 +352,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
331
352
  assert_no_queries do
332
353
  assert_equal desired, tag_with_include.tagged_posts
333
354
  end
355
+ assert_equal 5, tag_with_include.taggings.length
334
356
  end
335
357
 
336
358
  def test_has_many_through_has_many_find_all
@@ -385,7 +407,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
385
407
  assert_equal [1,2,3,5,6,7,8,9,10], author.comments.collect(&:id)
386
408
  end
387
409
  end
388
-
410
+
389
411
  def test_eager_load_has_many_through_has_many_with_conditions
390
412
  post = Post.find(:first, :include => :invalid_tags)
391
413
  assert_no_queries do
@@ -410,7 +432,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
410
432
  authors(:david).author_favorites.create :favorite_author => new_author
411
433
  assert_equal new_author, authors(:david).reload.favorite_authors.first
412
434
  end
413
-
435
+
414
436
  def test_has_many_through_uses_conditions_specified_on_the_has_many_association
415
437
  author = Author.find(:first)
416
438
  assert !author.comments.blank?
@@ -421,11 +443,33 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
421
443
  assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"]
422
444
  end
423
445
 
424
- def test_raise_error_when_adding_new_record_to_has_many_through
425
- assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags << tags(:general).clone }
426
- assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).clone.tags << tags(:general) }
427
- assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags.build }
428
- assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags.new }
446
+ def test_associating_unsaved_records_with_has_many_through
447
+ saved_post = posts(:thinking)
448
+ new_tag = Tag.new(:name => "new")
449
+
450
+ saved_post.tags << new_tag
451
+ assert !new_tag.new_record? #consistent with habtm!
452
+ assert !saved_post.new_record?
453
+ assert saved_post.tags.include?(new_tag)
454
+
455
+ assert !new_tag.new_record?
456
+ assert saved_post.reload.tags(true).include?(new_tag)
457
+
458
+
459
+ new_post = Post.new(:title => "Association replacmenet works!", :body => "You best believe it.")
460
+ saved_tag = tags(:general)
461
+
462
+ new_post.tags << saved_tag
463
+ assert new_post.new_record?
464
+ assert !saved_tag.new_record?
465
+ assert new_post.tags.include?(saved_tag)
466
+
467
+ new_post.save!
468
+ assert !new_post.new_record?
469
+ assert new_post.reload.tags(true).include?(saved_tag)
470
+
471
+ assert posts(:thinking).tags.build.new_record?
472
+ assert posts(:thinking).tags.new.new_record?
429
473
  end
430
474
 
431
475
  def test_create_associate_when_adding_to_has_many_through
@@ -504,11 +548,13 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
504
548
  tag = Tag.create!(:name => 'doomed')
505
549
  post_thinking = posts(:thinking)
506
550
  post_thinking.tags << tag
551
+ assert_equal(count + 1, post_thinking.taggings(true).size)
507
552
  assert_equal(count + 1, post_thinking.tags(true).size)
508
553
 
509
- assert_nothing_raised { post_thinking.tags.delete(tag) }
554
+ assert_nothing_raised { post_thinking.tags.delete(tag) }
510
555
  assert_equal(count, post_thinking.tags.size)
511
556
  assert_equal(count, post_thinking.tags(true).size)
557
+ assert_equal(count, post_thinking.taggings(true).size)
512
558
  assert_equal(tags_before.sort, post_thinking.tags.sort)
513
559
  end
514
560
 
@@ -522,7 +568,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
522
568
  post_thinking.tags << doomed << doomed2
523
569
  assert_equal(count + 2, post_thinking.tags(true).size)
524
570
 
525
- assert_nothing_raised { post_thinking.tags.delete(doomed, doomed2, quaked) }
571
+ assert_nothing_raised { post_thinking.tags.delete(doomed, doomed2, quaked) }
526
572
  assert_equal(count, post_thinking.tags.size)
527
573
  assert_equal(count, post_thinking.tags(true).size)
528
574
  assert_equal(tags_before.sort, post_thinking.tags.sort)
@@ -547,13 +593,107 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
547
593
  def test_has_many_through_has_many_with_sti
548
594
  assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments
549
595
  end
550
-
596
+
551
597
  def test_uniq_has_many_through_should_retain_order
552
598
  comment_ids = authors(:david).comments.map(&:id)
553
599
  assert_equal comment_ids.sort, authors(:david).ordered_uniq_comments.map(&:id)
554
600
  assert_equal comment_ids.sort.reverse, authors(:david).ordered_uniq_comments_desc.map(&:id)
555
601
  end
556
602
 
603
+ def test_polymorphic_has_many
604
+ expected = taggings(:welcome_general)
605
+ p = Post.find(posts(:welcome).id, :include => :taggings)
606
+ assert_no_queries {assert p.taggings.include?(expected)}
607
+ assert posts(:welcome).taggings.include?(taggings(:welcome_general))
608
+ end
609
+
610
+ def test_polymorphic_has_one
611
+ expected = posts(:welcome)
612
+
613
+ tagging = Tagging.find(taggings(:welcome_general).id, :include => :taggable)
614
+ assert_no_queries { assert_equal expected, tagging.taggable}
615
+ end
616
+
617
+ def test_polymorphic_belongs_to
618
+ p = Post.find(posts(:welcome).id, :include => {:taggings => :taggable})
619
+ assert_no_queries {assert_equal posts(:welcome), p.taggings.first.taggable}
620
+ end
621
+
622
+ def test_preload_polymorphic_has_many_through
623
+ posts = Post.find(:all, :order => 'posts.id')
624
+ posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
625
+ assert_equal posts.length, posts_with_tags.length
626
+ posts.length.times do |i|
627
+ assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
628
+ end
629
+ end
630
+
631
+ def test_preload_polymorph_many_types
632
+ taggings = Tagging.find :all, :include => :taggable, :conditions => ['taggable_type != ?', 'FakeModel']
633
+ assert_no_queries do
634
+ taggings.first.taggable.id
635
+ taggings[1].taggable.id
636
+ end
637
+
638
+ taggables = taggings.map(&:taggable)
639
+ assert taggables.include?(items(:dvd))
640
+ assert taggables.include?(posts(:welcome))
641
+ end
642
+
643
+ def test_preload_nil_polymorphic_belongs_to
644
+ assert_nothing_raised do
645
+ taggings = Tagging.find(:all, :include => :taggable, :conditions => ['taggable_type IS NULL'])
646
+ end
647
+ end
648
+
649
+ def test_preload_polymorphic_has_many
650
+ posts = Post.find(:all, :order => 'posts.id')
651
+ posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id')
652
+ assert_equal posts.length, posts_with_taggings.length
653
+ posts.length.times do |i|
654
+ assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
655
+ end
656
+ end
657
+
658
+ def test_belongs_to_shared_parent
659
+ comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 1')
660
+ assert_no_queries do
661
+ assert_equal comments.first.post, comments[1].post
662
+ end
663
+ end
664
+
665
+ def test_has_many_through_include_uses_array_include_after_loaded
666
+ david = authors(:david)
667
+ david.categories.class # force load target
668
+
669
+ category = david.categories.first
670
+
671
+ assert_no_queries do
672
+ assert david.categories.loaded?
673
+ assert david.categories.include?(category)
674
+ end
675
+ end
676
+
677
+ def test_has_many_through_include_checks_if_record_exists_if_target_not_loaded
678
+ david = authors(:david)
679
+ category = david.categories.first
680
+
681
+ david.reload
682
+ assert ! david.categories.loaded?
683
+ assert_queries(1) do
684
+ assert david.categories.include?(category)
685
+ end
686
+ assert ! david.categories.loaded?
687
+ end
688
+
689
+ def test_has_many_through_include_returns_false_for_non_matching_record_to_verify_scoping
690
+ david = authors(:david)
691
+ category = Category.create!(:name => 'Not Associated')
692
+
693
+ assert ! david.categories.loaded?
694
+ assert ! david.categories.include?(category)
695
+ end
696
+
557
697
  private
558
698
  # create dynamic Post models to allow different dependency options
559
699
  def find_post_with_dependency(post_id, association, association_name, dependency)