activerecord 1.0.0 → 2.0.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 (311) hide show
  1. data/CHANGELOG +4928 -3
  2. data/README +45 -46
  3. data/RUNNING_UNIT_TESTS +8 -11
  4. data/Rakefile +247 -0
  5. data/install.rb +8 -38
  6. data/lib/active_record/aggregations.rb +64 -49
  7. data/lib/active_record/associations/association_collection.rb +217 -47
  8. data/lib/active_record/associations/association_proxy.rb +159 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +155 -37
  12. data/lib/active_record/associations/has_many_association.rb +145 -75
  13. data/lib/active_record/associations/has_many_through_association.rb +283 -0
  14. data/lib/active_record/associations/has_one_association.rb +96 -0
  15. data/lib/active_record/associations.rb +1537 -304
  16. data/lib/active_record/attribute_methods.rb +328 -0
  17. data/lib/active_record/base.rb +2001 -588
  18. data/lib/active_record/calculations.rb +269 -0
  19. data/lib/active_record/callbacks.rb +169 -165
  20. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +308 -0
  21. data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -0
  22. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  23. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +472 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +306 -0
  26. data/lib/active_record/connection_adapters/abstract_adapter.rb +125 -279
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +442 -77
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +805 -135
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  30. data/lib/active_record/connection_adapters/sqlite_adapter.rb +353 -69
  31. data/lib/active_record/fixtures.rb +946 -100
  32. data/lib/active_record/locking/optimistic.rb +144 -0
  33. data/lib/active_record/locking/pessimistic.rb +77 -0
  34. data/lib/active_record/migration.rb +417 -0
  35. data/lib/active_record/observer.rb +142 -32
  36. data/lib/active_record/query_cache.rb +23 -0
  37. data/lib/active_record/reflection.rb +163 -70
  38. data/lib/active_record/schema.rb +58 -0
  39. data/lib/active_record/schema_dumper.rb +171 -0
  40. data/lib/active_record/serialization.rb +98 -0
  41. data/lib/active_record/serializers/json_serializer.rb +71 -0
  42. data/lib/active_record/serializers/xml_serializer.rb +315 -0
  43. data/lib/active_record/timestamp.rb +41 -0
  44. data/lib/active_record/transactions.rb +87 -57
  45. data/lib/active_record/validations.rb +909 -122
  46. data/lib/active_record/vendor/db2.rb +362 -0
  47. data/lib/active_record/vendor/mysql.rb +126 -29
  48. data/lib/active_record/version.rb +9 -0
  49. data/lib/active_record.rb +35 -7
  50. data/lib/activerecord.rb +1 -0
  51. data/test/aaa_create_tables_test.rb +72 -0
  52. data/test/abstract_unit.rb +73 -5
  53. data/test/active_schema_test_mysql.rb +43 -0
  54. data/test/adapter_test.rb +105 -0
  55. data/test/adapter_test_sqlserver.rb +95 -0
  56. data/test/aggregations_test.rb +110 -16
  57. data/test/all.sh +2 -2
  58. data/test/ar_schema_test.rb +33 -0
  59. data/test/association_inheritance_reload.rb +14 -0
  60. data/test/associations/ar_joins_test.rb +0 -0
  61. data/test/associations/callbacks_test.rb +147 -0
  62. data/test/associations/cascaded_eager_loading_test.rb +110 -0
  63. data/test/associations/eager_singularization_test.rb +145 -0
  64. data/test/associations/eager_test.rb +442 -0
  65. data/test/associations/extension_test.rb +47 -0
  66. data/test/associations/inner_join_association_test.rb +88 -0
  67. data/test/associations/join_model_test.rb +553 -0
  68. data/test/associations_test.rb +1930 -267
  69. data/test/attribute_methods_test.rb +146 -0
  70. data/test/base_test.rb +1316 -84
  71. data/test/binary_test.rb +32 -0
  72. data/test/calculations_test.rb +251 -0
  73. data/test/callbacks_test.rb +400 -0
  74. data/test/class_inheritable_attributes_test.rb +3 -4
  75. data/test/column_alias_test.rb +17 -0
  76. data/test/connection_test_firebird.rb +8 -0
  77. data/test/connection_test_mysql.rb +30 -0
  78. data/test/connections/native_db2/connection.rb +25 -0
  79. data/test/connections/native_firebird/connection.rb +26 -0
  80. data/test/connections/native_frontbase/connection.rb +27 -0
  81. data/test/connections/native_mysql/connection.rb +21 -18
  82. data/test/connections/native_openbase/connection.rb +21 -0
  83. data/test/connections/native_oracle/connection.rb +27 -0
  84. data/test/connections/native_postgresql/connection.rb +17 -18
  85. data/test/connections/native_sqlite/connection.rb +17 -16
  86. data/test/connections/native_sqlite3/connection.rb +25 -0
  87. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  88. data/test/connections/native_sybase/connection.rb +23 -0
  89. data/test/copy_table_test_sqlite.rb +69 -0
  90. data/test/datatype_test_postgresql.rb +203 -0
  91. data/test/date_time_test.rb +37 -0
  92. data/test/default_test_firebird.rb +16 -0
  93. data/test/defaults_test.rb +67 -0
  94. data/test/deprecated_finder_test.rb +30 -0
  95. data/test/finder_test.rb +607 -32
  96. data/test/fixtures/accounts.yml +28 -0
  97. data/test/fixtures/all/developers.yml +0 -0
  98. data/test/fixtures/all/people.csv +0 -0
  99. data/test/fixtures/all/tasks.yml +0 -0
  100. data/test/fixtures/author.rb +107 -0
  101. data/test/fixtures/author_favorites.yml +4 -0
  102. data/test/fixtures/authors.yml +7 -0
  103. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  104. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  105. data/test/fixtures/bad_fixtures/blank_line +3 -0
  106. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  107. data/test/fixtures/bad_fixtures/missing_value +1 -0
  108. data/test/fixtures/binaries.yml +132 -0
  109. data/test/fixtures/binary.rb +2 -0
  110. data/test/fixtures/book.rb +4 -0
  111. data/test/fixtures/books.yml +7 -0
  112. data/test/fixtures/categories/special_categories.yml +9 -0
  113. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  114. data/test/fixtures/categories.yml +14 -0
  115. data/test/fixtures/categories_ordered.yml +7 -0
  116. data/test/fixtures/categories_posts.yml +23 -0
  117. data/test/fixtures/categorization.rb +5 -0
  118. data/test/fixtures/categorizations.yml +17 -0
  119. data/test/fixtures/category.rb +26 -0
  120. data/test/fixtures/citation.rb +6 -0
  121. data/test/fixtures/comment.rb +23 -0
  122. data/test/fixtures/comments.yml +59 -0
  123. data/test/fixtures/companies.yml +55 -0
  124. data/test/fixtures/company.rb +81 -4
  125. data/test/fixtures/company_in_module.rb +32 -6
  126. data/test/fixtures/computer.rb +4 -0
  127. data/test/fixtures/computers.yml +4 -0
  128. data/test/fixtures/contact.rb +16 -0
  129. data/test/fixtures/courses.yml +7 -0
  130. data/test/fixtures/customer.rb +28 -3
  131. data/test/fixtures/customers.yml +17 -0
  132. data/test/fixtures/db_definitions/db2.drop.sql +33 -0
  133. data/test/fixtures/db_definitions/db2.sql +235 -0
  134. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  135. data/test/fixtures/db_definitions/db22.sql +5 -0
  136. data/test/fixtures/db_definitions/firebird.drop.sql +65 -0
  137. data/test/fixtures/db_definitions/firebird.sql +310 -0
  138. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  139. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  140. data/test/fixtures/db_definitions/frontbase.drop.sql +33 -0
  141. data/test/fixtures/db_definitions/frontbase.sql +273 -0
  142. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  143. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  144. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  145. data/test/fixtures/db_definitions/openbase.sql +318 -0
  146. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  148. data/test/fixtures/db_definitions/oracle.drop.sql +67 -0
  149. data/test/fixtures/db_definitions/oracle.sql +330 -0
  150. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  151. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  152. data/test/fixtures/db_definitions/postgresql.drop.sql +44 -0
  153. data/test/fixtures/db_definitions/postgresql.sql +217 -38
  154. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/postgresql2.sql +2 -2
  156. data/test/fixtures/db_definitions/schema.rb +354 -0
  157. data/test/fixtures/db_definitions/schema2.rb +11 -0
  158. data/test/fixtures/db_definitions/sqlite.drop.sql +33 -0
  159. data/test/fixtures/db_definitions/sqlite.sql +139 -5
  160. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  161. data/test/fixtures/db_definitions/sqlite2.sql +1 -0
  162. data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
  163. data/test/fixtures/db_definitions/sybase.sql +222 -0
  164. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  165. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  166. data/test/fixtures/developer.rb +70 -6
  167. data/test/fixtures/developers.yml +21 -0
  168. data/test/fixtures/developers_projects/david_action_controller +2 -1
  169. data/test/fixtures/developers_projects/david_active_record +2 -1
  170. data/test/fixtures/developers_projects.yml +17 -0
  171. data/test/fixtures/edge.rb +5 -0
  172. data/test/fixtures/edges.yml +6 -0
  173. data/test/fixtures/entrants.yml +14 -0
  174. data/test/fixtures/example.log +1 -0
  175. data/test/fixtures/fk_test_has_fk.yml +3 -0
  176. data/test/fixtures/fk_test_has_pk.yml +2 -0
  177. data/test/fixtures/flowers.jpg +0 -0
  178. data/test/fixtures/funny_jokes.yml +10 -0
  179. data/test/fixtures/item.rb +7 -0
  180. data/test/fixtures/items.yml +4 -0
  181. data/test/fixtures/joke.rb +3 -0
  182. data/test/fixtures/keyboard.rb +3 -0
  183. data/test/fixtures/legacy_thing.rb +3 -0
  184. data/test/fixtures/legacy_things.yml +3 -0
  185. data/test/fixtures/matey.rb +4 -0
  186. data/test/fixtures/mateys.yml +4 -0
  187. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  188. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  189. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  190. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  191. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  192. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  193. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  194. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  195. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  196. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  197. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  198. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  199. data/test/fixtures/minimalistic.rb +2 -0
  200. data/test/fixtures/minimalistics.yml +2 -0
  201. data/test/fixtures/mixed_case_monkey.rb +3 -0
  202. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  203. data/test/fixtures/mixins.yml +29 -0
  204. data/test/fixtures/movies.yml +7 -0
  205. data/test/fixtures/naked/csv/accounts.csv +1 -0
  206. data/test/fixtures/naked/yml/accounts.yml +1 -0
  207. data/test/fixtures/naked/yml/companies.yml +1 -0
  208. data/test/fixtures/naked/yml/courses.yml +1 -0
  209. data/test/fixtures/order.rb +4 -0
  210. data/test/fixtures/parrot.rb +13 -0
  211. data/test/fixtures/parrots.yml +27 -0
  212. data/test/fixtures/parrots_pirates.yml +7 -0
  213. data/test/fixtures/people.yml +3 -0
  214. data/test/fixtures/person.rb +4 -0
  215. data/test/fixtures/pirate.rb +5 -0
  216. data/test/fixtures/pirates.yml +9 -0
  217. data/test/fixtures/post.rb +59 -0
  218. data/test/fixtures/posts.yml +48 -0
  219. data/test/fixtures/project.rb +27 -2
  220. data/test/fixtures/projects.yml +7 -0
  221. data/test/fixtures/reader.rb +4 -0
  222. data/test/fixtures/readers.yml +4 -0
  223. data/test/fixtures/reply.rb +18 -2
  224. data/test/fixtures/reserved_words/distinct.yml +5 -0
  225. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  226. data/test/fixtures/reserved_words/group.yml +14 -0
  227. data/test/fixtures/reserved_words/select.yml +8 -0
  228. data/test/fixtures/reserved_words/values.yml +7 -0
  229. data/test/fixtures/ship.rb +3 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/subject.rb +4 -0
  232. data/test/fixtures/subscriber.rb +4 -3
  233. data/test/fixtures/tag.rb +7 -0
  234. data/test/fixtures/tagging.rb +10 -0
  235. data/test/fixtures/taggings.yml +25 -0
  236. data/test/fixtures/tags.yml +7 -0
  237. data/test/fixtures/task.rb +3 -0
  238. data/test/fixtures/tasks.yml +7 -0
  239. data/test/fixtures/topic.rb +20 -3
  240. data/test/fixtures/topics.yml +22 -0
  241. data/test/fixtures/treasure.rb +4 -0
  242. data/test/fixtures/treasures.yml +10 -0
  243. data/test/fixtures/vertex.rb +9 -0
  244. data/test/fixtures/vertices.yml +4 -0
  245. data/test/fixtures_test.rb +574 -8
  246. data/test/inheritance_test.rb +113 -27
  247. data/test/json_serialization_test.rb +180 -0
  248. data/test/lifecycle_test.rb +56 -29
  249. data/test/locking_test.rb +273 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +933 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_test.rb +95 -0
  254. data/test/modules_test.rb +23 -10
  255. data/test/multiple_db_test.rb +17 -3
  256. data/test/pk_test.rb +59 -15
  257. data/test/query_cache_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +124 -27
  260. data/test/reserved_word_test_mysql.rb +177 -0
  261. data/test/schema_authorization_test_postgresql.rb +75 -0
  262. data/test/schema_dumper_test.rb +131 -0
  263. data/test/schema_test_postgresql.rb +64 -0
  264. data/test/serialization_test.rb +47 -0
  265. data/test/synonym_test_oracle.rb +17 -0
  266. data/test/table_name_test_sqlserver.rb +23 -0
  267. data/test/threaded_connections_test.rb +48 -0
  268. data/test/transactions_test.rb +227 -29
  269. data/test/unconnected_test.rb +14 -6
  270. data/test/validations_test.rb +1293 -32
  271. data/test/xml_serialization_test.rb +202 -0
  272. metadata +347 -143
  273. data/dev-utils/eval_debugger.rb +0 -9
  274. data/examples/associations.rb +0 -87
  275. data/examples/shared_setup.rb +0 -15
  276. data/examples/validation.rb +0 -88
  277. data/lib/active_record/deprecated_associations.rb +0 -70
  278. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  279. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  280. data/lib/active_record/support/clean_logger.rb +0 -10
  281. data/lib/active_record/support/inflector.rb +0 -70
  282. data/lib/active_record/vendor/simple.rb +0 -702
  283. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  284. data/lib/active_record/wrappings.rb +0 -59
  285. data/rakefile +0 -122
  286. data/test/deprecated_associations_test.rb +0 -336
  287. data/test/fixtures/accounts/signals37 +0 -3
  288. data/test/fixtures/accounts/unknown +0 -2
  289. data/test/fixtures/companies/first_client +0 -6
  290. data/test/fixtures/companies/first_firm +0 -4
  291. data/test/fixtures/companies/second_client +0 -6
  292. data/test/fixtures/courses/java +0 -2
  293. data/test/fixtures/courses/ruby +0 -2
  294. data/test/fixtures/customers/david +0 -6
  295. data/test/fixtures/db_definitions/mysql.sql +0 -96
  296. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  297. data/test/fixtures/developers/david +0 -2
  298. data/test/fixtures/developers/jamis +0 -2
  299. data/test/fixtures/entrants/first +0 -3
  300. data/test/fixtures/entrants/second +0 -3
  301. data/test/fixtures/entrants/third +0 -3
  302. data/test/fixtures/fixture_database.sqlite +0 -0
  303. data/test/fixtures/fixture_database_2.sqlite +0 -0
  304. data/test/fixtures/movies/first +0 -2
  305. data/test/fixtures/movies/second +0 -2
  306. data/test/fixtures/projects/action_controller +0 -2
  307. data/test/fixtures/projects/active_record +0 -2
  308. data/test/fixtures/topics/first +0 -9
  309. data/test/fixtures/topics/second +0 -8
  310. data/test/inflector_test.rb +0 -104
  311. data/test/thread_safety_test.rb +0 -33
