activerecord 1.13.2 → 1.14.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 (144) hide show
  1. data/CHANGELOG +452 -10
  2. data/RUNNING_UNIT_TESTS +1 -1
  3. data/lib/active_record.rb +5 -2
  4. data/lib/active_record/acts/list.rb +1 -1
  5. data/lib/active_record/acts/tree.rb +29 -25
  6. data/lib/active_record/aggregations.rb +3 -2
  7. data/lib/active_record/associations.rb +783 -337
  8. data/lib/active_record/associations/association_collection.rb +7 -12
  9. data/lib/active_record/associations/association_proxy.rb +62 -24
  10. data/lib/active_record/associations/belongs_to_association.rb +27 -46
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  12. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +38 -38
  13. data/lib/active_record/associations/has_many_association.rb +61 -56
  14. data/lib/active_record/associations/has_many_through_association.rb +144 -0
  15. data/lib/active_record/associations/has_one_association.rb +22 -16
  16. data/lib/active_record/base.rb +482 -182
  17. data/lib/active_record/calculations.rb +225 -0
  18. data/lib/active_record/callbacks.rb +7 -7
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +162 -47
  20. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  21. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -1
  22. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +21 -1
  23. data/lib/active_record/connection_adapters/abstract_adapter.rb +34 -2
  24. data/lib/active_record/connection_adapters/db2_adapter.rb +107 -61
  25. data/lib/active_record/connection_adapters/mysql_adapter.rb +29 -6
  26. data/lib/active_record/connection_adapters/openbase_adapter.rb +349 -0
  27. data/lib/active_record/connection_adapters/{oci_adapter.rb → oracle_adapter.rb} +125 -59
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +24 -21
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +47 -8
  30. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +36 -16
  31. data/lib/active_record/connection_adapters/sybase_adapter.rb +684 -0
  32. data/lib/active_record/fixtures.rb +42 -17
  33. data/lib/active_record/locking.rb +36 -15
  34. data/lib/active_record/migration.rb +111 -8
  35. data/lib/active_record/observer.rb +25 -1
  36. data/lib/active_record/reflection.rb +103 -41
  37. data/lib/active_record/schema.rb +2 -2
  38. data/lib/active_record/schema_dumper.rb +55 -18
  39. data/lib/active_record/timestamp.rb +6 -6
  40. data/lib/active_record/validations.rb +65 -40
  41. data/lib/active_record/vendor/db2.rb +10 -5
  42. data/lib/active_record/vendor/simple.rb +693 -702
  43. data/lib/active_record/version.rb +2 -2
  44. data/rakefile +4 -4
  45. data/test/aaa_create_tables_test.rb +25 -6
  46. data/test/abstract_unit.rb +39 -1
  47. data/test/adapter_test.rb +31 -4
  48. data/test/associations_cascaded_eager_loading_test.rb +106 -0
  49. data/test/associations_go_eager_test.rb +85 -16
  50. data/test/associations_join_model_test.rb +338 -0
  51. data/test/associations_test.rb +129 -50
  52. data/test/base_test.rb +204 -49
  53. data/test/binary_test.rb +1 -1
  54. data/test/calculations_test.rb +169 -0
  55. data/test/callbacks_test.rb +5 -23
  56. data/test/class_inheritable_attributes_test.rb +1 -1
  57. data/test/column_alias_test.rb +1 -1
  58. data/test/connections/native_mysql/connection.rb +1 -0
  59. data/test/connections/native_openbase/connection.rb +22 -0
  60. data/test/connections/{native_oci → native_oracle}/connection.rb +7 -9
  61. data/test/connections/native_sqlite/connection.rb +1 -1
  62. data/test/connections/native_sqlite3/connection.rb +1 -0
  63. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -0
  64. data/test/connections/native_sybase/connection.rb +24 -0
  65. data/test/defaults_test.rb +18 -0
  66. data/test/deprecated_associations_test.rb +2 -2
  67. data/test/deprecated_finder_test.rb +0 -6
  68. data/test/finder_test.rb +26 -23
  69. data/test/fixtures/accounts.yml +10 -0
  70. data/test/fixtures/author.rb +31 -6
  71. data/test/fixtures/author_favorites.yml +4 -0
  72. data/test/fixtures/categories/special_categories.yml +9 -0
  73. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  74. data/test/fixtures/categories_posts.yml +4 -0
  75. data/test/fixtures/categorization.rb +5 -0
  76. data/test/fixtures/categorizations.yml +11 -0
  77. data/test/fixtures/category.rb +6 -0
  78. data/test/fixtures/company.rb +17 -5
  79. data/test/fixtures/company_in_module.rb +19 -5
  80. data/test/fixtures/db_definitions/db2.drop.sql +3 -0
  81. data/test/fixtures/db_definitions/db2.sql +121 -100
  82. data/test/fixtures/db_definitions/db22.sql +2 -2
  83. data/test/fixtures/db_definitions/firebird.drop.sql +4 -0
  84. data/test/fixtures/db_definitions/firebird.sql +26 -0
  85. data/test/fixtures/db_definitions/mysql.drop.sql +3 -0
  86. data/test/fixtures/db_definitions/mysql.sql +21 -1
  87. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  88. data/test/fixtures/db_definitions/openbase.sql +282 -0
  89. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  90. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  91. data/test/fixtures/db_definitions/{oci.drop.sql → oracle.drop.sql} +6 -0
  92. data/test/fixtures/db_definitions/{oci.sql → oracle.sql} +25 -4
  93. data/test/fixtures/db_definitions/{oci2.drop.sql → oracle2.drop.sql} +0 -0
  94. data/test/fixtures/db_definitions/{oci2.sql → oracle2.sql} +0 -0
  95. data/test/fixtures/db_definitions/postgresql.drop.sql +4 -0
  96. data/test/fixtures/db_definitions/postgresql.sql +22 -1
  97. data/test/fixtures/db_definitions/schema.rb +32 -0
  98. data/test/fixtures/db_definitions/sqlite.drop.sql +3 -0
  99. data/test/fixtures/db_definitions/sqlite.sql +18 -0
  100. data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
  101. data/test/fixtures/db_definitions/sqlserver.sql +23 -3
  102. data/test/fixtures/db_definitions/sybase.drop.sql +31 -0
  103. data/test/fixtures/db_definitions/sybase.sql +204 -0
  104. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  106. data/test/fixtures/developers.yml +6 -1
  107. data/test/fixtures/developers_projects.yml +4 -0
  108. data/test/fixtures/funny_jokes.yml +14 -0
  109. data/test/fixtures/joke.rb +6 -0
  110. data/test/fixtures/legacy_thing.rb +3 -0
  111. data/test/fixtures/legacy_things.yml +3 -0
  112. data/test/fixtures/mixin.rb +1 -1
  113. data/test/fixtures/person.rb +4 -1
  114. data/test/fixtures/post.rb +26 -1
  115. data/test/fixtures/project.rb +1 -0
  116. data/test/fixtures/reader.rb +4 -0
  117. data/test/fixtures/readers.yml +4 -0
  118. data/test/fixtures/reply.rb +2 -1
  119. data/test/fixtures/tag.rb +5 -0
  120. data/test/fixtures/tagging.rb +6 -0
  121. data/test/fixtures/taggings.yml +18 -0
  122. data/test/fixtures/tags.yml +7 -0
  123. data/test/fixtures/tasks.yml +2 -2
  124. data/test/fixtures/topic.rb +2 -2
  125. data/test/fixtures/topics.yml +1 -0
  126. data/test/fixtures_test.rb +47 -13
  127. data/test/inheritance_test.rb +2 -2
  128. data/test/locking_test.rb +15 -1
  129. data/test/method_scoping_test.rb +248 -13
  130. data/test/migration_test.rb +68 -11
  131. data/test/mixin_nested_set_test.rb +1 -1
  132. data/test/modules_test.rb +6 -1
  133. data/test/readonly_test.rb +1 -1
  134. data/test/reflection_test.rb +63 -9
  135. data/test/schema_dumper_test.rb +41 -0
  136. data/test/{synonym_test_oci.rb → synonym_test_oracle.rb} +1 -1
  137. data/test/threaded_connections_test.rb +10 -0
  138. data/test/unconnected_test.rb +12 -5
  139. data/test/validations_test.rb +197 -10
  140. metadata +295 -260
  141. data/test/fixtures/db_definitions/create_oracle_db.bat +0 -0
  142. data/test/fixtures/db_definitions/create_oracle_db.sh +0 -0
  143. data/test/fixtures/fixture_database.sqlite +0 -0
  144. data/test/fixtures/fixture_database_2.sqlite +0 -0
