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
@@ -1,16 +1,16 @@
1
- require 'abstract_unit'
2
- require 'fixtures/post'
3
- require 'fixtures/comment'
4
- require 'fixtures/project'
5
- require 'fixtures/developer'
1
+ require "cases/helper"
2
+ require 'models/post'
3
+ require 'models/comment'
4
+ require 'models/project'
5
+ require 'models/developer'
6
6
 
7
- class AssociationsExtensionsTest < Test::Unit::TestCase
7
+ class AssociationsExtensionsTest < ActiveRecord::TestCase
8
8
  fixtures :projects, :developers, :developers_projects, :comments, :posts
9
9
 
10
10
  def test_extension_on_has_many
11
11
  assert_equal comments(:more_greetings), posts(:welcome).comments.find_most_recent
12
12
  end
13
-
13
+
14
14
  def test_extension_on_habtm
15
15
  assert_equal projects(:action_controller), developers(:david).projects.find_most_recent
16
16
  end
@@ -44,4 +44,4 @@ class AssociationsExtensionsTest < Test::Unit::TestCase
44
44
  david = Marshal.load(Marshal.dump(david))
45
45
  assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
46
46
  end
47
- end
47
+ end
@@ -0,0 +1,684 @@
1
+ require "cases/helper"
2
+ require 'models/developer'
3
+ require 'models/project'
4
+ require 'models/company'
5
+ require 'models/topic'
6
+ require 'models/reply'
7
+ require 'models/computer'
8
+ require 'models/customer'
9
+ require 'models/order'
10
+ require 'models/categorization'
11
+ require 'models/category'
12
+ require 'models/post'
13
+ require 'models/author'
14
+ require 'models/comment'
15
+ require 'models/tag'
16
+ require 'models/tagging'
17
+ require 'models/person'
18
+ require 'models/reader'
19
+ require 'models/parrot'
20
+ require 'models/pirate'
21
+ require 'models/treasure'
22
+ require 'models/price_estimate'
23
+ require 'models/club'
24
+ require 'models/member'
25
+ require 'models/membership'
26
+ require 'models/sponsor'
27
+
28
+ class ProjectWithAfterCreateHook < ActiveRecord::Base
29
+ set_table_name 'projects'
30
+ has_and_belongs_to_many :developers,
31
+ :class_name => "DeveloperForProjectWithAfterCreateHook",
32
+ :join_table => "developers_projects",
33
+ :foreign_key => "project_id",
34
+ :association_foreign_key => "developer_id"
35
+
36
+ after_create :add_david
37
+
38
+ def add_david
39
+ david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
40
+ david.projects << self
41
+ end
42
+ end
43
+
44
+ class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
45
+ set_table_name 'developers'
46
+ has_and_belongs_to_many :projects,
47
+ :class_name => "ProjectWithAfterCreateHook",
48
+ :join_table => "developers_projects",
49
+ :association_foreign_key => "project_id",
50
+ :foreign_key => "developer_id"
51
+ end
52
+
53
+ class ProjectWithSymbolsForKeys < ActiveRecord::Base
54
+ set_table_name 'projects'
55
+ has_and_belongs_to_many :developers,
56
+ :class_name => "DeveloperWithSymbolsForKeys",
57
+ :join_table => :developers_projects,
58
+ :foreign_key => :project_id,
59
+ :association_foreign_key => "developer_id"
60
+ end
61
+
62
+ class DeveloperWithSymbolsForKeys < ActiveRecord::Base
63
+ set_table_name 'developers'
64
+ has_and_belongs_to_many :projects,
65
+ :class_name => "ProjectWithSymbolsForKeys",
66
+ :join_table => :developers_projects,
67
+ :association_foreign_key => :project_id,
68
+ :foreign_key => "developer_id"
69
+ end
70
+
71
+ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
72
+ fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
73
+ :parrots, :pirates, :treasures, :price_estimates
74
+
75
+ def test_has_and_belongs_to_many
76
+ david = Developer.find(1)
77
+
78
+ assert !david.projects.empty?
79
+ assert_equal 2, david.projects.size
80
+
81
+ active_record = Project.find(1)
82
+ assert !active_record.developers.empty?
83
+ assert_equal 3, active_record.developers.size
84
+ assert active_record.developers.include?(david)
85
+ end
86
+
87
+ def test_triple_equality
88
+ assert !(Array === Developer.find(1).projects)
89
+ assert Developer.find(1).projects === Array
90
+ end
91
+
92
+ def test_adding_single
93
+ jamis = Developer.find(2)
94
+ jamis.projects.reload # causing the collection to load
95
+ action_controller = Project.find(2)
96
+ assert_equal 1, jamis.projects.size
97
+ assert_equal 1, action_controller.developers.size
98
+
99
+ jamis.projects << action_controller
100
+
101
+ assert_equal 2, jamis.projects.size
102
+ assert_equal 2, jamis.projects(true).size
103
+ assert_equal 2, action_controller.developers(true).size
104
+ end
105
+
106
+ def test_adding_type_mismatch
107
+ jamis = Developer.find(2)
108
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { jamis.projects << nil }
109
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { jamis.projects << 1 }
110
+ end
111
+
112
+ def test_adding_from_the_project
113
+ jamis = Developer.find(2)
114
+ action_controller = Project.find(2)
115
+ action_controller.developers.reload
116
+ assert_equal 1, jamis.projects.size
117
+ assert_equal 1, action_controller.developers.size
118
+
119
+ action_controller.developers << jamis
120
+
121
+ assert_equal 2, jamis.projects(true).size
122
+ assert_equal 2, action_controller.developers.size
123
+ assert_equal 2, action_controller.developers(true).size
124
+ end
125
+
126
+ def test_adding_from_the_project_fixed_timestamp
127
+ jamis = Developer.find(2)
128
+ action_controller = Project.find(2)
129
+ action_controller.developers.reload
130
+ assert_equal 1, jamis.projects.size
131
+ assert_equal 1, action_controller.developers.size
132
+ updated_at = jamis.updated_at
133
+
134
+ action_controller.developers << jamis
135
+
136
+ assert_equal updated_at, jamis.updated_at
137
+ assert_equal 2, jamis.projects(true).size
138
+ assert_equal 2, action_controller.developers.size
139
+ assert_equal 2, action_controller.developers(true).size
140
+ end
141
+
142
+ def test_adding_multiple
143
+ aredridel = Developer.new("name" => "Aredridel")
144
+ aredridel.save
145
+ aredridel.projects.reload
146
+ aredridel.projects.push(Project.find(1), Project.find(2))
147
+ assert_equal 2, aredridel.projects.size
148
+ assert_equal 2, aredridel.projects(true).size
149
+ end
150
+
151
+ def test_adding_a_collection
152
+ aredridel = Developer.new("name" => "Aredridel")
153
+ aredridel.save
154
+ aredridel.projects.reload
155
+ aredridel.projects.concat([Project.find(1), Project.find(2)])
156
+ assert_equal 2, aredridel.projects.size
157
+ assert_equal 2, aredridel.projects(true).size
158
+ end
159
+
160
+ def test_adding_uses_default_values_on_join_table
161
+ ac = projects(:action_controller)
162
+ assert !developers(:jamis).projects.include?(ac)
163
+ developers(:jamis).projects << ac
164
+
165
+ assert developers(:jamis, :reload).projects.include?(ac)
166
+ project = developers(:jamis).projects.detect { |p| p == ac }
167
+ assert_equal 1, project.access_level.to_i
168
+ end
169
+
170
+ def test_habtm_attribute_access_and_respond_to
171
+ project = developers(:jamis).projects[0]
172
+ assert project.has_attribute?("name")
173
+ assert project.has_attribute?("joined_on")
174
+ assert project.has_attribute?("access_level")
175
+ assert project.respond_to?("name")
176
+ assert project.respond_to?("name=")
177
+ assert project.respond_to?("name?")
178
+ assert project.respond_to?("joined_on")
179
+ # given that the 'join attribute' won't be persisted, I don't
180
+ # think we should define the mutators
181
+ #assert project.respond_to?("joined_on=")
182
+ assert project.respond_to?("joined_on?")
183
+ assert project.respond_to?("access_level")
184
+ #assert project.respond_to?("access_level=")
185
+ assert project.respond_to?("access_level?")
186
+ end
187
+
188
+ def test_habtm_adding_before_save
189
+ no_of_devels = Developer.count
190
+ no_of_projects = Project.count
191
+ aredridel = Developer.new("name" => "Aredridel")
192
+ aredridel.projects.concat([Project.find(1), p = Project.new("name" => "Projekt")])
193
+ assert aredridel.new_record?
194
+ assert p.new_record?
195
+ assert aredridel.save
196
+ assert !aredridel.new_record?
197
+ assert_equal no_of_devels+1, Developer.count
198
+ assert_equal no_of_projects+1, Project.count
199
+ assert_equal 2, aredridel.projects.size
200
+ assert_equal 2, aredridel.projects(true).size
201
+ end
202
+
203
+ def test_habtm_saving_multiple_relationships
204
+ new_project = Project.new("name" => "Grimetime")
205
+ amount_of_developers = 4
206
+ developers = (0...amount_of_developers).collect {|i| Developer.create(:name => "JME #{i}") }.reverse
207
+
208
+ new_project.developer_ids = [developers[0].id, developers[1].id]
209
+ new_project.developers_with_callback_ids = [developers[2].id, developers[3].id]
210
+ assert new_project.save
211
+
212
+ new_project.reload
213
+ assert_equal amount_of_developers, new_project.developers.size
214
+ assert_equal developers, new_project.developers
215
+ end
216
+
217
+ def test_habtm_unique_order_preserved
218
+ assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).non_unique_developers
219
+ assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).developers
220
+ end
221
+
222
+ def test_build
223
+ devel = Developer.find(1)
224
+ proj = assert_no_queries { devel.projects.build("name" => "Projekt") }
225
+ assert !devel.projects.loaded?
226
+
227
+ assert_equal devel.projects.last, proj
228
+ assert devel.projects.loaded?
229
+
230
+ assert proj.new_record?
231
+ devel.save
232
+ assert !proj.new_record?
233
+ assert_equal devel.projects.last, proj
234
+ assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
235
+ end
236
+
237
+ def test_build_by_new_record
238
+ devel = Developer.new(:name => "Marcel", :salary => 75000)
239
+ proj1 = devel.projects.build(:name => "Make bed")
240
+ proj2 = devel.projects.build(:name => "Lie in it")
241
+ assert_equal devel.projects.last, proj2
242
+ assert proj2.new_record?
243
+ devel.save
244
+ assert !devel.new_record?
245
+ assert !proj2.new_record?
246
+ assert_equal devel.projects.last, proj2
247
+ assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
248
+ end
249
+
250
+ def test_create
251
+ devel = Developer.find(1)
252
+ proj = devel.projects.create("name" => "Projekt")
253
+ assert !devel.projects.loaded?
254
+
255
+ assert_equal devel.projects.last, proj
256
+ assert devel.projects.loaded?
257
+
258
+ assert !proj.new_record?
259
+ assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
260
+ end
261
+
262
+ def test_create_by_new_record
263
+ devel = Developer.new(:name => "Marcel", :salary => 75000)
264
+ proj1 = devel.projects.build(:name => "Make bed")
265
+ proj2 = devel.projects.build(:name => "Lie in it")
266
+ assert_equal devel.projects.last, proj2
267
+ assert proj2.new_record?
268
+ devel.save
269
+ assert !devel.new_record?
270
+ assert !proj2.new_record?
271
+ assert_equal devel.projects.last, proj2
272
+ assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
273
+ end
274
+
275
+ def test_creation_respects_hash_condition
276
+ post = categories(:general).post_with_conditions.build(:body => '')
277
+
278
+ assert post.save
279
+ assert_equal 'Yet Another Testing Title', post.title
280
+
281
+ another_post = categories(:general).post_with_conditions.create(:body => '')
282
+
283
+ assert !another_post.new_record?
284
+ assert_equal 'Yet Another Testing Title', another_post.title
285
+ end
286
+
287
+ def test_uniq_after_the_fact
288
+ dev = developers(:jamis)
289
+ dev.projects << projects(:active_record)
290
+ dev.projects << projects(:active_record)
291
+
292
+ assert_equal 3, dev.projects.size
293
+ assert_equal 1, dev.projects.uniq.size
294
+ end
295
+
296
+ def test_uniq_before_the_fact
297
+ projects(:active_record).developers << developers(:jamis)
298
+ projects(:active_record).developers << developers(:david)
299
+ assert_equal 3, projects(:active_record, :reload).developers.size
300
+ end
301
+
302
+ def test_deleting
303
+ david = Developer.find(1)
304
+ active_record = Project.find(1)
305
+ david.projects.reload
306
+ assert_equal 2, david.projects.size
307
+ assert_equal 3, active_record.developers.size
308
+
309
+ david.projects.delete(active_record)
310
+
311
+ assert_equal 1, david.projects.size
312
+ assert_equal 1, david.projects(true).size
313
+ assert_equal 2, active_record.developers(true).size
314
+ end
315
+
316
+ def test_deleting_array
317
+ david = Developer.find(1)
318
+ david.projects.reload
319
+ david.projects.delete(Project.find(:all))
320
+ assert_equal 0, david.projects.size
321
+ assert_equal 0, david.projects(true).size
322
+ end
323
+
324
+ def test_deleting_with_sql
325
+ david = Developer.find(1)
326
+ active_record = Project.find(1)
327
+ active_record.developers.reload
328
+ assert_equal 3, active_record.developers_by_sql.size
329
+
330
+ active_record.developers_by_sql.delete(david)
331
+ assert_equal 2, active_record.developers_by_sql(true).size
332
+ end
333
+
334
+ def test_deleting_array_with_sql
335
+ active_record = Project.find(1)
336
+ active_record.developers.reload
337
+ assert_equal 3, active_record.developers_by_sql.size
338
+
339
+ active_record.developers_by_sql.delete(Developer.find(:all))
340
+ assert_equal 0, active_record.developers_by_sql(true).size
341
+ end
342
+
343
+ def test_deleting_all
344
+ david = Developer.find(1)
345
+ david.projects.reload
346
+ david.projects.clear
347
+ assert_equal 0, david.projects.size
348
+ assert_equal 0, david.projects(true).size
349
+ end
350
+
351
+ def test_removing_associations_on_destroy
352
+ david = DeveloperWithBeforeDestroyRaise.find(1)
353
+ assert !david.projects.empty?
354
+ assert_nothing_raised { david.destroy }
355
+ assert david.projects.empty?
356
+ assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
357
+ end
358
+
359
+ def test_additional_columns_from_join_table
360
+ assert_date_from_db Date.new(2004, 10, 10), Developer.find(1).projects.first.joined_on.to_date
361
+ end
362
+
363
+ def test_destroy_all
364
+ david = Developer.find(1)
365
+ david.projects.reload
366
+ assert !david.projects.empty?
367
+ david.projects.destroy_all
368
+ assert david.projects.empty?
369
+ assert david.projects(true).empty?
370
+ end
371
+
372
+ def test_deprecated_push_with_attributes_was_removed
373
+ jamis = developers(:jamis)
374
+ assert_raise(NoMethodError) do
375
+ jamis.projects.push_with_attributes(projects(:action_controller), :joined_on => Date.today)
376
+ end
377
+ end
378
+
379
+ def test_associations_with_conditions
380
+ assert_equal 3, projects(:active_record).developers.size
381
+ assert_equal 1, projects(:active_record).developers_named_david.size
382
+ assert_equal 1, projects(:active_record).developers_named_david_with_hash_conditions.size
383
+
384
+ assert_equal developers(:david), projects(:active_record).developers_named_david.find(developers(:david).id)
385
+ assert_equal developers(:david), projects(:active_record).developers_named_david_with_hash_conditions.find(developers(:david).id)
386
+ assert_equal developers(:david), projects(:active_record).salaried_developers.find(developers(:david).id)
387
+
388
+ projects(:active_record).developers_named_david.clear
389
+ assert_equal 2, projects(:active_record, :reload).developers.size
390
+ end
391
+
392
+ def test_find_in_association
393
+ # Using sql
394
+ assert_equal developers(:david), projects(:active_record).developers.find(developers(:david).id), "SQL find"
395
+
396
+ # Using ruby
397
+ active_record = projects(:active_record)
398
+ active_record.developers.reload
399
+ assert_equal developers(:david), active_record.developers.find(developers(:david).id), "Ruby find"
400
+ end
401
+
402
+ def test_include_uses_array_include_after_loaded
403
+ project = projects(:active_record)
404
+ project.developers.class # force load target
405
+
406
+ developer = project.developers.first
407
+
408
+ assert_no_queries do
409
+ assert project.developers.loaded?
410
+ assert project.developers.include?(developer)
411
+ end
412
+ end
413
+
414
+ def test_include_checks_if_record_exists_if_target_not_loaded
415
+ project = projects(:active_record)
416
+ developer = project.developers.first
417
+
418
+ project.reload
419
+ assert ! project.developers.loaded?
420
+ assert_queries(1) do
421
+ assert project.developers.include?(developer)
422
+ end
423
+ assert ! project.developers.loaded?
424
+ end
425
+
426
+ def test_include_returns_false_for_non_matching_record_to_verify_scoping
427
+ project = projects(:active_record)
428
+ developer = Developer.create :name => "Bryan", :salary => 50_000
429
+
430
+ assert ! project.developers.loaded?
431
+ assert ! project.developers.include?(developer)
432
+ end
433
+
434
+ def test_find_in_association_with_custom_finder_sql
435
+ assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id), "SQL find"
436
+
437
+ active_record = projects(:active_record)
438
+ active_record.developers_with_finder_sql.reload
439
+ assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
440
+ end
441
+
442
+ def test_find_in_association_with_custom_finder_sql_and_string_id
443
+ assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
444
+ end
445
+
446
+ def test_find_with_merged_options
447
+ assert_equal 1, projects(:active_record).limited_developers.size
448
+ assert_equal 1, projects(:active_record).limited_developers.find(:all).size
449
+ assert_equal 3, projects(:active_record).limited_developers.find(:all, :limit => nil).size
450
+ end
451
+
452
+ def test_dynamic_find_should_respect_association_order
453
+ # Developers are ordered 'name DESC, id DESC'
454
+ low_id_jamis = developers(:jamis)
455
+ middle_id_jamis = developers(:poor_jamis)
456
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
457
+
458
+ assert_equal high_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'")
459
+ assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis')
460
+ end
461
+
462
+ def test_dynamic_find_order_should_override_association_order
463
+ # Developers are ordered 'name DESC, id DESC'
464
+ low_id_jamis = developers(:jamis)
465
+ middle_id_jamis = developers(:poor_jamis)
466
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
467
+
468
+ assert_equal low_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'", :order => 'id')
469
+ assert_equal low_id_jamis, projects(:active_record).developers.find_by_name('Jamis', :order => 'id')
470
+ end
471
+
472
+ def test_dynamic_find_all_should_respect_association_order
473
+ # Developers are ordered 'name DESC, id DESC'
474
+ low_id_jamis = developers(:jamis)
475
+ middle_id_jamis = developers(:poor_jamis)
476
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
477
+
478
+ assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'")
479
+ assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis')
480
+ end
481
+
482
+ def test_dynamic_find_all_order_should_override_association_order
483
+ # Developers are ordered 'name DESC, id DESC'
484
+ low_id_jamis = developers(:jamis)
485
+ middle_id_jamis = developers(:poor_jamis)
486
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
487
+
488
+ assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'", :order => 'id')
489
+ assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis', :order => 'id')
490
+ end
491
+
492
+ def test_dynamic_find_all_should_respect_association_limit
493
+ assert_equal 1, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'").length
494
+ assert_equal 1, projects(:active_record).limited_developers.find_all_by_name('Jamis').length
495
+ end
496
+
497
+ def test_dynamic_find_all_order_should_override_association_limit
498
+ assert_equal 2, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'", :limit => 9_000).length
499
+ assert_equal 2, projects(:active_record).limited_developers.find_all_by_name('Jamis', :limit => 9_000).length
500
+ end
501
+
502
+ def test_dynamic_find_all_should_respect_readonly_access
503
+ projects(:active_record).readonly_developers.each { |d| assert_raise(ActiveRecord::ReadOnlyRecord) { d.save! } if d.valid?}
504
+ projects(:active_record).readonly_developers.each { |d| d.readonly? }
505
+ end
506
+
507
+ def test_new_with_values_in_collection
508
+ jamis = DeveloperForProjectWithAfterCreateHook.find_by_name('Jamis')
509
+ david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
510
+ project = ProjectWithAfterCreateHook.new(:name => "Cooking with Bertie")
511
+ project.developers << jamis
512
+ project.save!
513
+ project.reload
514
+
515
+ assert project.developers.include?(jamis)
516
+ assert project.developers.include?(david)
517
+ end
518
+
519
+ def test_find_in_association_with_options
520
+ developers = projects(:active_record).developers.find(:all)
521
+ assert_equal 3, developers.size
522
+
523
+ assert_equal developers(:poor_jamis), projects(:active_record).developers.find(:first, :conditions => "salary < 10000")
524
+ assert_equal developers(:jamis), projects(:active_record).developers.find(:first, :order => "salary DESC")
525
+ end
526
+
527
+ def test_replace_with_less
528
+ david = developers(:david)
529
+ david.projects = [projects(:action_controller)]
530
+ assert david.save
531
+ assert_equal 1, david.projects.length
532
+ end
533
+
534
+ def test_replace_with_new
535
+ david = developers(:david)
536
+ david.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
537
+ david.save
538
+ assert_equal 2, david.projects.length
539
+ assert !david.projects.include?(projects(:active_record))
540
+ end
541
+
542
+ def test_replace_on_new_object
543
+ new_developer = Developer.new("name" => "Matz")
544
+ new_developer.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
545
+ new_developer.save
546
+ assert_equal 2, new_developer.projects.length
547
+ end
548
+
549
+ def test_consider_type
550
+ developer = Developer.find(:first)
551
+ special_project = SpecialProject.create("name" => "Special Project")
552
+
553
+ other_project = developer.projects.first
554
+ developer.special_projects << special_project
555
+ developer.reload
556
+
557
+ assert developer.projects.include?(special_project)
558
+ assert developer.special_projects.include?(special_project)
559
+ assert !developer.special_projects.include?(other_project)
560
+ end
561
+
562
+ def test_update_attributes_after_push_without_duplicate_join_table_rows
563
+ developer = Developer.new("name" => "Kano")
564
+ project = SpecialProject.create("name" => "Special Project")
565
+ assert developer.save
566
+ developer.projects << project
567
+ developer.update_attribute("name", "Bruza")
568
+ assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i
569
+ SELECT count(*) FROM developers_projects
570
+ WHERE project_id = #{project.id}
571
+ AND developer_id = #{developer.id}
572
+ end_sql
573
+ end
574
+
575
+ def test_updating_attributes_on_non_rich_associations
576
+ welcome = categories(:technology).posts.first
577
+ welcome.title = "Something else"
578
+ assert welcome.save!
579
+ end
580
+
581
+ def test_habtm_respects_select
582
+ categories(:technology).select_testing_posts(true).each do |o|
583
+ assert_respond_to o, :correctness_marker
584
+ end
585
+ assert_respond_to categories(:technology).select_testing_posts.find(:first), :correctness_marker
586
+ end
587
+
588
+ def test_updating_attributes_on_rich_associations
589
+ david = projects(:action_controller).developers.first
590
+ david.name = "DHH"
591
+ assert_raises(ActiveRecord::ReadOnlyRecord) { david.save! }
592
+ end
593
+
594
+ def test_updating_attributes_on_rich_associations_with_limited_find_from_reflection
595
+ david = projects(:action_controller).selected_developers.first
596
+ david.name = "DHH"
597
+ assert_nothing_raised { david.save! }
598
+ end
599
+
600
+
601
+ def test_updating_attributes_on_rich_associations_with_limited_find
602
+ david = projects(:action_controller).developers.find(:all, :select => "developers.*").first
603
+ david.name = "DHH"
604
+ assert david.save!
605
+ end
606
+
607
+ def test_join_table_alias
608
+ assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL').size
609
+ end
610
+
611
+ def test_join_with_group
612
+ group = Developer.columns.inject([]) do |g, c|
613
+ g << "developers.#{c.name}"
614
+ g << "developers_projects_2.#{c.name}"
615
+ end
616
+ Project.columns.each { |c| group << "projects.#{c.name}" }
617
+
618
+ assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL', :group => group.join(",")).size
619
+ end
620
+
621
+ def test_get_ids
622
+ assert_equal projects(:active_record, :action_controller).map(&:id).sort, developers(:david).project_ids.sort
623
+ assert_equal [projects(:active_record).id], developers(:jamis).project_ids
624
+ end
625
+
626
+ def test_assign_ids
627
+ developer = Developer.new("name" => "Joe")
628
+ developer.project_ids = projects(:active_record, :action_controller).map(&:id)
629
+ developer.save
630
+ developer.reload
631
+ assert_equal 2, developer.projects.length
632
+ assert_equal projects(:active_record), developer.projects[0]
633
+ assert_equal projects(:action_controller), developer.projects[1]
634
+ end
635
+
636
+ def test_assign_ids_ignoring_blanks
637
+ developer = Developer.new("name" => "Joe")
638
+ developer.project_ids = [projects(:active_record).id, nil, projects(:action_controller).id, '']
639
+ developer.save
640
+ developer.reload
641
+ assert_equal 2, developer.projects.length
642
+ assert_equal projects(:active_record), developer.projects[0]
643
+ assert_equal projects(:action_controller), developer.projects[1]
644
+ end
645
+
646
+ def test_select_limited_ids_list
647
+ # Set timestamps
648
+ Developer.transaction do
649
+ Developer.find(:all, :order => 'id').each_with_index do |record, i|
650
+ record.update_attributes(:created_at => 5.years.ago + (i * 5.minutes))
651
+ end
652
+ end
653
+
654
+ join_base = ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase.new(Project)
655
+ join_dep = ActiveRecord::Associations::ClassMethods::JoinDependency.new(join_base, :developers, nil)
656
+ projects = Project.send(:select_limited_ids_list, {:order => 'developers.created_at'}, join_dep)
657
+ assert !projects.include?("'"), projects
658
+ assert_equal %w(1 2), projects.scan(/\d/).sort
659
+ end
660
+
661
+ def test_scoped_find_on_through_association_doesnt_return_read_only_records
662
+ tag = Post.find(1).tags.find_by_name("General")
663
+
664
+ assert_nothing_raised do
665
+ tag.save!
666
+ end
667
+ end
668
+
669
+ def test_has_many_through_polymorphic_has_manys_works
670
+ assert_equal [10, 20].to_set, pirates(:redbeard).treasure_estimates.map(&:price).to_set
671
+ end
672
+
673
+ def test_symbols_as_keys
674
+ developer = DeveloperWithSymbolsForKeys.new(:name => 'David')
675
+ project = ProjectWithSymbolsForKeys.new(:name => 'Rails Testing')
676
+ project.developers << developer
677
+ project.save!
678
+
679
+ assert_equal 1, project.developers.size
680
+ assert_equal 1, developer.projects.size
681
+ assert_equal developer, project.developers.find(:first)
682
+ assert_equal project, developer.projects.find(:first)
683
+ end
684
+ end