@@ -1,40 +1,51 @@
1
1
  require 'abstract_unit'
2
2
  require 'fixtures/developer'
3
3
  require 'fixtures/project'
4
- # require File.dirname(__FILE__) + '/../dev-utils/eval_debugger'
5
4
  require 'fixtures/company'
6
5
  require 'fixtures/topic'
7
6
  require 'fixtures/reply'
8
-
9
- # Can't declare new classes in test case methods, so tests before that
10
- bad_collection_keys = false
11
- begin
12
- class Car < ActiveRecord::Base; has_many :wheels, :name => "wheels"; end
13
- rescue ActiveRecord::ActiveRecordError
14
- bad_collection_keys = true
15
- end
16
- raise "ActiveRecord should have barked on bad collection keys" unless bad_collection_keys
17
-
7
+ require 'fixtures/computer'
8
+ require 'fixtures/customer'
9
+ require 'fixtures/order'
10
+ require 'fixtures/categorization'
11
+ require 'fixtures/category'
12
+ require 'fixtures/post'
13
+ require 'fixtures/author'
14
+ require 'fixtures/comment'
15
+ require 'fixtures/tag'
16
+ require 'fixtures/tagging'
17
+ require 'fixtures/person'
18
+ require 'fixtures/reader'
18
19
 
19
20
  class AssociationsTest < Test::Unit::TestCase
20
- def setup
21
- create_fixtures "accounts", "companies", "accounts", "developers", "projects", "developers_projects"
22
- @signals37 = Firm.find(1)
21
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects,
22
+ :computers
23
+
24
+ def test_bad_collection_keys
25
+ assert_raise(ArgumentError, 'ActiveRecord should have barked on bad collection keys') do
26
+ Class.new(ActiveRecord::Base).has_many(:wheels, :name => 'wheels')
27
+ end
23
28
  end
24
29
 
30
+ def test_should_construct_new_finder_sql_after_create
31
+ person = Person.new
32
+ assert_equal [], person.readers.find(:all)
33
+ person.save!
34
+ reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
35
+ assert_equal [reader], person.readers.find(:all)
36
+ end
37
+
25
38
  def test_force_reload
26
- firm = Firm.new
39
+ firm = Firm.new("name" => "A New Firm, Inc")
27
40
  firm.save
28
41
  firm.clients.each {|c|} # forcing to load all clients
29
42
  assert firm.clients.empty?, "New firm shouldn't have client objects"
30
- assert !firm.has_clients?, "New firm shouldn't have clients"
31
43
  assert_equal 0, firm.clients.size, "New firm should have 0 clients"
32
44
 
33
- client = Client.new("firm_id" => firm.id)
45
+ client = Client.new("name" => "TheClient.com", "firm_id" => firm.id)
34
46
  client.save
35
47
 
36
48
  assert firm.clients.empty?, "New firm should have cached no client objects"
37
- assert !firm.has_clients?, "New firm should have cached a no-clients response"
38
49
  assert_equal 0, firm.clients.size, "New firm should have cached 0 clients count"
39
50
 
40
51
  assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
@@ -42,7 +53,8 @@ class AssociationsTest < Test::Unit::TestCase
42
53
  end
43
54
 
44
55
  def test_storing_in_pstore
45
- store_filename = "/tmp/ar-pstore-association-test"
56
+ require "tmpdir"
57
+ store_filename = File.join(Dir.tmpdir, "ar-pstore-association-test")
46
58
  File.delete(store_filename) if File.exists?(store_filename)
47
59
  require "pstore"
48
60
  apple = Firm.create("name" => "Apple")
@@ -61,26 +73,120 @@ class AssociationsTest < Test::Unit::TestCase
61
73
  end
62
74
  end
63
75
 
76
+ class AssociationProxyTest < Test::Unit::TestCase
77
+ fixtures :authors, :posts, :categorizations, :categories, :developers, :projects, :developers_projects
78
+
79
+ def test_proxy_accessors
80
+ welcome = posts(:welcome)
81
+ assert_equal welcome, welcome.author.proxy_owner
82
+ assert_equal welcome.class.reflect_on_association(:author), welcome.author.proxy_reflection
83
+ welcome.author.class # force load target
84
+ assert_equal welcome.author, welcome.author.proxy_target
85
+
86
+ david = authors(:david)
87
+ assert_equal david, david.posts.proxy_owner
88
+ assert_equal david.class.reflect_on_association(:posts), david.posts.proxy_reflection
89
+ david.posts.first # force load target
90
+ assert_equal david.posts, david.posts.proxy_target
91
+
92
+ assert_equal david, david.posts_with_extension.testing_proxy_owner
93
+ assert_equal david.class.reflect_on_association(:posts_with_extension), david.posts_with_extension.testing_proxy_reflection
94
+ david.posts_with_extension.first # force load target
95
+ assert_equal david.posts_with_extension, david.posts_with_extension.testing_proxy_target
96
+ end
97
+
98
+ def test_push_does_not_load_target
99
+ david = authors(:david)
100
+
101
+ david.categories << categories(:technology)
102
+ assert !david.categories.loaded?
103
+ assert david.categories.include?(categories(:technology))
104
+ end
105
+
106
+ def test_push_does_not_lose_additions_to_new_record
107
+ josh = Author.new(:name => "Josh")
108
+ josh.posts << Post.new(:title => "New on Edge", :body => "More cool stuff!")
109
+ assert josh.posts.loaded?
110
+ assert_equal 1, josh.posts.size
111
+ end
112
+
113
+ def test_save_on_parent_does_not_load_target
114
+ david = developers(:david)
115
+
116
+ assert !david.projects.loaded?
117
+ david.update_attribute(:created_at, Time.now)
118
+ assert !david.projects.loaded?
119
+ end
120
+
121
+ def test_save_on_parent_saves_children
122
+ developer = Developer.create :name => "Bryan", :salary => 50_000
123
+ assert_equal 1, developer.reload.audit_logs.size
124
+ end
125
+
126
+ def test_failed_reload_returns_nil
127
+ p = setup_dangling_association
128
+ assert_nil p.author.reload
129
+ end
130
+
131
+ def test_failed_reset_returns_nil
132
+ p = setup_dangling_association
133
+ assert_nil p.author.reset
134
+ end
135
+
136
+ def setup_dangling_association
137
+ josh = Author.create(:name => "Josh")
138
+ p = Post.create(:title => "New on Edge", :body => "More cool stuff!", :author => josh)
139
+ josh.destroy
140
+ p
141
+ end
142
+ end
143
+
64
144
  class HasOneAssociationsTest < Test::Unit::TestCase
145
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects
146
+
65
147
  def setup
66
- create_fixtures "accounts", "companies", "accounts", "developers", "projects", "developers_projects"
67
- @signals37 = Firm.find(1)
148
+ Account.destroyed_account_ids.clear
68
149
  end
69
-
150
+
70
151
  def test_has_one
71
- assert_equal @signals37.account, Account.find(1)
72
- assert_equal Account.find(1).credit_limit, @signals37.account.credit_limit
73
- assert @signals37.has_account?, "37signals should have an account"
74
- assert Account.find(1).firm?(@signals37), "37signals account should be able to backtrack"
75
- assert Account.find(1).has_firm?, "37signals account should be able to backtrack"
152
+ assert_equal companies(:first_firm).account, Account.find(1)
153
+ assert_equal Account.find(1).credit_limit, companies(:first_firm).account.credit_limit
154
+ end
155
+
156
+ def test_has_one_cache_nils
157
+ firm = companies(:another_firm)
158
+ assert_queries(1) { assert_nil firm.account }
159
+ assert_queries(0) { assert_nil firm.account }
160
+
161
+ firms = Firm.find(:all, :include => :account)
162
+ assert_queries(0) { firms.each(&:account) }
163
+ end
164
+
165
+ def test_can_marshal_has_one_association_with_nil_target
166
+ firm = Firm.new
167
+ assert_nothing_raised do
168
+ assert_equal firm.attributes, Marshal.load(Marshal.dump(firm)).attributes
169
+ end
170
+
171
+ firm.account
172
+ assert_nothing_raised do
173
+ assert_equal firm.attributes, Marshal.load(Marshal.dump(firm)).attributes
174
+ end
175
+ end
76
176
 
77
- assert !Account.find(2).has_firm?, "Unknown isn't linked"
78
- assert !Account.find(2).firm?(@signals37), "Unknown isn't linked"
177
+ def test_proxy_assignment
178
+ company = companies(:first_firm)
179
+ assert_nothing_raised { company.account = company.account }
180
+ end
181
+
182
+ def test_triple_equality
183
+ assert Account === companies(:first_firm).account
184
+ assert companies(:first_firm).account === Account
79
185
  end
80
186
 
81
187
  def test_type_mismatch
82
- assert_raises(ActiveRecord::AssociationTypeMismatch) { @signals37.account = 1 }
83
- assert_raises(ActiveRecord::AssociationTypeMismatch) { @signals37.account = Project.find(1) }
188
+ assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = 1 }
189
+ assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = Project.find(1) }
84
190
  end
85
191
 
86
192
  def test_natural_assignment
@@ -89,19 +195,81 @@ class HasOneAssociationsTest < Test::Unit::TestCase
89
195
  apple.account = citibank
90
196
  assert_equal apple.id, citibank.firm_id
91
197
  end
92
-
198
+
93
199
  def test_natural_assignment_to_nil
94
- old_account_id = @signals37.account.id
95
- @signals37.account = nil
96
- @signals37.save
97
- assert_nil @signals37.account
98
- assert_nil Account.find(old_account_id).firm_id
200
+ old_account_id = companies(:first_firm).account.id
201
+ companies(:first_firm).account = nil
202
+ companies(:first_firm).save
203
+ assert_nil companies(:first_firm).account
204
+ # account is dependent, therefore is destroyed when reference to owner is lost
205
+ assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
99
206
  end
100
207
 
101
- def test_build
208
+ def test_assignment_without_replacement
209
+ apple = Firm.create("name" => "Apple")
210
+ citibank = Account.create("credit_limit" => 10)
211
+ apple.account = citibank
212
+ assert_equal apple.id, citibank.firm_id
213
+
214
+ hsbc = apple.build_account({ :credit_limit => 20}, false)
215
+ assert_equal apple.id, hsbc.firm_id
216
+ hsbc.save
217
+ assert_equal apple.id, citibank.firm_id
218
+
219
+ nykredit = apple.create_account({ :credit_limit => 30}, false)
220
+ assert_equal apple.id, nykredit.firm_id
221
+ assert_equal apple.id, citibank.firm_id
222
+ assert_equal apple.id, hsbc.firm_id
223
+ end
224
+
225
+ def test_assignment_without_replacement_on_create
226
+ apple = Firm.create("name" => "Apple")
227
+ citibank = Account.create("credit_limit" => 10)
228
+ apple.account = citibank
229
+ assert_equal apple.id, citibank.firm_id
230
+
231
+ hsbc = apple.create_account({:credit_limit => 10}, false)
232
+ assert_equal apple.id, hsbc.firm_id
233
+ hsbc.save
234
+ assert_equal apple.id, citibank.firm_id
235
+ end
236
+
237
+ def test_dependence
238
+ num_accounts = Account.count
239
+
240
+ firm = Firm.find(1)
241
+ assert !firm.account.nil?
242
+ account_id = firm.account.id
243
+ assert_equal [], Account.destroyed_account_ids[firm.id]
244
+
245
+ firm.destroy
246
+ assert_equal num_accounts - 1, Account.count
247
+ assert_equal [account_id], Account.destroyed_account_ids[firm.id]
248
+ end
249
+
250
+ def test_exclusive_dependence
251
+ num_accounts = Account.count
252
+
253
+ firm = ExclusivelyDependentFirm.find(9)
254
+ assert !firm.account.nil?
255
+ account_id = firm.account.id
256
+ assert_equal [], Account.destroyed_account_ids[firm.id]
257
+
258
+ firm.destroy
259
+ assert_equal num_accounts - 1, Account.count
260
+ assert_equal [], Account.destroyed_account_ids[firm.id]
261
+ end
262
+
263
+ def test_dependence_with_nil_associate
264
+ firm = DependentFirm.new(:name => 'nullify')
265
+ firm.save!
266
+ assert_nothing_raised { firm.destroy }
267
+ end
268
+
269
+ def test_succesful_build_association
102
270
  firm = Firm.new("name" => "GlobalMegaCorp")
103
271
  firm.save
104
-
272
+
105
273
  account = firm.build_account("credit_limit" => 1000)
106
274
  assert account.save
107
275
  assert_equal account, firm.account
@@ -110,291 +278,1290 @@ class HasOneAssociationsTest < Test::Unit::TestCase
110
278
  def test_failing_build_association
111
279
  firm = Firm.new("name" => "GlobalMegaCorp")
112
280
  firm.save
113
-
281
+
114
282
  account = firm.build_account
115
283
  assert !account.save
116
284
  assert_equal "can't be empty", account.errors.on("credit_limit")
117
285
  end
118
286
 
119
- def test_create
287
+ def test_build_association_twice_without_saving_affects_nothing
288
+ count_of_account = Account.count
289
+ firm = Firm.find(:first)
290
+ account1 = firm.build_account("credit_limit" => 1000)
291
+ account2 = firm.build_account("credit_limit" => 2000)
292
+
293
+ assert_equal count_of_account, Account.count
294
+ end
295
+
296
+ def test_create_association
297
+ firm = Firm.create(:name => "GlobalMegaCorp")
298
+ account = firm.create_account(:credit_limit => 1000)
299
+ assert_equal account, firm.reload.account
300
+ end
301
+
302
+ def test_build
120
303
  firm = Firm.new("name" => "GlobalMegaCorp")