@@ -0,0 +1,338 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/tag'
3
+ require 'fixtures/tagging'
4
+ require 'fixtures/post'
5
+ require 'fixtures/comment'
6
+ require 'fixtures/author'
7
+ require 'fixtures/category'
8
+ require 'fixtures/categorization'
9
+
10
+ class AssociationsJoinModelTest < Test::Unit::TestCase
11
+ self.use_transactional_fixtures = false
12
+ fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites
13
+
14
+ def test_has_many
15
+ assert_equal categories(:general), authors(:david).categories.first
16
+ end
17
+
18
+ def test_has_many_inherited
19
+ assert_equal categories(:sti_test), authors(:mary).categories.first
20
+ end
21
+
22
+ def test_inherited_has_many
23
+ assert_equal authors(:mary), categories(:sti_test).authors.first
24
+ end
25
+
26
+ def test_polymorphic_has_many
27
+ assert_equal taggings(:welcome_general), posts(:welcome).taggings.first
28
+ end
29
+
30
+ def test_polymorphic_has_one
31
+ assert_equal taggings(:welcome_general), posts(:welcome).tagging
32
+ end
33
+
34
+ def test_polymorphic_belongs_to
35
+ assert_equal posts(:welcome), posts(:welcome).taggings.first.taggable
36
+ end
37
+
38
+ def test_polymorphic_has_many_going_through_join_model
39
+ assert_equal tags(:general), tag = posts(:welcome).tags.first
40
+ assert_no_queries do
41
+ tag.tagging
42
+ end
43
+ end
44
+
45
+ def test_count_polymorphic_has_many
46
+ assert_equal 1, posts(:welcome).taggings.count
47
+ assert_equal 1, posts(:welcome).tags.count
48
+ end
49
+
50
+ def test_polymorphic_has_many_going_through_join_model_with_find
51
+ assert_equal tags(:general), tag = posts(:welcome).tags.find(:first)
52
+ assert_no_queries do
53
+ tag.tagging
54
+ end
55
+ end
56
+
57
+ def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection
58
+ assert_equal tags(:general), tag = posts(:welcome).funky_tags.first
59
+ assert_no_queries do
60
+ tag.tagging
61
+ end
62
+ end
63
+
64
+ def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection_with_find
65
+ assert_equal tags(:general), tag = posts(:welcome).funky_tags.find(:first)
66
+ assert_no_queries do
67
+ tag.tagging
68
+ end
69
+ end
70
+
71
+ def test_polymorphic_has_many_going_through_join_model_with_disabled_include
72
+ assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
73
+ assert_queries 1 do
74
+ tag.tagging
75
+ end
76
+ end
77
+
78
+ def test_polymorphic_has_many_going_through_join_model_with_custom_select_and_joins
79
+ assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
80
+ tag.author_id
81
+ end
82
+
83
+ def test_polymorphic_has_many_going_through_join_model_with_custom_foreign_key
84
+ assert_equal tags(:misc), taggings(:welcome_general).super_tag
85
+ assert_equal tags(:misc), posts(:welcome).super_tags.first
86
+ end
87
+
88
+ def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class
89
+ post = SubStiPost.create :title => 'SubStiPost', :body => 'SubStiPost body'
90
+ assert_instance_of SubStiPost, post
91
+
92
+ tagging = tags(:misc).taggings.create(:taggable => post)
93
+ assert_equal "SubStiPost", tagging.taggable_type
94
+ end
95
+
96
+ def test_polymorphic_has_many_going_through_join_model_with_inheritance
97
+ assert_equal tags(:general), posts(:thinking).tags.first
98
+ end
99
+
100
+ def test_polymorphic_has_many_going_through_join_model_with_inheritance_with_custom_class_name
101
+ assert_equal tags(:general), posts(:thinking).funky_tags.first
102
+ end
103
+
104
+ def test_polymorphic_has_many_create_model_with_inheritance
105
+ post = posts(:thinking)
106
+ assert_instance_of SpecialPost, post
107
+
108
+ tagging = tags(:misc).taggings.create(:taggable => post)
109
+ assert_equal "Post", tagging.taggable_type
110
+ end
111
+
112
+ def test_polymorphic_has_one_create_model_with_inheritance
113
+ tagging = tags(:misc).create_tagging(:taggable => posts(:thinking))
114
+ assert_equal "Post", tagging.taggable_type
115
+ end
116
+
117
+ def test_set_polymorphic_has_many
118
+ tagging = tags(:misc).taggings.create
119
+ posts(:thinking).taggings << tagging
120
+ assert_equal "Post", tagging.taggable_type
121
+ end
122
+
123
+ def test_set_polymorphic_has_one
124
+ tagging = tags(:misc).taggings.create
125
+ posts(:thinking).tagging = tagging
126
+ assert_equal "Post", tagging.taggable_type
127
+ end
128
+
129
+ def test_create_polymorphic_has_many_with_scope
130
+ old_count = posts(:welcome).taggings.count
131
+ tagging = posts(:welcome).taggings.create(:tag => tags(:misc))
132
+ assert_equal "Post", tagging.taggable_type
133
+ assert_equal old_count+1, posts(:welcome).taggings.count
134
+ end
135
+
136
+ def test_create_polymorphic_has_one_with_scope
137
+ old_count = Tagging.count
138
+ tagging = posts(:welcome).tagging.create(:tag => tags(:misc))
139
+ assert_equal "Post", tagging.taggable_type
140
+ assert_equal old_count+1, Tagging.count
141
+ end
142
+
143
+ def test_delete_polymorphic_has_many_with_delete_all
144
+ assert_equal 1, posts(:welcome).taggings.count
145
+ posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDeleteAll'
146
+ post = find_post_with_dependency(1, :has_many, :taggings, :delete_all)
147
+
148
+ old_count = Tagging.count
149
+ post.destroy
150
+ assert_equal old_count-1, Tagging.count
151
+ assert_equal 0, posts(:welcome).taggings.count
152
+ end
153
+
154
+ def test_delete_polymorphic_has_many_with_destroy
155
+ assert_equal 1, posts(:welcome).taggings.count
156
+ posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDestroy'
157
+ post = find_post_with_dependency(1, :has_many, :taggings, :destroy)
158
+
159
+ old_count = Tagging.count
160
+ post.destroy
161
+ assert_equal old_count-1, Tagging.count
162
+ assert_equal 0, posts(:welcome).taggings.count
163
+ end
164
+
165
+ def test_delete_polymorphic_has_many_with_nullify
166
+ assert_equal 1, posts(:welcome).taggings.count
167
+ posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyNullify'
168
+ post = find_post_with_dependency(1, :has_many, :taggings, :nullify)
169
+
170
+ old_count = Tagging.count
171
+ post.destroy
172
+ assert_equal old_count, Tagging.count
173
+ assert_equal 0, posts(:welcome).taggings.count
174
+ end
175
+
176
+ def test_delete_polymorphic_has_one_with_destroy
177
+ assert posts(:welcome).tagging
178
+ posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneDestroy'
179
+ post = find_post_with_dependency(1, :has_one, :tagging, :destroy)
180
+
181
+ old_count = Tagging.count
182
+ post.destroy
183
+ assert_equal old_count-1, Tagging.count
184
+ assert_nil posts(:welcome).tagging(true)
185
+ end
186
+
187
+ def test_delete_polymorphic_has_one_with_nullify
188
+ assert posts(:welcome).tagging
189
+ posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneNullify'
190
+ post = find_post_with_dependency(1, :has_one, :tagging, :nullify)
191
+
192
+ old_count = Tagging.count
193
+ post.destroy
194
+ assert_equal old_count, Tagging.count
195
+ assert_nil posts(:welcome).tagging(true)
196
+ end
197
+
198
+ def test_has_many_with_piggyback
199
+ assert_equal "2", categories(:sti_test).authors.first.post_id.to_s
200
+ end
201
+
202
+ def test_include_has_many_through
203
+ posts = Post.find(:all, :order => 'posts.id')
204
+ posts_with_authors = Post.find(:all, :include => :authors, :order => 'posts.id')
205
+ assert_equal posts.length, posts_with_authors.length
206
+ posts.length.times do |i|
207
+ assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length }
208
+ end
209
+ end
210
+
211
+ def test_include_polymorphic_has_many_through
212
+ posts = Post.find(:all, :order => 'posts.id')
213
+ posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
214
+ assert_equal posts.length, posts_with_tags.length
215
+ posts.length.times do |i|
216
+ assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
217
+ end
218
+ end
219
+
220
+ def test_include_polymorphic_has_many
221
+ posts = Post.find(:all, :order => 'posts.id')
222
+ posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id')
223
+ assert_equal posts.length, posts_with_taggings.length
224
+ posts.length.times do |i|
225
+ assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
226
+ end
227
+ end
228
+
229
+ def test_has_many_find_all
230
+ assert_equal [categories(:general)], authors(:david).categories.find(:all)
231
+ end
232
+
233
+ def test_has_many_find_first
234
+ assert_equal categories(:general), authors(:david).categories.find(:first)
235
+ end
236
+
237
+ def test_has_many_find_conditions
238
+ assert_equal categories(:general), authors(:david).categories.find(:first, :conditions => "categories.name = 'General'")
239
+ assert_equal nil, authors(:david).categories.find(:first, :conditions => "categories.name = 'Technology'")
240
+ end
241
+
242
+ def test_has_many_class_methods_called_by_method_missing
243
+ assert_equal categories(:general), authors(:david).categories.find_all_by_name('General').first
244
+ # assert_equal nil, authors(:david).categories.find_by_name('Technology')
245
+ end
246
+
247
+ def test_has_many_going_through_join_model_with_custom_foreign_key
248
+ assert_equal [], posts(:thinking).authors
249
+ assert_equal [authors(:mary)], posts(:authorless).authors
250
+ end
251
+
252
+ def test_belongs_to_polymorphic_with_counter_cache
253
+ assert_equal 0, posts(:welcome)[:taggings_count]
254
+ tagging = posts(:welcome).taggings.create(:tag => tags(:general))
255
+ assert_equal 1, posts(:welcome, :reload)[:taggings_count]
256
+ tagging.destroy
257
+ assert posts(:welcome, :reload)[:taggings_count].zero?
258
+ end
259
+
260
+ def test_unavailable_through_reflection
261
+ assert_raises (ActiveRecord::HasManyThroughAssociationNotFoundError) { authors(:david).nothings }
262
+ end
263
+
264
+ def test_has_many_through_join_model_with_conditions
265
+ assert_equal [], posts(:welcome).invalid_taggings
266
+ assert_equal [], posts(:welcome).invalid_tags
267
+ end
268
+
269
+ def test_has_many_polymorphic
270
+ assert_raises ActiveRecord::HasManyThroughAssociationPolymorphicError do
271
+ assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggables
272
+ end
273
+ assert_raises ActiveRecord::EagerLoadPolymorphicError do
274
+ assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggings.find(:all, :include => :taggable)
275
+ end
276
+ end
277
+
278
+ def test_has_many_through_has_many_find_all
279
+ assert_equal comments(:greetings), authors(:david).comments.find(:all, :order => 'comments.id').first
280
+ end
281
+
282
+ def test_has_many_through_has_many_find_all_with_custom_class
283
+ assert_equal comments(:greetings), authors(:david).funky_comments.find(:all, :order => 'comments.id').first
284
+ end
285
+
286
+ def test_has_many_through_has_many_find_first
287
+ assert_equal comments(:greetings), authors(:david).comments.find(:first, :order => 'comments.id')
288
+ end
289
+
290
+ def test_has_many_through_has_many_find_conditions
291
+ assert_equal comments(:does_it_hurt), authors(:david).comments.find(:first, :conditions => "comments.type='SpecialComment'", :order => 'comments.id')
292
+ end
293
+
294
+ def test_has_many_through_has_many_find_by_id
295
+ assert_equal comments(:more_greetings), authors(:david).comments.find(2)
296
+ end
297
+
298
+ def test_eager_load_has_many_through_has_many
299
+ author = Author.find :first, :conditions => ['name = ?', 'David'], :include => :comments, :order => 'comments.id'
300
+ SpecialComment.new; VerySpecialComment.new
301
+ assert_no_queries do
302
+ assert_equal [1,2,3,5,6,7,8,9,10], author.comments.collect(&:id)
303
+ end
304
+ end
305
+
306
+ def test_eager_belongs_to_and_has_one_not_singularized
307
+ assert_nothing_raised do
308
+ Author.find(:first, :include => :author_address)
309
+ AuthorAddress.find(:first, :include => :author)
310
+ end
311
+ end
312
+
313
+ def test_self_referential_has_many_through
314
+ assert_equal [authors(:mary)], authors(:david).favorite_authors
315
+ assert_equal [], authors(:mary).favorite_authors
316
+ end
317
+
318
+ def test_add_to_self_referential_has_many_through
319
+ new_author = Author.create(:name => "Bob")
320
+ authors(:david).author_favorites.create :favorite_author => new_author
321
+ assert_equal new_author, authors(:david).reload.favorite_authors.first
322
+ end
323
+
324
+ def test_has_many_through_uses_correct_attributes
325
+ assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"]
326
+ end
327
+
328
+ private
329
+ # create dynamic Post models to allow different dependency options
330
+ def find_post_with_dependency(post_id, association, association_name, dependency)
331
+ class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
332
+ Post.find(post_id).update_attribute :type, class_name
333
+ klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
334
+ klass.set_table_name 'posts'
335
+ klass.send(association, association_name, :as => :taggable, :dependent => dependency)
336
+ klass.find(post_id)
337
+ end
338
+ end
@@ -79,6 +79,7 @@ class HasOneAssociationsTest < Test::Unit::TestCase
79
79
 