121
304
  firm.save
122
- assert_equal firm.create_account("credit_limit" => 1000), firm.account
305
+
306
+ firm.account = account = Account.new("credit_limit" => 1000)
307
+ assert_equal account, firm.account
308
+ assert account.save
309
+ assert_equal account, firm.account
123
310
  end
124
-
125
- def test_dependence
311
+
312
+ def test_build_before_child_saved
126
313
  firm = Firm.find(1)
127
- assert !firm.account.nil?
128
- firm.destroy
129
- assert_equal 1, Account.find_all.length
130
- end
131
314
 
132
- def test_dependence_with_missing_association
133
- Account.destroy_all
134
- firm = Firm.find(1)
135
- assert !firm.has_account?
136
- firm.destroy
315
+ account = firm.account.build("credit_limit" => 1000)
316
+ assert_equal account, firm.account
317
+ assert account.new_record?
318
+ assert firm.save
319
+ assert_equal account, firm.account
320
+ assert !account.new_record?
137
321
  end
138
- end
139
322
 
323
+ def test_build_before_either_saved
324
+ firm = Firm.new("name" => "GlobalMegaCorp")
140
325
 
141
- class HasManyAssociationsTest < Test::Unit::TestCase
142
- def setup
143
- create_fixtures "accounts", "companies", "accounts", "developers", "projects", "developers_projects", "topics"
144
- @signals37 = Firm.find(1)
326
+ firm.account = account = Account.new("credit_limit" => 1000)
327
+ assert_equal account, firm.account
328
+ assert account.new_record?
329
+ assert firm.save
330
+ assert_equal account, firm.account
331
+ assert !account.new_record?
145
332
  end
146
-
147
- def force_signal37_to_load_all_clients_of_firm
148
- @signals37.clients_of_firm.each {|f| }
333
+
334
+ def test_failing_build_association
335
+ firm = Firm.new("name" => "GlobalMegaCorp")
336
+ firm.save
337
+
338
+ firm.account = account = Account.new
339
+ assert_equal account, firm.account
340
+ assert !account.save
341
+ assert_equal account, firm.account
342
+ assert_equal "can't be empty", account.errors.on("credit_limit")
149
343
  end
150
-
151
- def test_finding
152
- assert_equal 2, Firm.find_first.clients.length
344
+
345
+ def test_create
346
+ firm = Firm.new("name" => "GlobalMegaCorp")
347
+ firm.save
348
+ firm.account = account = Account.create("credit_limit" => 1000)
349
+ assert_equal account, firm.account
153
350
  end
154
351
 
155
- def test_finding_default_orders
156
- assert_equal "Summit", Firm.find_first.clients.first.name
352
+ def test_create_before_save
353
+ firm = Firm.new("name" => "GlobalMegaCorp")
354
+ firm.account = account = Account.create("credit_limit" => 1000)
355
+ assert_equal account, firm.account
157
356
  end
158
357
 
159
- def test_finding_with_different_class_name_and_order
160
- assert_equal "Microsoft", Firm.find_first.clients_sorted_desc.first.name
358
+ def test_dependence_with_missing_association
359
+ Account.destroy_all
360
+ firm = Firm.find(1)
361
+ assert firm.account.nil?
362
+ firm.destroy
161
363
  end
162
364
 
163
- def test_finding_with_foreign_key
164
- assert_equal "Microsoft", Firm.find_first.clients_of_firm.first.name
365
+ def test_dependence_with_missing_association_and_nullify
366
+ Account.destroy_all
367
+ firm = DependentFirm.find(:first)
368
+ assert firm.account.nil?
369
+ firm.destroy
165
370
  end
166
371
 
167
- def test_finding_with_condition
168
- assert_equal "Microsoft", Firm.find_first.clients_like_ms.first.name
372
+ def test_assignment_before_parent_saved
373
+ firm = Firm.new("name" => "GlobalMegaCorp")
374
+ firm.account = a = Account.find(1)
375
+ assert firm.new_record?
376
+ assert_equal a, firm.account
377
+ assert firm.save
378
+ assert_equal a, firm.account
379
+ assert_equal a, firm.account(true)
169
380
  end
170
381
 
171
- def test_finding_using_sql
172
- firm = Firm.find_first
173
- firm.clients_using_sql.first
174
- assert_equal "Microsoft", firm.clients_using_sql.first.name
175
- assert_equal 1, firm.clients_using_sql.size
176
- assert_equal 1, Firm.find_first.clients_using_sql.size
382
+ def test_finding_with_interpolated_condition
383
+ firm = Firm.find(:first)
384
+ superior = firm.clients.create(:name => 'SuperiorCo')
385
+ superior.rating = 10
386
+ superior.save
387
+ assert_equal 10, firm.clients_with_interpolated_conditions.first.rating
177
388
  end
178
389
 
179
- def test_find_all
180
- assert_equal 2, Firm.find_first.clients.find_all("type = 'Client'").length
181
- assert_equal 1, Firm.find_first.clients.find_all("name = 'Summit'").length
390
+ def test_assignment_before_child_saved
391
+ firm = Firm.find(1)
392
+ firm.account = a = Account.new("credit_limit" => 1000)
393
+ assert !a.new_record?
394
+ assert_equal a, firm.account
395
+ assert_equal a, firm.account
396
+ assert_equal a, firm.account(true)
182
397
  end
183
398
 
184
- def test_find_in_collection
185
- assert_equal Client.find(2).name, @signals37.clients.find(2).name
186
- assert_equal Client.find(2).name, @signals37.clients.find {|c| c.name == @signals37.clients.find(2).name }.name
187
- assert_raises(ActiveRecord::RecordNotFound) { @signals37.clients.find(6) }
399
+ def test_assignment_before_either_saved
400
+ firm = Firm.new("name" => "GlobalMegaCorp")
401
+ firm.account = a = Account.new("credit_limit" => 1000)
402
+ assert firm.new_record?
403
+ assert a.new_record?
404
+ assert_equal a, firm.account
405
+ assert firm.save
406
+ assert !firm.new_record?
407
+ assert !a.new_record?
408
+ assert_equal a, firm.account
409
+ assert_equal a, firm.account(true)
188
410
  end
189
411
 
190
- def test_adding
191
- force_signal37_to_load_all_clients_of_firm
192
- natural = Client.new("name" => "Natural Company")
193
- @signals37.clients_of_firm << natural
194
- assert_equal 2, @signals37.clients_of_firm.size # checking via the collection
195
- assert_equal 2, @signals37.clients_of_firm(true).size # checking using the db
196
- assert_equal natural, @signals37.clients_of_firm.last
412
+ def test_not_resaved_when_unchanged
413
+ firm = Firm.find(:first, :include => :account)
414
+ assert_queries(1) { firm.save! }
415
+
416
+ firm = Firm.find(:first)
417
+ firm.account = Account.find(:first)
418
+ assert_queries(1) { firm.save! }
419
+
420
+ firm = Firm.find(:first).clone
421
+ firm.account = Account.find(:first)
422
+ assert_queries(2) { firm.save! }
423
+
424
+ firm = Firm.find(:first).clone
425
+ firm.account = Account.find(:first).clone
426
+ assert_queries(2) { firm.save! }
197
427
  end
198
-
199
- def test_adding_a_mismatch_class
200
- assert_raises(ActiveRecord::AssociationTypeMismatch) { @signals37.clients_of_firm << nil }
201
- assert_raises(ActiveRecord::AssociationTypeMismatch) { @signals37.clients_of_firm << 1 }
202
- assert_raises(ActiveRecord::AssociationTypeMismatch) { @signals37.clients_of_firm << Topic.find(1) }
428
+
429
+ def test_save_still_works_after_accessing_nil_has_one
430
+ jp = Company.new :name => 'Jaded Pixel'
431
+ jp.dummy_account.nil?
432
+
433
+ assert_nothing_raised do
434
+ jp.save!
435
+ end
203
436
  end
204
-
205
- def test_adding_a_collection
206
- force_signal37_to_load_all_clients_of_firm
207
- @signals37.clients_of_firm.concat(Client.new("name" => "Natural Company"), Client.new("name" => "Apple"))
208
- assert_equal 3, @signals37.clients_of_firm.size
209
- assert_equal 3, @signals37.clients_of_firm(true).size
437
+
438
+ end
439
+
440
+
441
+ class HasManyAssociationsTest < Test::Unit::TestCase
442
+ fixtures :accounts, :companies, :developers, :projects,
443
+ :developers_projects, :topics, :authors, :comments
444
+
445
+ def setup
446
+ Client.destroyed_client_ids.clear
210
447
  end
211
448
 
212
- def test_build
213
- new_client = @signals37.clients_of_firm.build("name" => "Another Client")
214
- assert_equal "Another Client", new_client.name
215
- assert new_client.save
216
- assert_equal 2, @signals37.clients_of_firm(true).size
449
+ def force_signal37_to_load_all_clients_of_firm
450
+ companies(:first_firm).clients_of_firm.each {|f| }
217
451
  end
218
-
219
- def test_create
220
- force_signal37_to_load_all_clients_of_firm
221
- new_client = @signals37.clients_of_firm.create("name" => "Another Client")
222
- assert_equal new_client, @signals37.clients_of_firm.last
223
- assert_equal new_client, @signals37.clients_of_firm(true).last
452
+
453
+ def test_counting_with_counter_sql
454
+ assert_equal 2, Firm.find(:first).clients.count
224
455
  end
225
456
 
226
- def test_deleting
227
- force_signal37_to_load_all_clients_of_firm
228
- @signals37.clients_of_firm.delete(@signals37.clients_of_firm.first)
229
- assert_equal 0, @signals37.clients_of_firm.size
230
- assert_equal 0, @signals37.clients_of_firm(true).size
457
+ def test_counting
458
+ assert_equal 2, Firm.find(:first).plain_clients.count
231
459
  end
232
460
 
233
- def test_deleting_a_collection
234
- force_signal37_to_load_all_clients_of_firm
235
- @signals37.clients_of_firm.create("name" => "Another Client")
236
- assert_equal 2, @signals37.clients_of_firm.size
237
- @signals37.clients_of_firm.delete([@signals37.clients_of_firm[0], @signals37.clients_of_firm[1]])
238
- assert_equal 0, @signals37.clients_of_firm.size
239
- assert_equal 0, @signals37.clients_of_firm(true).size
461
+ def test_counting_with_single_conditions
462
+ assert_equal 2, Firm.find(:first).plain_clients.count(:conditions => '1=1')
240
463
  end
241
464
 
242
- def test_deleting_a_association_collection
243
- force_signal37_to_load_all_clients_of_firm
244
- @signals37.clients_of_firm.create("name" => "Another Client")
245
- assert_equal 2, @signals37.clients_of_firm.size
246
- @signals37.clients_of_firm.delete(@signals37.clients_of_firm)
247
- assert_equal 0, @signals37.clients_of_firm.size
248
- assert_equal 0, @signals37.clients_of_firm(true).size
465
+ def test_counting_with_single_hash
466
+ assert_equal 2, Firm.find(:first).plain_clients.count(:conditions => '1=1')
249
467
  end
250
468
 
251
- def test_deleting_a_item_which_is_not_in_the_collection
252
- force_signal37_to_load_all_clients_of_firm
253
- summit = Client.find_first("name = 'Summit'")
254
- @signals37.clients_of_firm.delete(summit)
255
- assert_equal 1, @signals37.clients_of_firm.size
256
- assert_equal 1, @signals37.clients_of_firm(true).size
257
- assert_equal 2, summit.client_of
469
+ def test_counting_with_column_name_and_hash
470
+ assert_equal 2, Firm.find(:first).plain_clients.count(:all, :conditions => '1=1')
258
471
  end
259
472
 
260
- def test_destroy_all
261
- force_signal37_to_load_all_clients_of_firm
262
- assert !@signals37.clients_of_firm.empty?
263
- @signals37.clients_of_firm.destroy_all
264
- assert @signals37.clients_of_firm.empty?
265
- assert @signals37.clients_of_firm(true).empty?
473
+ def test_finding
474
+ assert_equal 2, Firm.find(:first).clients.length
266
475
  end
267
476
 
268
- def test_dependence
269
- assert_equal 2, Client.find_all.length
270
- Firm.find_first.destroy
271
- assert_equal 0, Client.find_all.length
477
+ def test_find_many_with_merged_options
478
+ assert_equal 1, companies(:first_firm).limited_clients.size
479
+ assert_equal 1, companies(:first_firm).limited_clients.find(:all).size
480
+ assert_equal 2, companies(:first_firm).limited_clients.find(:all, :limit => nil).size
272
481
  end
273
482
 
274
- def test_dependence_with_transaction_support_on_failure
275
- assert_equal 2, Client.find_all.length
276
- firm = Firm.find_first
277
- clients = firm.clients
278
- clients.last.instance_eval { def before_destroy() raise "Trigger rollback" end }
483
+ def test_dynamic_find_should_respect_association_order
484
+ assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find(:first, :conditions => "type = 'Client'")
485
+ assert_equal companies(:second_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client')
486
+ end
279
487
 
280
- firm.destroy rescue "do nothing"
488
+ def test_dynamic_find_order_should_override_association_order
489
+ assert_equal companies(:first_client), companies(:first_firm).clients_sorted_desc.find(:first, :conditions => "type = 'Client'", :order => 'id')
490
+ assert_equal companies(:first_client), companies(:first_firm).clients_sorted_desc.find_by_type('Client', :order => 'id')
491
+ end
281
492
 
282
- assert_equal 2, Client.find_all.length
493
+ def test_dynamic_find_all_should_respect_association_order
494
+ assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find(:all, :conditions => "type = 'Client'")
495
+ assert_equal [companies(:second_client), companies(:first_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client')
283
496
  end
284
497
 
285
- def test_dependence_on_account
286
- assert_equal 2, Account.find_all.length
287
- @signals37.destroy
288
- assert_equal 1, Account.find_all.length
498
+ def test_dynamic_find_all_order_should_override_association_order
499
+ assert_equal [companies(:first_client), companies(:second_client)], companies(:first_firm).clients_sorted_desc.find(:all, :conditions => "type = 'Client'", :order => 'id')
500
+ assert_equal [companies(:first_client), companies(:second_client)], companies(:first_firm).clients_sorted_desc.find_all_by_type('Client', :order => 'id')
289
501
  end
290
502
 
291
- def test_included_in_collection
292
- assert @signals37.clients.include?(Client.find(2))
503
+ def test_dynamic_find_all_should_respect_association_limit
504
+ assert_equal 1, companies(:first_firm).limited_clients.find(:all, :conditions => "type = 'Client'").length
505
+ assert_equal 1, companies(:first_firm).limited_clients.find_all_by_type('Client').length
293
506
  end
294
- end
295
507
 
296
- class BelongsToAssociationsTest < Test::Unit::TestCase
297
- def setup
298
- create_fixtures "accounts", "companies", "accounts", "developers", "projects", "developers_projects", "topics"
299
- @signals37 = Firm.find(1)
508
+ def test_dynamic_find_all_limit_should_override_association_limit
509
+ assert_equal 2, companies(:first_firm).limited_clients.find(:all, :conditions => "type = 'Client'", :limit => 9_000).length
510
+ assert_equal 2, companies(:first_firm).limited_clients.find_all_by_type('Client', :limit => 9_000).length
300
511
  end
301
512
 
302
- def test_belongs_to
303
- Client.find(3).firm.name
304
- assert_equal @signals37.name, Client.find(3).firm.name
305
- assert !Client.find(3).firm.nil?, "Microsoft should have a firm"
513
+ def test_triple_equality
514
+ assert !(Array === Firm.find(:first).clients)
515
+ assert Firm.find(:first).clients === Array
306
516
  end
307
517
 
308
- def test_type_mismatch
309
- assert_raises(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = 1 }
310
- assert_raises(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = Project.find(1) }
518
+ def test_finding_default_orders
519
+ assert_equal "Summit", Firm.find(:first).clients.first.name
311
520
  end
312
521
 
313
- def test_natural_assignment
314
- apple = Firm.create("name" => "Apple")
315
- citibank = Account.create("credit_limit" => 10)
316
- citibank.firm = apple
317
- assert_equal apple.id, citibank.firm_id
522
+ def test_finding_with_different_class_name_and_order
523
+ assert_equal "Microsoft", Firm.find(:first).clients_sorted_desc.first.name
318
524
  end
319
-
320
- def test_natural_assignment_to_nil
321
- client = Client.find(3)
322
- client.firm = nil
323
- client.save
324
- assert_nil client.firm(true)
325
- assert_nil client.client_of
525
+
526
+ def test_finding_with_foreign_key
527
+ assert_equal "Microsoft", Firm.find(:first).clients_of_firm.first.name
326
528
  end
327
-
328
- def test_with_different_class_name
329
- assert_equal Company.find(1).name, Company.find(3).firm_with_other_name.name
330
- assert !Company.find(3).firm_with_other_name.empty?, "Microsoft should have a firm"
529
+
530
+ def test_finding_with_condition
531
+ assert_equal "Microsoft", Firm.find(:first).clients_like_ms.first.name
331
532
  end
332
533
 
333
- def test_with_condition
334
- assert_equal Company.find(1).name, Company.find(3).firm_with_condition.name
335
- assert !Company.find(3).firm_with_condition.empty?, "Microsoft should have a firm"
534
+ def test_finding_with_condition_hash
535
+ assert_equal "Microsoft", Firm.find(:first).clients_like_ms_with_hash_conditions.first.name
336
536
  end
337
-
338
- def test_belongs_to_counter
339
- debate = Topic.create("title" => "debate")
340
- assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet"
341
537
 
342
- trash = debate.replies.create("title" => "blah!", "content" => "world around!")
343
- assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply created"
538
+ def test_finding_using_sql
539
+ firm = Firm.find(:first)
540
+ first_client = firm.clients_using_sql.first
541
+ assert_not_nil first_client
542
+ assert_equal "Microsoft", first_client.name
543
+ assert_equal 1, firm.clients_using_sql.size
544
+ assert_equal 1, Firm.find(:first).clients_using_sql.size
545
+ end
344
546
 
345
- trash.destroy
346
- assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted"
547
+ def test_counting_using_sql
548
+ assert_equal 1, Firm.find(:first).clients_using_counter_sql.size
549
+ assert Firm.find(:first).clients_using_counter_sql.any?
550
+ assert_equal 0, Firm.find(:first).clients_using_zero_counter_sql.size
551
+ assert !Firm.find(:first).clients_using_zero_counter_sql.any?
347
552
  end
348
553
 
349
- def xtest_counter_cache
350
- apple = Firm.create("name" => "Apple")
351
- final_cut = apple.clients.create("name" => "Final Cut")
554
+ def test_counting_non_existant_items_using_sql
555
+ assert_equal 0, Firm.find(:first).no_clients_using_counter_sql.size
556
+ end
352
557
 
353
- apple.clients.to_s
354
- assert_equal 1, apple.clients.size, "Created one client"
355
-
356
- apple.companies_count = 2
357
- apple.save
558
+ def test_belongs_to_sanity
559
+ c = Client.new
560
+ assert_nil c.firm
358
561
 
359
- apple = Firm.find_first("name = 'Apple'")
360
- assert_equal 2, apple.clients.size, "Should use the new cached number"
562
+ if c.firm
563
+ assert false, "belongs_to failed if check"
564
+ end
361
565
 
362
- apple.clients.to_s
363
- assert_equal 1, apple.clients.size, "Should not use the cached number, but go to the database"
566
+ unless c.firm
567
+ else
568
+ assert false, "belongs_to failed unless check"
569
+ end
364
570
  end
365
- end
366
571
 
572
+ def test_find_ids
573
+ firm = Firm.find(:first)
367
574
 
368
- class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
369
- def setup
370
- create_fixtures "accounts"
371
- create_fixtures "companies"
372
- create_fixtures "accounts"
373
- create_fixtures "developers"
374
- create_fixtures "projects"
375
- create_fixtures "developers_projects"
376
- @signals37 = Firm.find(1)
377
- end
378
-
379
- def test_has_and_belongs_to_many
380
- david = Developer.find(1)
575
+ assert_raises(ActiveRecord::RecordNotFound) { firm.clients.find }
381
576
 
382
- assert !david.projects.empty?
383
- assert_equal 2, david.projects.size
577
+ client = firm.clients.find(2)
578
+ assert_kind_of Client, client
384
579
 
385
- active_record = Project.find(1)
386
- assert !active_record.developers.empty?
387
- assert_equal 2, active_record.developers.size
388
- assert_equal david.name, active_record.developers.first.name
580
+ client_ary = firm.clients.find([2])
581
+ assert_kind_of Array, client_ary
582
+ assert_equal client, client_ary.first
583
+
584
+ client_ary = firm.clients.find(2, 3)
585
+ assert_kind_of Array, client_ary
586
+ assert_equal 2, client_ary.size
587
+ assert_equal client, client_ary.first
588
+
589
+ assert_raises(ActiveRecord::RecordNotFound) { firm.clients.find(2, 99) }
389
590
  end
390
-
391
- def test_addings
591
+
592
+ def test_find_string_ids_when_using_finder_sql
593
+ firm = Firm.find(:first)
594
+
595
+ client = firm.clients_using_finder_sql.find("2")
596
+ assert_kind_of Client, client
597
+
598
+ client_ary = firm.clients_using_finder_sql.find(["2"])
599
+ assert_kind_of Array, client_ary
600
+ assert_equal client, client_ary.first
601
+
602
+ client_ary = firm.clients_using_finder_sql.find("2", "3")
603
+ assert_kind_of Array, client_ary
604
+ assert_equal 2, client_ary.size
605
+ assert client_ary.include?(client)
606
+ end
607
+
608
+ def test_find_all
609
+ firm = Firm.find(:first)
610
+ assert_equal 2, firm.clients.find(:all, :conditions => "#{QUOTED_TYPE} = 'Client'").length
611
+ assert_equal 1, firm.clients.find(:all, :conditions => "name = 'Summit'").length
612
+ end
613
+
614
+ def test_find_all_sanitized
615
+ firm = Firm.find(:first)
616
+ summit = firm.clients.find(:all, :conditions => "name = 'Summit'")
617
+ assert_equal summit, firm.clients.find(:all, :conditions => ["name = ?", "Summit"])
618
+ assert_equal summit, firm.clients.find(:all, :conditions => ["name = :name", { :name => "Summit" }])
619
+ end
620
+
621
+ def test_find_first
622
+ firm = Firm.find(:first)
623
+ client2 = Client.find(2)
624
+ assert_equal firm.clients.first, firm.clients.find(:first)
625
+ assert_equal client2, firm.clients.find(:first, :conditions => "#{QUOTED_TYPE} = 'Client'")
626
+ end
627
+
628
+ def test_find_first_sanitized
629
+ firm = Firm.find(:first)
630
+ client2 = Client.find(2)
631
+ assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = ?", 'Client'])
632
+ assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }])
633
+ end
634
+
635
+ def test_find_in_collection
636
+ assert_equal Client.find(2).name, companies(:first_firm).clients.find(2).name
637
+ assert_raises(ActiveRecord::RecordNotFound) { companies(:first_firm).clients.find(6) }
638
+ end
639
+
640
+ def test_find_grouped
641
+ all_clients_of_firm1 = Client.find(:all, :conditions => "firm_id = 1")
642
+ grouped_clients_of_firm1 = Client.find(:all, :conditions => "firm_id = 1", :group => "firm_id", :select => 'firm_id, count(id) as clients_count')
643
+ assert_equal 2, all_clients_of_firm1.size
644
+ assert_equal 1, grouped_clients_of_firm1.size
645
+ end
646
+
647
+ def test_adding
648
+ force_signal37_to_load_all_clients_of_firm
649
+ natural = Client.new("name" => "Natural Company")
650
+ companies(:first_firm).clients_of_firm << natural
651
+ assert_equal 2, companies(:first_firm).clients_of_firm.size # checking via the collection
652
+ assert_equal 2, companies(:first_firm).clients_of_firm(true).size # checking using the db
653
+ assert_equal natural, companies(:first_firm).clients_of_firm.last
654
+ end
655
+
656
+ def test_adding_using_create
657
+ first_firm = companies(:first_firm)
658
+ assert_equal 2, first_firm.plain_clients.size
659
+ natural = first_firm.plain_clients.create(:name => "Natural Company")
660
+ assert_equal 3, first_firm.plain_clients.length
661
+ assert_equal 3, first_firm.plain_clients.size
662
+ end
663
+
664
+ def test_create_with_bang_on_has_many_when_parent_is_new_raises
665
+ assert_raises(ActiveRecord::RecordNotSaved) do
666
+ firm = Firm.new
667
+ firm.plain_clients.create! :name=>"Whoever"
668
+ end
669
+ end
670
+
671
+ def test_regular_create_on_has_many_when_parent_is_new_raises
672
+ assert_raises(ActiveRecord::RecordNotSaved) do
673
+ firm = Firm.new
674
+ firm.plain_clients.create :name=>"Whoever"
675
+ end
676
+ end
677
+
678
+ def test_create_with_bang_on_has_many_raises_when_record_not_saved
679
+ assert_raises(ActiveRecord::RecordInvalid) do
680
+ firm = Firm.find(:first)
681
+ firm.plain_clients.create!
682
+ end
683
+ end
684
+
685
+ def test_create_with_bang_on_habtm_when_parent_is_new_raises
686
+ assert_raises(ActiveRecord::RecordNotSaved) do
687
+ Developer.new("name" => "Aredridel").projects.create!
688
+ end
689
+ end
690
+
691
+ def test_adding_a_mismatch_class
692
+ assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << nil }
693
+ assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << 1 }
694
+ assert_raises(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).clients_of_firm << Topic.find(1) }
695
+ end
696
+
697
+ def test_adding_a_collection
698
+ force_signal37_to_load_all_clients_of_firm
699
+ companies(:first_firm).clients_of_firm.concat([Client.new("name" => "Natural Company"), Client.new("name" => "Apple")])
700
+ assert_equal 3, companies(:first_firm).clients_of_firm.size
701
+ assert_equal 3, companies(:first_firm).clients_of_firm(true).size
702
+ end
703
+
704
+ def test_adding_before_save
705
+ no_of_firms = Firm.count
706
+ no_of_clients = Client.count
707
+
708
+ new_firm = Firm.new("name" => "A New Firm, Inc")
709
+ c = Client.new("name" => "Apple")
710
+
711
+ new_firm.clients_of_firm.push Client.new("name" => "Natural Company")
712
+ assert_equal 1, new_firm.clients_of_firm.size
713
+ new_firm.clients_of_firm << c
714
+ assert_equal 2, new_firm.clients_of_firm.size
715
+
716
+ assert_equal no_of_firms, Firm.count # Firm was not saved to database.
717
+ assert_equal no_of_clients, Client.count # Clients were not saved to database.
718
+ assert new_firm.save
719
+ assert !new_firm.new_record?
720
+ assert !c.new_record?
721
+ assert_equal new_firm, c.firm
722
+ assert_equal no_of_firms+1, Firm.count # Firm was saved to database.
723
+ assert_equal no_of_clients+2, Client.count # Clients were saved to database.
724
+
725
+ assert_equal 2, new_firm.clients_of_firm.size
726
+ assert_equal 2, new_firm.clients_of_firm(true).size
727
+ end
728
+
729
+ def test_invalid_adding
730
+ firm = Firm.find(1)
731
+ assert !(firm.clients_of_firm << c = Client.new)
732
+ assert c.new_record?
733
+ assert !firm.valid?
734
+ assert !firm.save
735
+ assert c.new_record?
736
+ end
737
+
738
+ def test_invalid_adding_before_save
739
+ no_of_firms = Firm.count
740
+ no_of_clients = Client.count
741
+ new_firm = Firm.new("name" => "A New Firm, Inc")
742
+ new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
743
+ assert c.new_record?
744
+ assert !c.valid?
745
+ assert !new_firm.valid?
746
+ assert !new_firm.save
747
+ assert c.new_record?
748
+ assert new_firm.new_record?
749
+ end
750
+
751
+ def test_build
752
+ new_client = companies(:first_firm).clients_of_firm.build("name" => "Another Client")
753
+ assert_equal "Another Client", new_client.name
754
+ assert new_client.new_record?
755
+ assert_equal new_client, companies(:first_firm).clients_of_firm.last
756
+ assert companies(:first_firm).save
757
+ assert !new_client.new_record?
758
+ assert_equal 2, companies(:first_firm).clients_of_firm(true).size
759
+ end
760
+
761
+ def test_build_many
762
+ new_clients = companies(:first_firm).clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}])
763
+ assert_equal 2, new_clients.size
764
+
765
+ assert companies(:first_firm).save
766
+ assert_equal 3, companies(:first_firm).clients_of_firm(true).size
767
+ end
768
+
769
+ def test_build_without_loading_association
770
+ first_topic = topics(:first)
771
+ Reply.column_names
772
+
773
+ assert_equal 1, first_topic.replies.length
774
+
775
+ assert_no_queries do
776
+ first_topic.replies.build(:title => "Not saved", :content => "Superstars")
777
+ assert_equal 2, first_topic.replies.size
778
+ end
779
+
780
+ assert_equal 2, first_topic.replies.to_ary.size
781
+ end
782
+
783
+ def test_create_without_loading_association
784
+ first_firm = companies(:first_firm)
785
+ Firm.column_names
786
+ Client.column_names
787
+
788
+ assert_equal 1, first_firm.clients_of_firm.size
789
+ first_firm.clients_of_firm.reset
790
+
791
+ assert_queries(1) do
792
+ first_firm.clients_of_firm.create(:name => "Superstars")
793
+ end
794
+
795
+ assert_equal 2, first_firm.clients_of_firm.size
796
+ end
797
+
798
+ def test_invalid_build
799
+ new_client = companies(:first_firm).clients_of_firm.build
800
+ assert new_client.new_record?
801
+ assert !new_client.valid?
802
+ assert_equal new_client, companies(:first_firm).clients_of_firm.last
803
+ assert !companies(:first_firm).save
804
+ assert new_client.new_record?
805
+ assert_equal 1, companies(:first_firm).clients_of_firm(true).size
806
+ end
807
+
808
+ def test_create
809
+ force_signal37_to_load_all_clients_of_firm
810
+ new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
811
+ assert !new_client.new_record?
812
+ assert_equal new_client, companies(:first_firm).clients_of_firm.last
813
+ assert_equal new_client, companies(:first_firm).clients_of_firm(true).last
814
+ end
815
+
816
+ def test_create_many
817
+ companies(:first_firm).clients_of_firm.create([{"name" => "Another Client"}, {"name" => "Another Client II"}])
818
+ assert_equal 3, companies(:first_firm).clients_of_firm(true).size
819
+ end
820
+
821
+ def test_find_or_initialize
822
+ the_client = companies(:first_firm).clients.find_or_initialize_by_name("Yet another client")
823
+ assert_equal companies(:first_firm).id, the_client.firm_id
824
+ assert_equal "Yet another client", the_client.name
825
+ assert the_client.new_record?
826
+ end
827
+
828
+ def test_find_or_create
829
+ number_of_clients = companies(:first_firm).clients.size
830
+ the_client = companies(:first_firm).clients.find_or_create_by_name("Yet another client")
831
+ assert_equal number_of_clients + 1, companies(:first_firm, :reload).clients.size
832
+ assert_equal the_client, companies(:first_firm).clients.find_or_create_by_name("Yet another client")
833
+ assert_equal number_of_clients + 1, companies(:first_firm, :reload).clients.size
834
+ end
835
+
836
+ def test_deleting
837
+ force_signal37_to_load_all_clients_of_firm
838
+ companies(:first_firm).clients_of_firm.delete(companies(:first_firm).clients_of_firm.first)
839
+ assert_equal 0, companies(:first_firm).clients_of_firm.size
840
+ assert_equal 0, companies(:first_firm).clients_of_firm(true).size
841
+ end
842
+
843
+ def test_deleting_before_save
844
+ new_firm = Firm.new("name" => "A New Firm, Inc.")
845
+ new_client = new_firm.clients_of_firm.build("name" => "Another Client")
846
+ assert_equal 1, new_firm.clients_of_firm.size
847
+ new_firm.clients_of_firm.delete(new_client)
848
+ assert_equal 0, new_firm.clients_of_firm.size
849
+ end
850
+
851
+ def test_deleting_a_collection
852
+ force_signal37_to_load_all_clients_of_firm
853
+ companies(:first_firm).clients_of_firm.create("name" => "Another Client")
854
+ assert_equal 2, companies(:first_firm).clients_of_firm.size
855
+ companies(:first_firm).clients_of_firm.delete([companies(:first_firm).clients_of_firm[0], companies(:first_firm).clients_of_firm[1]])
856
+ assert_equal 0, companies(:first_firm).clients_of_firm.size
857
+ assert_equal 0, companies(:first_firm).clients_of_firm(true).size
858
+ end
859
+
860
+ def test_delete_all
861
+ force_signal37_to_load_all_clients_of_firm
862
+ companies(:first_firm).clients_of_firm.create("name" => "Another Client")
863
+ assert_equal 2, companies(:first_firm).clients_of_firm.size
864
+ companies(:first_firm).clients_of_firm.delete_all
865
+ assert_equal 0, companies(:first_firm).clients_of_firm.size
866
+ assert_equal 0, companies(:first_firm).clients_of_firm(true).size
867
+ end
868
+
869
+ def test_delete_all_with_not_yet_loaded_association_collection
870
+ force_signal37_to_load_all_clients_of_firm
871
+ companies(:first_firm).clients_of_firm.create("name" => "Another Client")
872
+ assert_equal 2, companies(:first_firm).clients_of_firm.size
873
+ companies(:first_firm).clients_of_firm.reset
874
+ companies(:first_firm).clients_of_firm.delete_all
875
+ assert_equal 0, companies(:first_firm).clients_of_firm.size
876
+ assert_equal 0, companies(:first_firm).clients_of_firm(true).size
877
+ end
878
+
879
+ def test_clearing_an_association_collection
880
+ firm = companies(:first_firm)
881
+ client_id = firm.clients_of_firm.first.id
882
+ assert_equal 1, firm.clients_of_firm.size
883
+
884
+ firm.clients_of_firm.clear
885
+
886
+ assert_equal 0, firm.clients_of_firm.size
887
+ assert_equal 0, firm.clients_of_firm(true).size
888
+ assert_equal [], Client.destroyed_client_ids[firm.id]
889
+
890
+ # Should not be destroyed since the association is not dependent.
891
+ assert_nothing_raised do
892
+ assert Client.find(client_id).firm.nil?
893
+ end
894
+ end
895
+
896
+ def test_clearing_a_dependent_association_collection
897
+ firm = companies(:first_firm)
898
+ client_id = firm.dependent_clients_of_firm.first.id
899
+ assert_equal 1, firm.dependent_clients_of_firm.size
900
+
901
+ # :dependent means destroy is called on each client
902
+ firm.dependent_clients_of_firm.clear
903
+
904
+ assert_equal 0, firm.dependent_clients_of_firm.size
905
+ assert_equal 0, firm.dependent_clients_of_firm(true).size
906
+ assert_equal [client_id], Client.destroyed_client_ids[firm.id]
907
+
908
+ # Should be destroyed since the association is dependent.
909
+ assert Client.find_by_id(client_id).nil?
910
+ end
911
+
912
+ def test_clearing_an_exclusively_dependent_association_collection
913
+ firm = companies(:first_firm)
914
+ client_id = firm.exclusively_dependent_clients_of_firm.first.id
915
+ assert_equal 1, firm.exclusively_dependent_clients_of_firm.size
916
+
917
+ assert_equal [], Client.destroyed_client_ids[firm.id]
918
+
919
+ # :exclusively_dependent means each client is deleted directly from
920
+ # the database without looping through them calling destroy.
921
+ firm.exclusively_dependent_clients_of_firm.clear
922
+
923
+ assert_equal 0, firm.exclusively_dependent_clients_of_firm.size
924
+ assert_equal 0, firm.exclusively_dependent_clients_of_firm(true).size
925
+ # no destroy-filters should have been called
926
+ assert_equal [], Client.destroyed_client_ids[firm.id]
927
+
928
+ # Should be destroyed since the association is exclusively dependent.
929
+ assert Client.find_by_id(client_id).nil?
930
+ end
931
+
932
+ def test_dependent_association_respects_optional_conditions_on_delete
933
+ firm = companies(:odegy)
934
+ Client.create(:client_of => firm.id, :name => "BigShot Inc.")
935
+ Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
936
+ # only one of two clients is included in the association due to the :conditions key
937
+ assert_equal 2, Client.find_all_by_client_of(firm.id).size
938
+ assert_equal 1, firm.dependent_conditional_clients_of_firm.size
939
+ firm.destroy
940
+ # only the correctly associated client should have been deleted
941
+ assert_equal 1, Client.find_all_by_client_of(firm.id).size
942
+ end
943
+
944
+ def test_dependent_association_respects_optional_sanitized_conditions_on_delete
945
+ firm = companies(:odegy)
946
+ Client.create(:client_of => firm.id, :name => "BigShot Inc.")
947
+ Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
948
+ # only one of two clients is included in the association due to the :conditions key
949
+ assert_equal 2, Client.find_all_by_client_of(firm.id).size
950
+ assert_equal 1, firm.dependent_sanitized_conditional_clients_of_firm.size
951
+ firm.destroy
952
+ # only the correctly associated client should have been deleted
953
+ assert_equal 1, Client.find_all_by_client_of(firm.id).size
954
+ end
955
+
956
+ def test_clearing_without_initial_access
957
+ firm = companies(:first_firm)
958
+
959
+ firm.clients_of_firm.clear
960
+
961
+ assert_equal 0, firm.clients_of_firm.size
962
+ assert_equal 0, firm.clients_of_firm(true).size
963
+ end
964
+
965
+ def test_deleting_a_item_which_is_not_in_the_collection
966
+ force_signal37_to_load_all_clients_of_firm
967
+ summit = Client.find_by_name('Summit')
968
+ companies(:first_firm).clients_of_firm.delete(summit)
969
+ assert_equal 1, companies(:first_firm).clients_of_firm.size
970
+ assert_equal 1, companies(:first_firm).clients_of_firm(true).size
971
+ assert_equal 2, summit.client_of
972
+ end
973
+
974
+ def test_deleting_type_mismatch
975
+ david = Developer.find(1)
976
+ david.projects.reload
977
+ assert_raises(ActiveRecord::AssociationTypeMismatch) { david.projects.delete(1) }
978
+ end
979
+
980
+ def test_deleting_self_type_mismatch
981
+ david = Developer.find(1)
982
+ david.projects.reload
983
+ assert_raises(ActiveRecord::AssociationTypeMismatch) { david.projects.delete(Project.find(1).developers) }
984
+ end
985
+
986
+ def test_destroy_all
987
+ force_signal37_to_load_all_clients_of_firm
988
+ assert !companies(:first_firm).clients_of_firm.empty?, "37signals has clients after load"
989
+ companies(:first_firm).clients_of_firm.destroy_all
990
+ assert companies(:first_firm).clients_of_firm.empty?, "37signals has no clients after destroy all"
991
+ assert companies(:first_firm).clients_of_firm(true).empty?, "37signals has no clients after destroy all and refresh"
992
+ end
993
+
994
+ def test_dependence
995
+ firm = companies(:first_firm)
996
+ assert_equal 2, firm.clients.size
997
+ firm.destroy
998
+ assert Client.find(:all, :conditions => "firm_id=#{firm.id}").empty?
999
+ end
1000
+
1001
+ def test_destroy_dependent_when_deleted_from_association
1002
+ firm = Firm.find(:first)
1003
+ assert_equal 2, firm.clients.size
1004
+
1005
+ client = firm.clients.first
1006
+ firm.clients.delete(client)
1007
+
1008
+ assert_raise(ActiveRecord::RecordNotFound) { Client.find(client.id) }
1009
+ assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find(client.id) }
1010
+ assert_equal 1, firm.clients.size
1011
+ end
1012
+
1013
+ def test_three_levels_of_dependence
1014
+ topic = Topic.create "title" => "neat and simple"
1015
+ reply = topic.replies.create "title" => "neat and simple", "content" => "still digging it"
1016
+ silly_reply = reply.replies.create "title" => "neat and simple", "content" => "ain't complaining"
1017
+
1018
+ assert_nothing_raised { topic.destroy }
1019
+ end
1020
+
1021
+ uses_transaction :test_dependence_with_transaction_support_on_failure
1022
+ def test_dependence_with_transaction_support_on_failure
1023
+ firm = companies(:first_firm)
1024
+ clients = firm.clients
1025
+ assert_equal 2, clients.length
1026
+ clients.last.instance_eval { def before_destroy() raise "Trigger rollback" end }
1027
+
1028
+ firm.destroy rescue "do nothing"
1029
+
1030
+ assert_equal 2, Client.find(:all, :conditions => "firm_id=#{firm.id}").size
1031
+ end
1032
+
1033
+ def test_dependence_on_account
1034
+ num_accounts = Account.count
1035
+ companies(:first_firm).destroy
1036
+ assert_equal num_accounts - 1, Account.count
1037
+ end
1038
+
1039
+ def test_depends_and_nullify
1040
+ num_accounts = Account.count
1041
+ num_companies = Company.count
1042
+
1043
+ core = companies(:rails_core)
1044
+ assert_equal accounts(:rails_core_account), core.account
1045
+ assert_equal companies(:leetsoft, :jadedpixel), core.companies
1046
+ core.destroy
1047
+ assert_nil accounts(:rails_core_account).reload.firm_id
1048
+ assert_nil companies(:leetsoft).reload.client_of
1049
+ assert_nil companies(:jadedpixel).reload.client_of
1050
+
1051
+
1052
+ assert_equal num_accounts, Account.count
1053
+ end
1054
+
1055
+ def test_included_in_collection
1056
+ assert companies(:first_firm).clients.include?(Client.find(2))
1057
+ end
1058
+
1059
+ def test_adding_array_and_collection
1060
+ assert_nothing_raised { Firm.find(:first).clients + Firm.find(:all).last.clients }
1061
+ end
1062
+
1063
+ def test_find_all_without_conditions
1064
+ firm = companies(:first_firm)
1065
+ assert_equal 2, firm.clients.find(:all).length
1066
+ end
1067
+
1068
+ def test_replace_with_less
1069
+ firm = Firm.find(:first)
1070
+ firm.clients = [companies(:first_client)]
1071
+ assert firm.save, "Could not save firm"
1072
+ firm.reload
1073
+ assert_equal 1, firm.clients.length
1074
+ end
1075
+
1076
+ def test_replace_with_less_and_dependent_nullify
1077
+ num_companies = Company.count
1078
+ companies(:rails_core).companies = []
1079
+ assert_equal num_companies, Company.count
1080
+ end
1081
+
1082
+ def test_replace_with_new
1083
+ firm = Firm.find(:first)
1084
+ firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
1085
+ firm.save
1086
+ firm.reload
1087
+ assert_equal 2, firm.clients.length
1088
+ assert !firm.clients.include?(:first_client)
1089
+ end
1090
+
1091
+ def test_replace_on_new_object
1092
+ firm = Firm.new("name" => "New Firm")
1093
+ firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
1094
+ assert firm.save
1095
+ firm.reload
1096
+ assert_equal 2, firm.clients.length
1097
+ assert firm.clients.include?(Client.find_by_name("New Client"))
1098
+ end
1099
+
1100
+ def test_get_ids
1101
+ assert_equal [companies(:first_client).id, companies(:second_client).id], companies(:first_firm).client_ids
1102
+ end
1103
+
1104
+ def test_assign_ids
1105
+ firm = Firm.new("name" => "Apple")
1106
+ firm.client_ids = [companies(:first_client).id, companies(:second_client).id]
1107
+ firm.save
1108
+ firm.reload
1109
+ assert_equal 2, firm.clients.length
1110
+ assert firm.clients.include?(companies(:second_client))
1111
+ end
1112
+
1113
+ def test_assign_ids_ignoring_blanks
1114
+ firm = Firm.create!(:name => 'Apple')
1115
+ firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, '']
1116
+ firm.save!
1117
+
1118
+ assert_equal 2, firm.clients(true).size
1119
+ assert firm.clients.include?(companies(:second_client))
1120
+ end
1121
+
1122
+ def test_get_ids_for_through
1123
+ assert_equal [comments(:eager_other_comment1).id], authors(:mary).comment_ids
1124
+ end
1125
+
1126
+ def test_assign_ids_for_through
1127
+ assert_raise(NoMethodError) { authors(:mary).comment_ids = [123] }
1128
+ end
1129
+
1130
+ def test_dynamic_find_should_respect_association_order_for_through
1131
+ assert_equal Comment.find(10), authors(:david).comments_desc.find(:first, :conditions => "comments.type = 'SpecialComment'")
1132
+ assert_equal Comment.find(10), authors(:david).comments_desc.find_by_type('SpecialComment')
1133
+ end
1134
+
1135
+ def test_dynamic_find_order_should_override_association_order_for_through
1136
+ assert_equal Comment.find(3), authors(:david).comments_desc.find(:first, :conditions => "comments.type = 'SpecialComment'", :order => 'comments.id')
1137
+ assert_equal Comment.find(3), authors(:david).comments_desc.find_by_type('SpecialComment', :order => 'comments.id')
1138
+ end
1139
+
1140
+ def test_dynamic_find_all_should_respect_association_order_for_through
1141
+ assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find(:all, :conditions => "comments.type = 'SpecialComment'")
1142
+ assert_equal [Comment.find(10), Comment.find(7), Comment.find(6), Comment.find(3)], authors(:david).comments_desc.find_all_by_type('SpecialComment')
1143
+ end
1144
+
1145
+ def test_dynamic_find_all_order_should_override_association_order_for_through
1146
+ assert_equal [Comment.find(3), Comment.find(6), Comment.find(7), Comment.find(10)], authors(:david).comments_desc.find(:all, :conditions => "comments.type = 'SpecialComment'", :order => 'comments.id')
1147
+ assert_equal [Comment.find(3), Comment.find(6), Comment.find(7), Comment.find(10)], authors(:david).comments_desc.find_all_by_type('SpecialComment', :order => 'comments.id')
1148
+ end
1149
+
1150
+ def test_dynamic_find_all_should_respect_association_limit_for_through
1151
+ assert_equal 1, authors(:david).limited_comments.find(:all, :conditions => "comments.type = 'SpecialComment'").length
1152
+ assert_equal 1, authors(:david).limited_comments.find_all_by_type('SpecialComment').length
1153
+ end
1154
+
1155
+ def test_dynamic_find_all_order_should_override_association_limit_for_through
1156
+ assert_equal 4, authors(:david).limited_comments.find(:all, :conditions => "comments.type = 'SpecialComment'", :limit => 9_000).length
1157
+ assert_equal 4, authors(:david).limited_comments.find_all_by_type('SpecialComment', :limit => 9_000).length
1158
+ end
1159
+
1160
+ end
1161
+
1162
+ class BelongsToAssociationsTest < Test::Unit::TestCase
1163
+ fixtures :accounts, :companies, :developers, :projects, :topics,
1164
+ :developers_projects, :computers, :authors, :posts, :tags, :taggings
1165
+
1166
+ def test_belongs_to
1167
+ Client.find(3).firm.name
1168
+ assert_equal companies(:first_firm).name, Client.find(3).firm.name
1169
+ assert !Client.find(3).firm.nil?, "Microsoft should have a firm"
1170
+ end
1171
+
1172
+ def test_proxy_assignment
1173
+ account = Account.find(1)
1174
+ assert_nothing_raised { account.firm = account.firm }
1175
+ end
1176
+
1177
+ def test_triple_equality
1178
+ assert Client.find(3).firm === Firm
1179
+ assert Firm === Client.find(3).firm
1180
+ end
1181
+
1182
+ def test_type_mismatch
1183
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = 1 }
1184
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = Project.find(1) }
1185
+ end
1186
+
1187
+ def test_natural_assignment
1188
+ apple = Firm.create("name" => "Apple")
1189
+ citibank = Account.create("credit_limit" => 10)
1190
+ citibank.firm = apple
1191
+ assert_equal apple.id, citibank.firm_id
1192
+ end
1193
+
1194
+ def test_no_unexpected_aliasing
1195
+ first_firm = companies(:first_firm)
1196
+ another_firm = companies(:another_firm)
1197
+
1198
+ citibank = Account.create("credit_limit" => 10)
1199
+ citibank.firm = first_firm
1200
+ original_proxy = citibank.firm
1201
+ citibank.firm = another_firm
1202
+
1203
+ assert_equal first_firm.object_id, original_proxy.object_id
1204
+ assert_equal another_firm.object_id, citibank.firm.object_id
1205
+ end
1206
+
1207
+ def test_creating_the_belonging_object
1208
+ citibank = Account.create("credit_limit" => 10)
1209
+ apple = citibank.create_firm("name" => "Apple")
1210
+ assert_equal apple, citibank.firm
1211
+ citibank.save
1212
+ citibank.reload
1213
+ assert_equal apple, citibank.firm
1214
+ end
1215
+
1216
+ def test_building_the_belonging_object
1217
+ citibank = Account.create("credit_limit" => 10)
1218
+ apple = citibank.build_firm("name" => "Apple")
1219
+ citibank.save
1220
+ assert_equal apple.id, citibank.firm_id
1221
+ end
1222
+
1223
+ def test_natural_assignment_to_nil
1224
+ client = Client.find(3)
1225
+ client.firm = nil
1226
+ client.save
1227
+ assert_nil client.firm(true)
1228
+ assert_nil client.client_of
1229
+ end
1230
+
1231
+ def test_with_different_class_name
1232
+ assert_equal Company.find(1).name, Company.find(3).firm_with_other_name.name
1233
+ assert_not_nil Company.find(3).firm_with_other_name, "Microsoft should have a firm"
1234
+ end
1235
+
1236
+ def test_with_condition
1237
+ assert_equal Company.find(1).name, Company.find(3).firm_with_condition.name
1238
+ assert_not_nil Company.find(3).firm_with_condition, "Microsoft should have a firm"
1239
+ end
1240
+
1241
+ def test_belongs_to_counter
1242
+ debate = Topic.create("title" => "debate")
1243
+ assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet"
1244
+
1245
+ trash = debate.replies.create("title" => "blah!", "content" => "world around!")
1246
+ assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply created"
1247
+
1248
+ trash.destroy
1249
+ assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted"
1250
+ end
1251
+
1252
+ def test_belongs_to_counter_with_reassigning
1253
+ t1 = Topic.create("title" => "t1")
1254
+ t2 = Topic.create("title" => "t2")
1255
+ r1 = Reply.new("title" => "r1", "content" => "r1")
1256
+ r1.topic = t1
1257
+
1258
+ assert r1.save
1259
+ assert_equal 1, Topic.find(t1.id).replies.size
1260
+ assert_equal 0, Topic.find(t2.id).replies.size
1261
+
1262
+ r1.topic = Topic.find(t2.id)
1263
+
1264
+ assert r1.save
1265
+ assert_equal 0, Topic.find(t1.id).replies.size
1266
+ assert_equal 1, Topic.find(t2.id).replies.size
1267
+
1268
+ r1.topic = nil
1269
+
1270
+ assert_equal 0, Topic.find(t1.id).replies.size
1271
+ assert_equal 0, Topic.find(t2.id).replies.size
1272
+
1273
+ r1.topic = t1
1274
+
1275
+ assert_equal 1, Topic.find(t1.id).replies.size
1276
+ assert_equal 0, Topic.find(t2.id).replies.size
1277
+
1278
+ r1.destroy
1279
+
1280
+ assert_equal 0, Topic.find(t1.id).replies.size
1281
+ assert_equal 0, Topic.find(t2.id).replies.size
1282
+ end
1283
+
1284
+ def test_belongs_to_counter_after_save
1285
+ topic = Topic.create!(:title => "monday night")
1286
+ topic.replies.create!(:title => "re: monday night", :content => "football")
1287
+ assert_equal 1, Topic.find(topic.id)[:replies_count]
1288
+
1289
+ topic.save!
1290
+ assert_equal 1, Topic.find(topic.id)[:replies_count]
1291
+ end
1292
+
1293
+ def test_belongs_to_counter_after_update_attributes
1294
+ topic = Topic.create!(:title => "37s")
1295
+ topic.replies.create!(:title => "re: 37s", :content => "rails")
1296
+ assert_equal 1, Topic.find(topic.id)[:replies_count]
1297
+
1298
+ topic.update_attributes(:title => "37signals")
1299
+ assert_equal 1, Topic.find(topic.id)[:replies_count]
1300
+ end
1301
+
1302
+ def test_belongs_to_counter_after_save
1303
+ topic = Topic.create("title" => "monday night")
1304
+ topic.replies.create("title" => "re: monday night", "content" => "football")
1305
+ assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
1306
+
1307
+ topic.save
1308
+ assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
1309
+ end
1310
+
1311
+ def test_belongs_to_counter_after_update_attributes
1312
+ topic = Topic.create("title" => "37s")
1313
+ topic.replies.create("title" => "re: 37s", "content" => "rails")
1314
+ assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
1315
+
1316
+ topic.update_attributes("title" => "37signals")
1317
+ assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
1318
+ end
1319
+
1320
+ def test_assignment_before_parent_saved
1321
+ client = Client.find(:first)
1322
+ apple = Firm.new("name" => "Apple")
1323
+ client.firm = apple
1324
+ assert_equal apple, client.firm
1325
+ assert apple.new_record?
1326
+ assert client.save
1327
+ assert apple.save
1328
+ assert !apple.new_record?
1329
+ assert_equal apple, client.firm
1330
+ assert_equal apple, client.firm(true)
1331
+ end
1332
+
1333
+ def test_assignment_before_child_saved
1334
+ final_cut = Client.new("name" => "Final Cut")
1335
+ firm = Firm.find(1)
1336
+ final_cut.firm = firm
1337
+ assert final_cut.new_record?
1338
+ assert final_cut.save
1339
+ assert !final_cut.new_record?
1340
+ assert !firm.new_record?
1341
+ assert_equal firm, final_cut.firm
1342
+ assert_equal firm, final_cut.firm(true)
1343
+ end
1344
+
1345
+ def test_assignment_before_either_saved
1346
+ final_cut = Client.new("name" => "Final Cut")
1347
+ apple = Firm.new("name" => "Apple")
1348
+ final_cut.firm = apple
1349
+ assert final_cut.new_record?
1350
+ assert apple.new_record?
1351
+ assert final_cut.save
1352
+ assert !final_cut.new_record?
1353
+ assert !apple.new_record?
1354
+ assert_equal apple, final_cut.firm
1355
+ assert_equal apple, final_cut.firm(true)
1356
+ end
1357
+
1358
+ def test_new_record_with_foreign_key_but_no_object
1359
+ c = Client.new("firm_id" => 1)
1360
+ assert_equal Firm.find(:first), c.firm_with_basic_id
1361
+ end
1362
+
1363
+ def test_forgetting_the_load_when_foreign_key_enters_late
1364
+ c = Client.new
1365
+ assert_nil c.firm_with_basic_id
1366
+
1367
+ c.firm_id = 1
1368
+ assert_equal Firm.find(:first), c.firm_with_basic_id
1369
+ end
1370
+
1371
+ def test_field_name_same_as_foreign_key
1372
+ computer = Computer.find(1)
1373
+ assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # '
1374
+ end
1375
+
1376
+ def test_counter_cache
1377
+ topic = Topic.create :title => "Zoom-zoom-zoom"
1378
+ assert_equal 0, topic[:replies_count]
1379
+
1380
+ reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
1381
+ reply.topic = topic
1382
+
1383
+ assert_equal 1, topic.reload[:replies_count]
1384
+ assert_equal 1, topic.replies.size
1385
+
1386
+ topic[:replies_count] = 15
1387
+ assert_equal 15, topic.replies.size
1388
+ end
1389
+
1390
+ def test_custom_counter_cache
1391
+ reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
1392
+ assert_equal 0, reply[:replies_count]
1393
+
1394
+ silly = SillyReply.create(:title => "gaga", :content => "boo-boo")
1395
+ silly.reply = reply
1396
+
1397
+ assert_equal 1, reply.reload[:replies_count]
1398
+ assert_equal 1, reply.replies.size
1399
+
1400
+ reply[:replies_count] = 17
1401
+ assert_equal 17, reply.replies.size
1402
+ end
1403
+
1404
+ def test_store_two_association_with_one_save
1405
+ num_orders = Order.count
1406
+ num_customers = Customer.count
1407
+ order = Order.new
1408
+
1409
+ customer1 = order.billing = Customer.new
1410
+ customer2 = order.shipping = Customer.new
1411
+ assert order.save
1412
+ assert_equal customer1, order.billing
1413
+ assert_equal customer2, order.shipping
1414
+
1415
+ order.reload
1416
+
1417
+ assert_equal customer1, order.billing
1418
+ assert_equal customer2, order.shipping
1419
+
1420
+ assert_equal num_orders +1, Order.count
1421
+ assert_equal num_customers +2, Customer.count
1422
+ end
1423
+
1424
+
1425
+ def test_store_association_in_two_relations_with_one_save
1426
+ num_orders = Order.count
1427
+ num_customers = Customer.count
1428
+ order = Order.new
1429
+
1430
+ customer = order.billing = order.shipping = Customer.new
1431
+ assert order.save
1432
+ assert_equal customer, order.billing
1433
+ assert_equal customer, order.shipping
1434
+
1435
+ order.reload
1436
+
1437
+ assert_equal customer, order.billing
1438
+ assert_equal customer, order.shipping
1439
+
1440
+ assert_equal num_orders +1, Order.count
1441
+ assert_equal num_customers +1, Customer.count
1442
+ end
1443
+
1444
+ def test_store_association_in_two_relations_with_one_save_in_existing_object
1445
+ num_orders = Order.count
1446
+ num_customers = Customer.count
1447
+ order = Order.create
1448
+
1449
+ customer = order.billing = order.shipping = Customer.new
1450
+ assert order.save
1451
+ assert_equal customer, order.billing
1452
+ assert_equal customer, order.shipping
1453
+
1454
+ order.reload
1455
+
1456
+ assert_equal customer, order.billing
1457
+ assert_equal customer, order.shipping
1458
+
1459
+ assert_equal num_orders +1, Order.count
1460
+ assert_equal num_customers +1, Customer.count
1461
+ end
1462
+
1463
+ def test_store_association_in_two_relations_with_one_save_in_existing_object_with_values
1464
+ num_orders = Order.count
1465
+ num_customers = Customer.count
1466
+ order = Order.create
1467
+
1468
+ customer = order.billing = order.shipping = Customer.new
1469
+ assert order.save
1470
+ assert_equal customer, order.billing
1471
+ assert_equal customer, order.shipping
1472
+
1473
+ order.reload
1474
+
1475
+ customer = order.billing = order.shipping = Customer.new
1476
+
1477
+ assert order.save
1478
+ order.reload
1479
+
1480
+ assert_equal customer, order.billing
1481
+ assert_equal customer, order.shipping
1482
+
1483
+ assert_equal num_orders +1, Order.count
1484
+ assert_equal num_customers +2, Customer.count
1485
+ end
1486
+
1487
+
1488
+ def test_association_assignment_sticks
1489
+ post = Post.find(:first)
1490
+
1491
+ author1, author2 = Author.find(:all, :limit => 2)
1492
+ assert_not_nil author1
1493
+ assert_not_nil author2
1494
+
1495
+ # make sure the association is loaded
1496
+ post.author
1497
+
1498
+ # set the association by id, directly
1499
+ post.author_id = author2.id
1500
+
1501
+ # save and reload
1502
+ post.save!
1503
+ post.reload
1504
+
1505
+ # the author id of the post should be the id we set
1506
+ assert_equal post.author_id, author2.id
1507
+ end
1508
+
1509
+ end
1510
+
1511
+
1512
+ class ProjectWithAfterCreateHook < ActiveRecord::Base
1513
+ set_table_name 'projects'
1514
+ has_and_belongs_to_many :developers,
1515
+ :class_name => "DeveloperForProjectWithAfterCreateHook",
1516
+ :join_table => "developers_projects",
1517
+ :foreign_key => "project_id",
1518
+ :association_foreign_key => "developer_id"
1519
+
1520
+ after_create :add_david
1521
+
1522
+ def add_david
1523
+ david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
1524
+ david.projects << self
1525
+ end
1526
+ end
1527
+
1528
+ class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
1529
+ set_table_name 'developers'
1530
+ has_and_belongs_to_many :projects,
1531
+ :class_name => "ProjectWithAfterCreateHook",
1532
+ :join_table => "developers_projects",
1533
+ :association_foreign_key => "project_id",
1534
+ :foreign_key => "developer_id"
1535
+ end
1536
+
1537
+
1538
+ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1539
+ fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects
1540
+
1541
+ def test_has_and_belongs_to_many
1542
+ david = Developer.find(1)
1543
+
1544
+ assert !david.projects.empty?
1545
+ assert_equal 2, david.projects.size
1546
+
1547
+ active_record = Project.find(1)
1548
+ assert !active_record.developers.empty?
1549
+ assert_equal 3, active_record.developers.size
1550
+ assert active_record.developers.include?(david)
1551
+ end
1552
+
1553
+ def test_triple_equality
1554
+ assert !(Array === Developer.find(1).projects)
1555
+ assert Developer.find(1).projects === Array
1556
+ end
1557
+
1558
+ def test_adding_single
392
1559
  jamis = Developer.find(2)