80
80
  def test_triple_equality
81
81
  assert Account === companies(:first_firm).account
82
+ assert companies(:first_firm).account === Account
82
83
  end
83
84
 
84
85
  def test_type_mismatch
@@ -157,6 +158,15 @@ class HasOneAssociationsTest < Test::Unit::TestCase
157
158
  assert_equal "can't be empty", account.errors.on("credit_limit")
158
159
  end
159
160
 
161
+ def test_build_association_twice_without_saving_affects_nothing
162
+ count_of_account = Account.count
163
+ firm = Firm.find(:first)
164
+ account1 = firm.build_account("credit_limit" => 1000)
165
+ account2 = firm.build_account("credit_limit" => 2000)
166
+
167
+ assert_equal count_of_account, Account.count
168
+ end
169
+
160
170
  def test_create_association
161
171
  firm = Firm.new("name" => "GlobalMegaCorp")
162
172
  firm.save
@@ -280,6 +290,17 @@ class HasManyAssociationsTest < Test::Unit::TestCase
280
290
  assert_equal 2, Firm.find(:first).clients.length
281
291
  end
282
292
 
293
+ def test_find_many_with_merged_options
294
+ assert_equal 1, companies(:first_firm).limited_clients.size
295
+ assert_equal 1, companies(:first_firm).limited_clients.find(:all).size
296
+ assert_equal 2, companies(:first_firm).limited_clients.find(:all, :limit => nil).size
297
+ end
298
+
299
+ def test_triple_equality
300
+ assert !(Array === Firm.find(:first).clients)
301
+ assert Firm.find(:first).clients === Array
302
+ end
303
+
283
304
  def test_finding_default_orders