393
- jamis.projects.id # causing the collection to load
1560
+ jamis.projects.reload # causing the collection to load
394
1561
  action_controller = Project.find(2)
395
1562
  assert_equal 1, jamis.projects.size
396
- assert_equal 1, action_controller.developers.size
397
-
1563
+ assert_equal 1, action_controller.developers.size
1564
+
398
1565
  jamis.projects << action_controller
399
1566
 
400
1567
  assert_equal 2, jamis.projects.size
@@ -404,74 +1571,570 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
404
1571
 
405
1572
  def test_adding_type_mismatch
406
1573
  jamis = Developer.find(2)
407
- assert_raises(ActiveRecord::AssociationTypeMismatch) { jamis.projects << nil }
408
- assert_raises(ActiveRecord::AssociationTypeMismatch) { jamis.projects << 1 }
1574
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { jamis.projects << nil }
1575
+ assert_raise(ActiveRecord::AssociationTypeMismatch) { jamis.projects << 1 }
409
1576
  end
410
1577
 
411
1578
  def test_adding_from_the_project
412
1579
  jamis = Developer.find(2)
413
1580
  action_controller = Project.find(2)
414
- action_controller.developers.id
1581
+ action_controller.developers.reload
1582
+ assert_equal 1, jamis.projects.size
1583
+ assert_equal 1, action_controller.developers.size
1584
+
1585
+ action_controller.developers << jamis
1586
+
1587
+ assert_equal 2, jamis.projects(true).size
1588
+ assert_equal 2, action_controller.developers.size
1589
+ assert_equal 2, action_controller.developers(true).size
1590
+ end
1591
+
1592
+ def test_adding_from_the_project_fixed_timestamp
1593
+ jamis = Developer.find(2)
1594
+ action_controller = Project.find(2)
1595
+ action_controller.developers.reload
415
1596
  assert_equal 1, jamis.projects.size
416
1597
  assert_equal 1, action_controller.developers.size
1598
+ updated_at = jamis.updated_at
417
1599
 
418
- action_controller.developers << jamis
419
-
1600
+ action_controller.developers << jamis
1601
+
1602
+ assert_equal updated_at, jamis.updated_at
420
1603
  assert_equal 2, jamis.projects(true).size
421
1604
  assert_equal 2, action_controller.developers.size
422
1605
  assert_equal 2, action_controller.developers(true).size
423
1606
  end
424
1607
 
1608
+ def test_adding_multiple
1609
+ aredridel = Developer.new("name" => "Aredridel")
1610
+ aredridel.save
1611
+ aredridel.projects.reload
1612
+ aredridel.projects.push(Project.find(1), Project.find(2))
1613
+ assert_equal 2, aredridel.projects.size
1614
+ assert_equal 2, aredridel.projects(true).size
1615
+ end
1616
+
425
1617
  def test_adding_a_collection
426
- aridridel = Developer.new("name" => "Aridridel")
427
- aridridel.save
428
- aridridel.projects.id
429
- aridridel.projects.concat([ Project.find(1), Project.find(2) ])
430
- assert_equal 2, aridridel.projects.size
431
- assert_equal 2, aridridel.projects(true).size
1618
+ aredridel = Developer.new("name" => "Aredridel")
1619
+ aredridel.save
1620
+ aredridel.projects.reload
1621
+ aredridel.projects.concat([Project.find(1), Project.find(2)])
1622
+ assert_equal 2, aredridel.projects.size
1623
+ assert_equal 2, aredridel.projects(true).size
432
1624
  end