284
305
  assert_equal "Summit", Firm.find(:first).clients.first.name
285
306
  end
@@ -641,7 +662,7 @@ class HasManyAssociationsTest < Test::Unit::TestCase
641
662
  def test_three_levels_of_dependence
642
663
  topic = Topic.create "title" => "neat and simple"
643
664
  reply = topic.replies.create "title" => "neat and simple", "content" => "still digging it"
644
- silly_reply = reply.silly_replies.create "title" => "neat and simple", "content" => "ain't complaining"
665
+ silly_reply = reply.replies.create "title" => "neat and simple", "content" => "ain't complaining"
645
666
 
646
667
  assert_nothing_raised { topic.destroy }
647
668
  end
@@ -664,7 +685,6 @@ class HasManyAssociationsTest < Test::Unit::TestCase
664
685
  assert_equal num_accounts - 1, Account.count
665
686
  end
666
687
 
667
-
668
688
  def test_depends_and_nullify
669
689
  num_accounts = Account.count
670
690
  num_companies = Company.count
@@ -747,6 +767,11 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
747
767
  assert_nothing_raised { account.firm = account.firm }
748
768
  end
749
769
 
770
+ def test_triple_equality
771
+ assert Client.find(3).firm === Firm
772
+ assert Firm === Client.find(3).firm
773
+ end
774
+
750
775
  def test_type_mismatch
751
776
  assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = 1 }
752
777
  assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = Project.find(1) }
@@ -804,6 +829,38 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
804
829
  assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted"
805
830
  end
806
831
 
832
+ def test_belongs_to_counter_with_reassigning
833
+ t1 = Topic.create("title" => "t1")
834
+ t2 = Topic.create("title" => "t2")
835
+ r1 = Reply.new("title" => "r1", "content" => "r1")
836
+ r1.topic = t1
837
+
838
+ assert r1.save
839
+ assert_equal 1, Topic.find(t1.id).replies.size
840
+ assert_equal 0, Topic.find(t2.id).replies.size
841
+
842
+ r1.topic = Topic.find(t2.id)
843
+
844
+ assert r1.save
845
+ assert_equal 0, Topic.find(t1.id).replies.size
846
+ assert_equal 1, Topic.find(t2.id).replies.size
847
+
848
+ r1.topic = nil
849
+
850
+ assert_equal 0, Topic.find(t1.id).replies.size
851
+ assert_equal 0, Topic.find(t2.id).replies.size
852
+
853
+ r1.topic = t1
854
+
855
+ assert_equal 1, Topic.find(t1.id).replies.size
856
+ assert_equal 0, Topic.find(t2.id).replies.size
857
+
858
+ r1.destroy
859
+
860
+ assert_equal 0, Topic.find(t1.id).replies.size
861
+ assert_equal 0, Topic.find(t2.id).replies.size
862
+ end
863
+
807
864
  def test_assignment_before_parent_saved