433
-
1625
+
1626
+ def test_adding_uses_default_values_on_join_table
1627
+ ac = projects(:action_controller)
1628
+ assert !developers(:jamis).projects.include?(ac)
1629
+ developers(:jamis).projects << ac
1630
+
1631
+ assert developers(:jamis, :reload).projects.include?(ac)
1632
+ project = developers(:jamis).projects.detect { |p| p == ac }
1633
+ assert_equal 1, project.access_level.to_i
1634
+ end
1635
+
1636
+ def test_habtm_attribute_access_and_respond_to
1637
+ project = developers(:jamis).projects[0]
1638
+ assert project.has_attribute?("name")
1639
+ assert project.has_attribute?("joined_on")
1640
+ assert project.has_attribute?("access_level")
1641
+ assert project.respond_to?("name")
1642
+ assert project.respond_to?("name=")
1643
+ assert project.respond_to?("name?")
1644
+ assert project.respond_to?("joined_on")
1645
+ # given that the 'join attribute' won't be persisted, I don't
1646
+ # think we should define the mutators
1647
+ #assert project.respond_to?("joined_on=")
1648
+ assert project.respond_to?("joined_on?")
1649
+ assert project.respond_to?("access_level")
1650
+ #assert project.respond_to?("access_level=")
1651
+ assert project.respond_to?("access_level?")
1652
+ end
1653
+
1654
+ def test_habtm_adding_before_save
1655
+ no_of_devels = Developer.count
1656
+ no_of_projects = Project.count
1657
+ aredridel = Developer.new("name" => "Aredridel")
1658
+ aredridel.projects.concat([Project.find(1), p = Project.new("name" => "Projekt")])
1659
+ assert aredridel.new_record?
1660
+ assert p.new_record?
1661
+ assert aredridel.save
1662
+ assert !aredridel.new_record?
1663
+ assert_equal no_of_devels+1, Developer.count
1664
+ assert_equal no_of_projects+1, Project.count
1665
+ assert_equal 2, aredridel.projects.size
1666
+ assert_equal 2, aredridel.projects(true).size
1667
+ end
1668
+
1669
+ def test_habtm_saving_multiple_relationships
1670
+ new_project = Project.new("name" => "Grimetime")
1671
+ amount_of_developers = 4
1672
+ developers = (0...amount_of_developers).collect {|i| Developer.create(:name => "JME #{i}") }.reverse
1673
+
1674
+ new_project.developer_ids = [developers[0].id, developers[1].id]
1675
+ new_project.developers_with_callback_ids = [developers[2].id, developers[3].id]
1676
+ assert new_project.save
1677
+
1678
+ new_project.reload
1679
+ assert_equal amount_of_developers, new_project.developers.size
1680
+ assert_equal developers, new_project.developers
1681
+ end
1682
+
1683
+ def test_habtm_unique_order_preserved
1684
+ assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).non_unique_developers
1685
+ assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).developers
1686
+ end
1687
+
1688
+ def test_build
1689
+ devel = Developer.find(1)
1690
+ proj = devel.projects.build("name" => "Projekt")
1691
+ assert_equal devel.projects.last, proj
1692
+ assert proj.new_record?
1693
+ devel.save
1694
+ assert !proj.new_record?
1695
+ assert_equal devel.projects.last, proj
1696
+ assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
1697
+ end
1698
+
1699
+ def test_build_by_new_record
1700
+ devel = Developer.new(:name => "Marcel", :salary => 75000)
1701
+ proj1 = devel.projects.build(:name => "Make bed")
1702
+ proj2 = devel.projects.build(:name => "Lie in it")
1703
+ assert_equal devel.projects.last, proj2
1704
+ assert proj2.new_record?
1705
+ devel.save
1706
+ assert !devel.new_record?
1707
+ assert !proj2.new_record?
1708
+ assert_equal devel.projects.last, proj2
1709
+ assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
1710
+ end
1711
+
1712
+ def test_create
1713
+ devel = Developer.find(1)
1714
+ proj = devel.projects.create("name" => "Projekt")
1715
+ assert_equal devel.projects.last, proj
1716
+ assert !proj.new_record?
1717
+ assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
1718
+ end
1719
+
1720
+ def test_create_by_new_record
1721
+ devel = Developer.new(:name => "Marcel", :salary => 75000)
1722
+ proj1 = devel.projects.build(:name => "Make bed")
1723
+ proj2 = devel.projects.build(:name => "Lie in it")
1724
+ assert_equal devel.projects.last, proj2
1725
+ assert proj2.new_record?
1726
+ devel.save
1727
+ assert !devel.new_record?
1728
+ assert !proj2.new_record?
1729
+ assert_equal devel.projects.last, proj2
1730
+ assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
1731
+ end
1732
+
1733
+ def test_uniq_after_the_fact
1734
+ developers(:jamis).projects << projects(:active_record)
1735
+ developers(:jamis).projects << projects(:active_record)
1736
+ assert_equal 3, developers(:jamis).projects.size
1737
+ assert_equal 1, developers(:jamis).projects.uniq.size
1738
+ end
1739
+
1740
+ def test_uniq_before_the_fact
1741
+ projects(:active_record).developers << developers(:jamis)
1742
+ projects(:active_record).developers << developers(:david)
1743
+ assert_equal 3, projects(:active_record, :reload).developers.size
1744
+ end
1745
+
434
1746
  def test_deleting
435
1747
  david = Developer.find(1)
436
1748
  active_record = Project.find(1)
437
- david.projects.id
1749
+ david.projects.reload
438
1750
  assert_equal 2, david.projects.size
439
- assert_equal 2, active_record.developers.size
1751
+ assert_equal 3, active_record.developers.size
440
1752
 
441
1753
  david.projects.delete(active_record)
442
-
1754
+
443
1755
  assert_equal 1, david.projects.size
444
1756
  assert_equal 1, david.projects(true).size
445
- assert_equal 1, active_record.developers(true).size
1757
+ assert_equal 2, active_record.developers(true).size
446
1758
  end
447
1759
 
448
- def test_deleting_a_collection
1760
+ def test_deleting_array
449
1761
  david = Developer.find(1)
450
- david.projects.id
451
- david.projects.delete(Project.find_all)
1762
+ david.projects.reload
1763
+ david.projects.delete(Project.find(:all))
452
1764
  assert_equal 0, david.projects.size
453
1765
  assert_equal 0, david.projects(true).size
454
1766
  end
455
-
456
- def test_deleting_a_association_collection
1767
+
1768
+ def test_deleting_with_sql
1769
+ david = Developer.find(1)
1770
+ active_record = Project.find(1)
1771
+ active_record.developers.reload
1772
+ assert_equal 3, active_record.developers_by_sql.size
1773
+
1774
+ active_record.developers_by_sql.delete(david)
1775
+ assert_equal 2, active_record.developers_by_sql(true).size
1776
+ end
1777
+
1778
+ def test_deleting_array_with_sql
1779
+ active_record = Project.find(1)
1780
+ active_record.developers.reload
1781
+ assert_equal 3, active_record.developers_by_sql.size
1782
+
1783
+ active_record.developers_by_sql.delete(Developer.find(:all))
1784
+ assert_equal 0, active_record.developers_by_sql(true).size
1785
+ end
1786
+
1787
+ def test_deleting_all
457
1788
  david = Developer.find(1)
458
- david.projects.id
459
- david.projects.delete(david.projects)
1789
+ david.projects.reload
1790
+ david.projects.clear
460
1791
  assert_equal 0, david.projects.size
461
1792
  assert_equal 0, david.projects(true).size
462
1793
  end
463
1794
 
464
1795
  def test_removing_associations_on_destroy
465
- Developer.find(1).destroy
466
- assert Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = '1'").empty?
1796
+ david = DeveloperWithBeforeDestroyRaise.find(1)
1797
+ assert !david.projects.empty?
1798
+ assert_nothing_raised { david.destroy }
1799
+ assert david.projects.empty?
1800
+ assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
467
1801
  end