808
865
  client = Client.find(:first)
809
866
  apple = Firm.new("name" => "Apple")
@@ -860,21 +917,32 @@ class BelongsToAssociationsTest < Test::Unit::TestCase
860
917
  assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # '
861
918
  end
862
919
 
863
- def xtest_counter_cache
864
- apple = Firm.create("name" => "Apple")
865
- final_cut = apple.clients.create("name" => "Final Cut")
866
-
867
- apple.clients.to_s
868
- assert_equal 1, apple.clients.size, "Created one client"
920
+ def test_counter_cache
921
+ topic = Topic.create :title => "Zoom-zoom-zoom"
922
+ assert_equal 0, topic[:replies_count]
869
923
 
870
- apple.companies_count = 2
871
- apple.save
924
+ reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
925
+ reply.topic = topic
926
+
927
+ assert_equal 1, topic.reload[:replies_count]
928
+ assert_equal 1, topic.replies.size
929
+
930
+ topic[:replies_count] = 15
931
+ assert_equal 15, topic.replies.size
932
+ end
933
+
934
+ def test_custom_counter_cache
935
+ reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
936
+ assert_equal 0, reply[:replies_count]
937
+
938
+ silly = SillyReply.create(:title => "gaga", :content => "boo-boo")
939
+ silly.reply = reply
872
940
 
873
- apple = Firm.find(:first, :conditions => "name = 'Apple'")
874
- assert_equal 2, apple.clients.size, "Should use the new cached number"
941
+ assert_equal 1, reply.reload[:replies_count]
942
+ assert_equal 1, reply.replies.size
875
943
 
876
- apple.clients.to_s
877
- assert_equal 1, apple.clients.size, "Should not use the cached number, but go to the database"
944
+ reply[:replies_count] = 17
945
+ assert_equal 17, reply.replies.size
878
946
  end
879
947
 
880
948
  def test_store_two_association_with_one_save
@@ -1022,10 +1090,15 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1022
1090
 
1023
1091
  active_record = Project.find(1)
1024
1092
  assert !active_record.developers.empty?
1025
- assert_equal 2, active_record.developers.size
1093
+ assert_equal 3, active_record.developers.size
1026
1094
  assert active_record.developers.include?(david)
1027
1095
  end
1028
1096
 
1097
+ def test_triple_equality
1098
+ assert !(Array === Developer.find(1).projects)
1099
+ assert Developer.find(1).projects === Array
1100
+ end
1101
+
1029
1102
  def test_adding_single
1030
1103
  jamis = Developer.find(2)
1031
1104
  jamis.projects.reload # causing the collection to load
@@ -1149,7 +1222,6 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1149
1222
  no_of_devels = Developer.count
1150
1223
  no_of_projects = Project.count
1151
1224
  now = Date.today
1152
- sqlnow = Time.now.strftime("%Y/%m/%d 00:00:00")
1153
1225
  ken = Developer.new("name" => "Ken")
1154
1226
  ken.projects.push_with_attributes( Project.find(1), :joined_on => now )
1155
1227
  p = Project.new("name" => "Foomatic")
@@ -1164,12 +1236,22 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1164
1236
  assert_equal 2, ken.projects(true).size
1165
1237
 
1166
1238
  kenReloaded = Developer.find_by_name 'Ken'
1167
- # SQL Server doesn't have a separate column type just for dates,
1168
- # so the time is in the string and incorrectly formatted
1169
- if current_adapter?(:SQLServerAdapter)
1170
- kenReloaded.projects.each { |prj| assert_equal(sqlnow, prj.joined_on.strftime("%Y/%m/%d 00:00:00")) }
1171
- else
1172
- kenReloaded.projects.each { |prj| assert_equal(now.to_s, prj.joined_on.to_s) }
1239
+ kenReloaded.projects.each {|prj| assert_date_from_db(now, prj.joined_on)}
1240
+ end
1241
+
1242
+ def test_habtm_saving_multiple_relationships
1243
+ new_project = Project.new("name" => "Grimetime")
1244
+ amount_of_developers = 4
1245
+ developers = (0..amount_of_developers).collect {|i| Developer.create(:name => "JME #{i}") }
1246
+
1247
+ new_project.developer_ids = [developers[0].id, developers[1].id]
1248
+ new_project.developers_with_callback_ids = [developers[2].id, developers[3].id]
1249
+ assert new_project.save
1250
+
1251
+ new_project.reload
1252
+ assert_equal amount_of_developers, new_project.developers.size
1253
+ amount_of_developers.times do |i|
1254
+ assert_equal developers[i].name, new_project.developers[i].name
1173
1255
  end
1174
1256
  end
1175
1257
 
@@ -1200,7 +1282,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1200
1282
  def test_uniq_before_the_fact
1201
1283
  projects(:active_record).developers << developers(:jamis)
1202
1284
  projects(:active_record).developers << developers(:david)
1203
- assert_equal 2, projects(:active_record, :reload).developers.size
1285
+ assert_equal 3, projects(:active_record, :reload).developers.size
1204
1286
  end
1205
1287
 
1206
1288
  def test_deleting
@@ -1208,13 +1290,13 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1208
1290
  active_record = Project.find(1)
1209
1291
  david.projects.reload
1210
1292
  assert_equal 2, david.projects.size
1211
- assert_equal 2, active_record.developers.size
1293
+ assert_equal 3, active_record.developers.size
1212
1294
 
1213
1295
  david.projects.delete(active_record)
1214
1296
 
1215
1297
  assert_equal 1, david.projects.size
1216
1298
  assert_equal 1, david.projects(true).size
1217
- assert_equal 1, active_record.developers(true).size
1299
+ assert_equal 2, active_record.developers(true).size
1218
1300
  end
1219
1301
 
1220
1302
  def test_deleting_array
@@ -1229,16 +1311,16 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1229
1311
  david = Developer.find(1)
1230
1312
  active_record = Project.find(1)
1231
1313
  active_record.developers.reload
1232
- assert_equal 2, active_record.developers_by_sql.size
1314
+ assert_equal 3, active_record.developers_by_sql.size
1233
1315
 
1234
1316
  active_record.developers_by_sql.delete(david)
1235
- assert_equal 1, active_record.developers_by_sql(true).size
1317
+ assert_equal 2, active_record.developers_by_sql(true).size
1236
1318
  end
1237
1319
 
1238
1320
  def test_deleting_array_with_sql
1239
1321
  active_record = Project.find(1)
1240
1322
  active_record.developers.reload
1241
- assert_equal 2, active_record.developers_by_sql.size
1323
+ assert_equal 3, active_record.developers_by_sql.size
1242
1324
 