468
-
1802
+
1803
+ def test_additional_columns_from_join_table
1804
+ assert_date_from_db Date.new(2004, 10, 10), Developer.find(1).projects.first.joined_on.to_date
1805
+ end
1806
+
469
1807
  def test_destroy_all
470
1808
  david = Developer.find(1)
471
- david.projects.id
1809
+ david.projects.reload
472
1810
  assert !david.projects.empty?
473
1811
  david.projects.destroy_all
474
1812
  assert david.projects.empty?
475
1813
  assert david.projects(true).empty?
476
1814
  end
477
- end
1815
+
1816
+ def test_deprecated_push_with_attributes_was_removed
1817
+ jamis = developers(:jamis)
1818
+ assert_raise(NoMethodError) do
1819
+ jamis.projects.push_with_attributes(projects(:action_controller), :joined_on => Date.today)
1820
+ end
1821
+ end
1822
+
1823
+ def test_associations_with_conditions
1824
+ assert_equal 3, projects(:active_record).developers.size
1825
+ assert_equal 1, projects(:active_record).developers_named_david.size
1826
+ assert_equal 1, projects(:active_record).developers_named_david_with_hash_conditions.size
1827
+
1828
+ assert_equal developers(:david), projects(:active_record).developers_named_david.find(developers(:david).id)
1829
+ assert_equal developers(:david), projects(:active_record).developers_named_david_with_hash_conditions.find(developers(:david).id)
1830
+ assert_equal developers(:david), projects(:active_record).salaried_developers.find(developers(:david).id)
1831
+
1832
+ projects(:active_record).developers_named_david.clear
1833
+ assert_equal 2, projects(:active_record, :reload).developers.size
1834
+ end
1835
+
1836
+ def test_find_in_association
1837
+ # Using sql
1838
+ assert_equal developers(:david), projects(:active_record).developers.find(developers(:david).id), "SQL find"
1839
+
1840
+ # Using ruby
1841
+ active_record = projects(:active_record)
1842
+ active_record.developers.reload
1843
+ assert_equal developers(:david), active_record.developers.find(developers(:david).id), "Ruby find"
1844
+ end
1845
+
1846
+ def test_find_in_association_with_custom_finder_sql
1847
+ assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id), "SQL find"
1848
+
1849
+ active_record = projects(:active_record)
1850
+ active_record.developers_with_finder_sql.reload
1851
+ assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
1852
+ end
1853
+
1854
+ def test_find_in_association_with_custom_finder_sql_and_string_id
1855
+ assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
1856
+ end
1857
+
1858
+ def test_find_with_merged_options
1859
+ assert_equal 1, projects(:active_record).limited_developers.size
1860
+ assert_equal 1, projects(:active_record).limited_developers.find(:all).size
1861
+ assert_equal 3, projects(:active_record).limited_developers.find(:all, :limit => nil).size
1862
+ end
1863
+
1864
+ def test_dynamic_find_should_respect_association_order
1865
+ # Developers are ordered 'name DESC, id DESC'
1866
+ low_id_jamis = developers(:jamis)
1867
+ middle_id_jamis = developers(:poor_jamis)
1868
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
1869
+
1870
+ assert_equal high_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'")
1871
+ assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis')
1872
+ end
1873
+
1874
+ def test_dynamic_find_order_should_override_association_order
1875
+ # Developers are ordered 'name DESC, id DESC'
1876
+ low_id_jamis = developers(:jamis)
1877
+ middle_id_jamis = developers(:poor_jamis)
1878
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
1879
+
1880
+ assert_equal low_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'", :order => 'id')
1881
+ assert_equal low_id_jamis, projects(:active_record).developers.find_by_name('Jamis', :order => 'id')
1882
+ end
1883
+
1884
+ def test_dynamic_find_all_should_respect_association_order
1885
+ # Developers are ordered 'name DESC, id DESC'
1886
+ low_id_jamis = developers(:jamis)
1887
+ middle_id_jamis = developers(:poor_jamis)
1888
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
1889
+
1890
+ assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'")
1891
+ assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis')
1892
+ end
1893
+
1894
+ def test_dynamic_find_all_order_should_override_association_order
1895
+ # Developers are ordered 'name DESC, id DESC'
1896
+ low_id_jamis = developers(:jamis)
1897
+ middle_id_jamis = developers(:poor_jamis)
1898
+ high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
1899
+
1900
+ assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'", :order => 'id')
1901
+ assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis', :order => 'id')
1902
+ end
1903
+
1904
+ def test_dynamic_find_all_should_respect_association_limit
1905
+ assert_equal 1, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'").length
1906
+ assert_equal 1, projects(:active_record).limited_developers.find_all_by_name('Jamis').length
1907
+ end
1908
+
1909
+ def test_dynamic_find_all_order_should_override_association_limit
1910
+ assert_equal 2, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'", :limit => 9_000).length
1911
+ assert_equal 2, projects(:active_record).limited_developers.find_all_by_name('Jamis', :limit => 9_000).length
1912
+ end
1913
+
1914
+ def test_new_with_values_in_collection
1915
+ jamis = DeveloperForProjectWithAfterCreateHook.find_by_name('Jamis')
1916
+ david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
1917
+ project = ProjectWithAfterCreateHook.new(:name => "Cooking with Bertie")
1918
+ project.developers << jamis
1919
+ project.save!
1920
+ project.reload
1921
+
1922
+ assert project.developers.include?(jamis)
1923
+ assert project.developers.include?(david)
1924
+ end
1925
+
1926
+ def test_find_in_association_with_options
1927
+ developers = projects(:active_record).developers.find(:all)
1928
+ assert_equal 3, developers.size
1929
+
1930
+ assert_equal developers(:poor_jamis), projects(:active_record).developers.find(:first, :conditions => "salary < 10000")
1931
+ assert_equal developers(:jamis), projects(:active_record).developers.find(:first, :order => "salary DESC")
1932
+ end
1933
+
1934
+ def test_replace_with_less
1935
+ david = developers(:david)
1936
+ david.projects = [projects(:action_controller)]
1937
+ assert david.save
1938
+ assert_equal 1, david.projects.length
1939
+ end
1940
+
1941
+ def test_replace_with_new
1942
+ david = developers(:david)
1943
+ david.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
1944
+ david.save
1945
+ assert_equal 2, david.projects.length
1946
+ assert !david.projects.include?(projects(:active_record))
1947
+ end
1948
+
1949
+ def test_replace_on_new_object
1950
+ new_developer = Developer.new("name" => "Matz")
1951
+ new_developer.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
1952
+ new_developer.save
1953
+ assert_equal 2, new_developer.projects.length
1954
+ end
1955
+
1956
+ def test_consider_type
1957
+ developer = Developer.find(:first)
1958
+ special_project = SpecialProject.create("name" => "Special Project")
1959
+
1960
+ other_project = developer.projects.first
1961
+ developer.special_projects << special_project
1962
+ developer.reload
1963
+
1964
+ assert developer.projects.include?(special_project)
1965
+ assert developer.special_projects.include?(special_project)
1966
+ assert !developer.special_projects.include?(other_project)
1967
+ end
1968
+
1969
+ def test_update_attributes_after_push_without_duplicate_join_table_rows
1970
+ developer = Developer.new("name" => "Kano")
1971
+ project = SpecialProject.create("name" => "Special Project")
1972
+ assert developer.save
1973
+ developer.projects << project
1974
+ developer.update_attribute("name", "Bruza")
1975
+ assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i
1976
+ SELECT count(*) FROM developers_projects
1977
+ WHERE project_id = #{project.id}
1978
+ AND developer_id = #{developer.id}
1979
+ end_sql
1980
+ end
1981
+
1982
+ def test_updating_attributes_on_non_rich_associations
1983
+ welcome = categories(:technology).posts.first
1984
+ welcome.title = "Something else"
1985
+ assert welcome.save!
1986
+ end
1987
+
1988
+ def test_habtm_respects_select
1989
+ categories(:technology).select_testing_posts(true).each do |o|
1990
+ assert_respond_to o, :correctness_marker
1991
+ end
1992
+ assert_respond_to categories(:technology).select_testing_posts.find(:first), :correctness_marker
1993
+ end
1994
+
1995
+ def test_updating_attributes_on_rich_associations
1996
+ david = projects(:action_controller).developers.first
1997
+ david.name = "DHH"
1998
+ assert_raises(ActiveRecord::ReadOnlyRecord) { david.save! }
1999
+ end
2000
+
2001
+ def test_updating_attributes_on_rich_associations_with_limited_find_from_reflection
2002
+ david = projects(:action_controller).selected_developers.first
2003
+ david.name = "DHH"
2004
+ assert_nothing_raised { david.save! }
2005
+ end
2006
+
2007
+
2008
+ def test_updating_attributes_on_rich_associations_with_limited_find
2009
+ david = projects(:action_controller).developers.find(:all, :select => "developers.*").first
2010
+ david.name = "DHH"
2011
+ assert david.save!
2012
+ end
2013
+
2014
+ def test_join_table_alias
2015
+ assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL').size
2016
+ end
2017
+
2018
+ def test_join_with_group
2019
+ group = Developer.columns.inject([]) do |g, c|
2020
+ g << "developers.#{c.name}"
2021
+ g << "developers_projects_2.#{c.name}"
2022
+ end
2023
+ Project.columns.each { |c| group << "projects.#{c.name}" }
2024
+
2025
+ assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL', :group => group.join(",")).size
2026
+ end
2027
+
2028
+ def test_get_ids
2029
+ assert_equal projects(:active_record, :action_controller).map(&:id), developers(:david).project_ids
2030
+ assert_equal [projects(:active_record).id], developers(:jamis).project_ids
2031
+ end
2032
+
2033
+ def test_assign_ids
2034
+ developer = Developer.new("name" => "Joe")
2035
+ developer.project_ids = projects(:active_record, :action_controller).map(&:id)
2036
+ developer.save
2037
+ developer.reload
2038
+ assert_equal 2, developer.projects.length
2039
+ assert_equal projects(:active_record), developer.projects[0]
2040
+ assert_equal projects(:action_controller), developer.projects[1]
2041
+ end
2042
+
2043
+ def test_assign_ids_ignoring_blanks
2044
+ developer = Developer.new("name" => "Joe")
2045
+ developer.project_ids = [projects(:active_record).id, nil, projects(:action_controller).id, '']
2046
+ developer.save
2047
+ developer.reload
2048
+ assert_equal 2, developer.projects.length
2049
+ assert_equal projects(:active_record), developer.projects[0]
2050
+ assert_equal projects(:action_controller), developer.projects[1]
2051
+ end
2052
+
2053
+ def test_select_limited_ids_list
2054
+ # Set timestamps
2055
+ Developer.transaction do
2056
+ Developer.find(:all, :order => 'id').each_with_index do |record, i|
2057
+ record.update_attributes(:created_at => 5.years.ago + (i * 5.minutes))
2058
+ end
2059
+ end
2060
+
2061
+ join_base = ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase.new(Project)
2062
+ join_dep = ActiveRecord::Associations::ClassMethods::JoinDependency.new(join_base, :developers, nil)
2063
+ projects = Project.send(:select_limited_ids_list, {:order => 'developers.created_at'}, join_dep)
2064
+ assert !projects.include?("'"), projects
2065
+ assert_equal %w(1 2), projects.scan(/\d/).sort
2066
+ end
2067
+
2068
+ def test_scoped_find_on_through_association_doesnt_return_read_only_records
2069
+ tag = Post.find(1).tags.find_by_name("General")
2070
+
2071
+ assert_nothing_raised do
2072
+ tag.save!
2073
+ end
2074
+ end
2075
+ end
2076
+
2077
+
2078
+ class OverridingAssociationsTest < Test::Unit::TestCase
2079
+ class Person < ActiveRecord::Base; end
2080
+ class DifferentPerson < ActiveRecord::Base; end
2081
+
2082
+ class PeopleList < ActiveRecord::Base
2083
+ has_and_belongs_to_many :has_and_belongs_to_many, :before_add => :enlist
2084
+ has_many :has_many, :before_add => :enlist
2085
+ belongs_to :belongs_to
2086
+ has_one :has_one
2087
+ end
2088
+
2089
+ class DifferentPeopleList < PeopleList
2090
+ # Different association with the same name, callbacks should be omitted here.
2091
+ has_and_belongs_to_many :has_and_belongs_to_many, :class_name => 'DifferentPerson'
2092
+ has_many :has_many, :class_name => 'DifferentPerson'
2093
+ belongs_to :belongs_to, :class_name => 'DifferentPerson'
2094
+ has_one :has_one, :class_name => 'DifferentPerson'
2095
+ end
2096
+
2097
+ def test_habtm_association_redefinition_callbacks_should_differ_and_not_inherited
2098
+ # redeclared association on AR descendant should not inherit callbacks from superclass
2099
+ callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
2100
+ assert_equal([:enlist], callbacks)
2101
+ callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
2102
+ assert_equal([], callbacks)
2103
+ end
2104
+
2105
+ def test_has_many_association_redefinition_callbacks_should_differ_and_not_inherited
2106
+ # redeclared association on AR descendant should not inherit callbacks from superclass
2107
+ callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_many)
2108
+ assert_equal([:enlist], callbacks)
2109
+ callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_many)
2110
+ assert_equal([], callbacks)
2111
+ end
2112
+
2113
+ def test_habtm_association_redefinition_reflections_should_differ_and_not_inherited
2114
+ assert_not_equal(
2115
+ PeopleList.reflect_on_association(:has_and_belongs_to_many),
2116
+ DifferentPeopleList.reflect_on_association(:has_and_belongs_to_many)
2117
+ )
2118
+ end
2119
+
2120
+ def test_has_many_association_redefinition_reflections_should_differ_and_not_inherited
2121
+ assert_not_equal(
2122
+ PeopleList.reflect_on_association(:has_many),
2123
+ DifferentPeopleList.reflect_on_association(:has_many)
2124
+ )
2125
+ end
2126
+
2127
+ def test_belongs_to_association_redefinition_reflections_should_differ_and_not_inherited
2128
+ assert_not_equal(
2129
+ PeopleList.reflect_on_association(:belongs_to),
2130
+ DifferentPeopleList.reflect_on_association(:belongs_to)
2131
+ )
2132
+ end
2133
+
2134
+ def test_has_one_association_redefinition_reflections_should_differ_and_not_inherited
2135
+ assert_not_equal(
2136
+ PeopleList.reflect_on_association(:has_one),
2137
+ DifferentPeopleList.reflect_on_association(:has_one)
2138
+ )
2139
+ end
2140
+ end