1243
1325
  active_record.developers_by_sql.delete(Developer.find(:all))
1244
1326
  assert_equal 0, active_record.developers_by_sql(true).size
@@ -1261,13 +1343,7 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1261
1343
  end
1262
1344
 
1263
1345
  def test_additional_columns_from_join_table
1264
- # SQL Server doesn't have a separate column type just for dates,
1265
- # so the time is in the string and incorrectly formatted
1266
- if current_adapter?(:SQLServerAdapter)
1267
- assert_equal Time.mktime(2004, 10, 10).strftime("%Y/%m/%d 00:00:00"), Developer.find(1).projects.first.joined_on.strftime("%Y/%m/%d 00:00:00")
1268
- else
1269
- assert_equal Date.new(2004, 10, 10).to_s, Developer.find(1).projects.first.joined_on.to_s
1270
- end
1346
+ assert_date_from_db Date.new(2004, 10, 10), Developer.find(1).projects.first.joined_on
1271
1347
  end
1272
1348
 
1273
1349
  def test_destroy_all
@@ -1282,26 +1358,20 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1282
1358
  def test_rich_association
1283
1359
  jamis = developers(:jamis)
1284
1360
  jamis.projects.push_with_attributes(projects(:action_controller), :joined_on => Date.today)
1285
- # SQL Server doesn't have a separate column type just for dates,
1286
- # so the time is in the string and incorrectly formatted
1287
- if current_adapter?(:SQLServerAdapter)
1288
- assert_equal Time.now.strftime("%Y/%m/%d 00:00:00"), jamis.projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on.strftime("%Y/%m/%d 00:00:00")
1289
- assert_equal Time.now.strftime("%Y/%m/%d 00:00:00"), developers(:jamis).projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on.strftime("%Y/%m/%d 00:00:00")
1290
- else
1291
- assert_equal Date.today.to_s, jamis.projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on.to_s
1292
- assert_equal Date.today.to_s, developers(:jamis).projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on.to_s
1293
- end
1361
+
1362
+ assert_date_from_db Date.today, jamis.projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on
1363
+ assert_date_from_db Date.today, developers(:jamis).projects.select { |p| p.name == projects(:action_controller).name }.first.joined_on
1294
1364
  end
1295
1365
 
1296
1366
  def test_associations_with_conditions
1297
- assert_equal 2, projects(:active_record).developers.size
1367
+ assert_equal 3, projects(:active_record).developers.size
1298
1368
  assert_equal 1, projects(:active_record).developers_named_david.size
1299
1369
 
1300
1370
  assert_equal developers(:david), projects(:active_record).developers_named_david.find(developers(:david).id)
1301
1371
  assert_equal developers(:david), projects(:active_record).salaried_developers.find(developers(:david).id)
1302
1372
 
1303
1373
  projects(:active_record).developers_named_david.clear
1304
- assert_equal 1, projects(:active_record, :reload).developers.size
1374
+ assert_equal 2, projects(:active_record, :reload).developers.size
1305
1375
  end
1306
1376
 
1307
1377
  def test_find_in_association
@@ -1326,6 +1396,11 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1326
1396
  assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
1327
1397
  end
1328
1398
 
1399
+ def test_find_with_merged_options
1400
+ assert_equal 1, projects(:active_record).limited_developers.size
1401
+ assert_equal 1, projects(:active_record).limited_developers.find(:all).size
1402
+ assert_equal 3, projects(:active_record).limited_developers.find(:all, :limit => nil).size
1403
+ end
1329
1404
 
1330
1405
  def test_new_with_values_in_collection
1331
1406
  jamis = DeveloperForProjectWithAfterCreateHook.find_by_name('Jamis')
@@ -1339,12 +1414,12 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1339
1414
  assert project.developers.include?(david)
1340
1415
  end
1341
1416
 
1342
- def xtest_find_in_association_with_options
1417
+ def test_find_in_association_with_options
1343
1418
  developers = projects(:active_record).developers.find(:all)
1344
- assert_equal 2, developers.size
1419
+ assert_equal 3, developers.size
1345
1420
 
1346
- assert_equal developers(:david), projects(:active_record).developers.find(:first, :conditions => "salary < 10000")
1347
- assert_equal developers(:jamis), projects(:active_record).developers.find(:first, :order => "salary DESC")
1421
+ assert_equal developers(:poor_jamis), projects(:active_record).developers.find(:first, :conditions => "salary < 10000")
1422
+ assert_equal developers(:jamis), projects(:active_record).developers.find(:first, :order => "salary DESC")
1348
1423
  end
1349
1424
 
1350
1425
  def test_replace_with_less
@@ -1394,4 +1469,8 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
1394
1469
  AND developer_id = #{developer.id}
1395
1470
  end_sql
1396
1471
  end
1472
+
1473
+ def test_join_table_alias
1474
+ assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL').size
1475
+ end
1397
1476
  end