activerecord 2.1.2 → 2.2.2

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 (110) hide show
  1. data/CHANGELOG +32 -6
  2. data/README +0 -0
  3. data/Rakefile +4 -5
  4. data/lib/active_record.rb +11 -10
  5. data/lib/active_record/aggregations.rb +110 -38
  6. data/lib/active_record/association_preload.rb +104 -15
  7. data/lib/active_record/associations.rb +427 -212
  8. data/lib/active_record/associations/association_collection.rb +101 -16
  9. data/lib/active_record/associations/association_proxy.rb +65 -13
  10. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
  12. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -3
  13. data/lib/active_record/associations/has_many_association.rb +28 -28
  14. data/lib/active_record/associations/has_many_through_association.rb +21 -19
  15. data/lib/active_record/associations/has_one_association.rb +24 -7
  16. data/lib/active_record/associations/has_one_through_association.rb +3 -4
  17. data/lib/active_record/attribute_methods.rb +13 -5
  18. data/lib/active_record/base.rb +435 -212
  19. data/lib/active_record/calculations.rb +12 -5
  20. data/lib/active_record/callbacks.rb +28 -9
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +355 -0
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +42 -215
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -5
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +48 -7
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +10 -4
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -26
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +71 -45
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +155 -84
  30. data/lib/active_record/dirty.rb +25 -7
  31. data/lib/active_record/dynamic_finder_match.rb +41 -0
  32. data/lib/active_record/fixtures.rb +10 -9
  33. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  34. data/lib/active_record/locale/en.yml +54 -0
  35. data/lib/active_record/migration.rb +47 -10
  36. data/lib/active_record/named_scope.rb +29 -16
  37. data/lib/active_record/reflection.rb +118 -54
  38. data/lib/active_record/schema_dumper.rb +13 -7
  39. data/lib/active_record/test_case.rb +18 -5
  40. data/lib/active_record/transactions.rb +89 -34
  41. data/lib/active_record/validations.rb +270 -180
  42. data/lib/active_record/version.rb +1 -1
  43. data/test/cases/active_schema_test_mysql.rb +5 -0
  44. data/test/cases/adapter_test.rb +6 -0
  45. data/test/cases/aggregations_test.rb +39 -0
  46. data/test/cases/associations/belongs_to_associations_test.rb +10 -0
  47. data/test/cases/associations/eager_load_nested_include_test.rb +30 -12
  48. data/test/cases/associations/eager_test.rb +54 -5
  49. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +77 -10
  50. data/test/cases/associations/has_many_associations_test.rb +74 -7
  51. data/test/cases/associations/has_many_through_associations_test.rb +50 -3
  52. data/test/cases/associations/has_one_associations_test.rb +17 -0
  53. data/test/cases/associations/has_one_through_associations_test.rb +49 -1
  54. data/test/cases/associations_test.rb +0 -0
  55. data/test/cases/attribute_methods_test.rb +59 -4
  56. data/test/cases/base_test.rb +93 -21
  57. data/test/cases/binary_test.rb +1 -5
  58. data/test/cases/calculations_test.rb +5 -0
  59. data/test/cases/callbacks_observers_test.rb +38 -0
  60. data/test/cases/connection_test_mysql.rb +1 -1
  61. data/test/cases/defaults_test.rb +32 -1
  62. data/test/cases/deprecated_finder_test.rb +0 -0
  63. data/test/cases/dirty_test.rb +13 -0
  64. data/test/cases/finder_test.rb +162 -12
  65. data/test/cases/fixtures_test.rb +32 -3
  66. data/test/cases/helper.rb +15 -0
  67. data/test/cases/i18n_test.rb +41 -0
  68. data/test/cases/inheritance_test.rb +2 -2
  69. data/test/cases/lifecycle_test.rb +0 -0
  70. data/test/cases/locking_test.rb +4 -9
  71. data/test/cases/method_scoping_test.rb +109 -2
  72. data/test/cases/migration_test.rb +43 -8
  73. data/test/cases/multiple_db_test.rb +25 -0
  74. data/test/cases/named_scope_test.rb +74 -0
  75. data/test/cases/pooled_connections_test.rb +103 -0
  76. data/test/cases/readonly_test.rb +0 -0
  77. data/test/cases/reflection_test.rb +11 -3
  78. data/test/cases/reload_models_test.rb +20 -0
  79. data/test/cases/sanitize_test.rb +25 -0
  80. data/test/cases/schema_authorization_test_postgresql.rb +2 -2
  81. data/test/cases/transactions_test.rb +62 -12
  82. data/test/cases/unconnected_test.rb +0 -0
  83. data/test/cases/validations_i18n_test.rb +921 -0
  84. data/test/cases/validations_test.rb +44 -33
  85. data/test/connections/native_mysql/connection.rb +1 -3
  86. data/test/fixtures/companies.yml +1 -0
  87. data/test/fixtures/customers.yml +10 -1
  88. data/test/fixtures/fixture_database.sqlite3 +0 -0
  89. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  90. data/test/fixtures/organizations.yml +5 -0
  91. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  92. data/test/models/author.rb +3 -0
  93. data/test/models/category.rb +3 -0
  94. data/test/models/club.rb +6 -0
  95. data/test/models/company.rb +25 -1
  96. data/test/models/customer.rb +19 -1
  97. data/test/models/member.rb +2 -0
  98. data/test/models/member_detail.rb +4 -0
  99. data/test/models/organization.rb +4 -0
  100. data/test/models/parrot.rb +1 -0
  101. data/test/models/post.rb +3 -0
  102. data/test/models/reply.rb +0 -0
  103. data/test/models/topic.rb +3 -0
  104. data/test/schema/schema.rb +12 -1
  105. metadata +22 -10
  106. data/lib/active_record/vendor/mysql.rb +0 -1214
  107. data/test/cases/adapter_test_sqlserver.rb +0 -95
  108. data/test/cases/table_name_test_sqlserver.rb +0 -23
  109. data/test/cases/threaded_connections_test.rb +0 -48
  110. data/test/schema/sqlserver_specific_schema.rb +0 -5
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 2
4
- MINOR = 1
4
+ MINOR = 2
5
5
  TINY = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
@@ -25,6 +25,11 @@ class ActiveSchemaTest < ActiveRecord::TestCase
25
25
  assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'})
26
26
  assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci})
27
27
  end
28
+
29
+ def test_recreate_mysql_database_with_encoding
30
+ create_database(:luca, {:charset => 'latin1'})
31
+ assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'})
32
+ end
28
33
  end
29
34
 
30
35
  def test_add_column
@@ -65,6 +65,12 @@ class AdapterTest < ActiveRecord::TestCase
65
65
  end
66
66
  end
67
67
 
68
+ if current_adapter?(:PostgreSQLAdapter)
69
+ def test_encoding
70
+ assert_not_nil @connection.encoding
71
+ end
72
+ end
73
+
68
74
  def test_table_alias
69
75
  def @connection.test_table_alias_length() 10; end
70
76
  class << @connection
@@ -107,6 +107,45 @@ class AggregationsTest < ActiveRecord::TestCase
107
107
  customers(:david).gps_location = nil
108
108
  assert_equal nil, customers(:david).gps_location
109
109
  end
110
+
111
+ def test_custom_constructor
112
+ assert_equal 'Barney GUMBLE', customers(:barney).fullname.to_s
113
+ assert_kind_of Fullname, customers(:barney).fullname
114
+ end
115
+
116
+ def test_custom_converter
117
+ customers(:barney).fullname = 'Barnoit Gumbleau'
118
+ assert_equal 'Barnoit GUMBLEAU', customers(:barney).fullname.to_s
119
+ assert_kind_of Fullname, customers(:barney).fullname
120
+ end
121
+ end
122
+
123
+ class DeprecatedAggregationsTest < ActiveRecord::TestCase
124
+ class Person < ActiveRecord::Base; end
125
+
126
+ def test_conversion_block_is_deprecated
127
+ assert_deprecated 'conversion block has been deprecated' do
128
+ Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
129
+ end
130
+ end
131
+
132
+ def test_conversion_block_used_when_converter_option_is_nil
133
+ assert_deprecated 'conversion block has been deprecated' do
134
+ Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
135
+ end
136
+ assert_raise(NoMethodError) { Person.new.balance = 5 }
137
+ end
138
+
139
+ def test_converter_option_overrides_conversion_block
140
+ assert_deprecated 'conversion block has been deprecated' do
141
+ Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| Money.new(balance) }) { |balance| balance.to_money }
142
+ end
143
+
144
+ person = Person.new
145
+ assert_nothing_raised { person.balance = 5 }
146
+ assert_equal 5, person.balance.amount
147
+ assert_kind_of Money, person.balance
148
+ end
110
149
  end
111
150
 
112
151
  class OverridingAggregationsTest < ActiveRecord::TestCase
@@ -428,4 +428,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
428
428
  assert log.valid?
429
429
  assert log.save
430
430
  end
431
+
432
+ def test_belongs_to_proxy_should_not_respond_to_private_methods
433
+ assert_raises(NoMethodError) { companies(:first_firm).private_method }
434
+ assert_raises(NoMethodError) { companies(:second_client).firm.private_method }
435
+ end
436
+
437
+ def test_belongs_to_proxy_should_respond_to_private_methods_via_send
438
+ companies(:first_firm).send(:private_method)
439
+ companies(:second_client).firm.send(:private_method)
440
+ end
431
441
  end
@@ -1,5 +1,20 @@
1
1
  require 'cases/helper'
2
2
 
3
+ module Remembered
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ base.class_eval do
7
+ after_create :remember
8
+ protected
9
+ def remember; self.class.remembered << self; end
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ def remembered; @@remembered ||= []; end
15
+ def rand; @@remembered.rand; end
16
+ end
17
+ end
3
18
 
4
19
  class ShapeExpression < ActiveRecord::Base
5
20
  belongs_to :shape, :polymorphic => true
@@ -8,26 +23,33 @@ end
8
23
 
9
24
  class Circle < ActiveRecord::Base
10
25
  has_many :shape_expressions, :as => :shape
26
+ include Remembered
11
27
  end
12
28
  class Square < ActiveRecord::Base
13
29
  has_many :shape_expressions, :as => :shape
30
+ include Remembered
14
31
  end
15
32
  class Triangle < ActiveRecord::Base
16
33
  has_many :shape_expressions, :as => :shape
34
+ include Remembered
17
35
  end
18
36
  class PaintColor < ActiveRecord::Base
19
37
  has_many :shape_expressions, :as => :paint
20
38
  belongs_to :non_poly, :foreign_key => "non_poly_one_id", :class_name => "NonPolyOne"
39
+ include Remembered
21
40
  end
22
41
  class PaintTexture < ActiveRecord::Base
23
42
  has_many :shape_expressions, :as => :paint
24
43
  belongs_to :non_poly, :foreign_key => "non_poly_two_id", :class_name => "NonPolyTwo"
44
+ include Remembered
25
45
  end
26
46
  class NonPolyOne < ActiveRecord::Base
27
47
  has_many :paint_colors
48
+ include Remembered
28
49
  end
29
50
  class NonPolyTwo < ActiveRecord::Base
30
51
  has_many :paint_textures
52
+ include Remembered
31
53
  end
32
54
 
33
55
 
@@ -49,23 +71,19 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
49
71
  end
50
72
 
51
73
 
52
- # meant to be supplied as an ID, never returns 0
53
- def rand_simple
54
- val = (NUM_SIMPLE_OBJS * rand).round
55
- val == 0 ? 1 : val
56
- end
57
-
58
74
  def generate_test_object_graphs
59
75
  1.upto(NUM_SIMPLE_OBJS) do
60
76
  [Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!)
61
77
  end
62
- 1.upto(NUM_SIMPLE_OBJS) do |i|
63
- PaintColor.create!(:non_poly_one_id => rand_simple)
64
- PaintTexture.create!(:non_poly_two_id => rand_simple)
78
+ 1.upto(NUM_SIMPLE_OBJS) do
79
+ PaintColor.create!(:non_poly_one_id => NonPolyOne.rand.id)
80
+ PaintTexture.create!(:non_poly_two_id => NonPolyTwo.rand.id)
65
81
  end
66
- 1.upto(NUM_SHAPE_EXPRESSIONS) do |i|
67
- ShapeExpression.create!(:shape_type => [Circle, Square, Triangle].rand.to_s, :shape_id => rand_simple,
68
- :paint_type => [PaintColor, PaintTexture].rand.to_s, :paint_id => rand_simple)
82
+ 1.upto(NUM_SHAPE_EXPRESSIONS) do
83
+ shape_type = [Circle, Square, Triangle].rand
84
+ paint_type = [PaintColor, PaintTexture].rand
85
+ ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.rand.id,
86
+ :paint_type => paint_type.to_s, :paint_id => paint_type.rand.id)
69
87
  end
70
88
  end
71
89
 
@@ -18,7 +18,7 @@ require 'models/developer'
18
18
  require 'models/project'
19
19
 
20
20
  class EagerAssociationTest < ActiveRecord::TestCase
21
- fixtures :posts, :comments, :authors, :categories, :categories_posts,
21
+ fixtures :posts, :comments, :authors, :author_addresses, :categories, :categories_posts,
22
22
  :companies, :accounts, :tags, :taggings, :people, :readers,
23
23
  :owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books,
24
24
  :developers, :projects, :developers_projects
@@ -111,6 +111,46 @@ class EagerAssociationTest < ActiveRecord::TestCase
111
111
  end
112
112
  end
113
113
 
114
+ def test_finding_with_includes_on_has_many_association_with_same_include_includes_only_once
115
+ author_id = authors(:david).id
116
+ author = assert_queries(3) { Author.find(author_id, :include => {:posts_with_comments => :comments}) } # find the author, then find the posts, then find the comments
117
+ author.posts_with_comments.each do |post_with_comments|
118
+ assert_equal post_with_comments.comments.length, post_with_comments.comments.count
119
+ assert_equal nil, post_with_comments.comments.uniq!
120
+ end
121
+ end
122
+
123
+ def test_finding_with_includes_on_has_one_assocation_with_same_include_includes_only_once
124
+ author = authors(:david)
125
+ post = author.post_about_thinking_with_last_comment
126
+ last_comment = post.last_comment
127
+ author = assert_queries(3) { Author.find(author.id, :include => {:post_about_thinking_with_last_comment => :last_comment})} # find the author, then find the posts, then find the comments
128
+ assert_no_queries do
129
+ assert_equal post, author.post_about_thinking_with_last_comment
130
+ assert_equal last_comment, author.post_about_thinking_with_last_comment.last_comment
131
+ end
132
+ end
133
+
134
+ def test_finding_with_includes_on_belongs_to_association_with_same_include_includes_only_once
135
+ post = posts(:welcome)
136
+ author = post.author
137
+ author_address = author.author_address
138
+ post = assert_queries(3) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author, then find the address
139
+ assert_no_queries do
140
+ assert_equal author, post.author_with_address
141
+ assert_equal author_address, post.author_with_address.author_address
142
+ end
143
+ end
144
+
145
+ def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
146
+ post = posts(:welcome)
147
+ post.update_attributes!(:author => nil)
148
+ post = assert_queries(2) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author which is null so no query for the address
149
+ assert_no_queries do
150
+ assert_equal nil, post.author_with_address
151
+ end
152
+ end
153
+
114
154
  def test_loading_from_an_association
115
155
  posts = authors(:david).posts.find(:all, :include => :comments, :order => "posts.id")
116
156
  assert_equal 2, posts.first.comments.size
@@ -267,14 +307,23 @@ class EagerAssociationTest < ActiveRecord::TestCase
267
307
  end
268
308
 
269
309
  def test_eager_with_has_many_through
270
- posts_with_comments = people(:michael).posts.find(:all, :include => :comments)
271
- posts_with_author = people(:michael).posts.find(:all, :include => :author )
272
- posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ])
310
+ posts_with_comments = people(:michael).posts.find(:all, :include => :comments, :order => 'posts.id')
311
+ posts_with_author = people(:michael).posts.find(:all, :include => :author, :order => 'posts.id')
312
+ posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ], :order => 'posts.id')
273
313
  assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size }
274
314
  assert_equal authors(:david), assert_no_queries { posts_with_author.first.author }
275
315
  assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
276
316
  end
277
317
 
318
+ def test_eager_with_has_many_through_a_belongs_to_association
319
+ author = authors(:mary)
320
+ post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
321
+ author.author_favorites.create(:favorite_author_id => 1)
322
+ author.author_favorites.create(:favorite_author_id => 2)
323
+ posts_with_author_favorites = author.posts.find(:all, :include => :author_favorites)
324
+ assert_no_queries { posts_with_author_favorites.first.author_favorites.first.author_id }
325
+ end
326
+
278
327
  def test_eager_with_has_many_through_an_sti_join_model
279
328
  author = Author.find(:first, :include => :special_post_comments, :order => 'authors.id')
280
329
  assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
@@ -618,7 +667,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
618
667
  end
619
668
 
620
669
  def test_count_with_include
621
- if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
670
+ if current_adapter?(:SybaseAdapter)
622
671
  assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
623
672
  elsif current_adapter?(:OpenBaseAdapter)
624
673
  assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15")
@@ -68,6 +68,16 @@ class DeveloperWithSymbolsForKeys < ActiveRecord::Base
68
68
  :foreign_key => "developer_id"
69
69
  end
70
70
 
71
+ class DeveloperWithCounterSQL < ActiveRecord::Base
72
+ set_table_name 'developers'
73
+ has_and_belongs_to_many :projects,
74
+ :class_name => "DeveloperWithCounterSQL",
75
+ :join_table => "developers_projects",
76
+ :association_foreign_key => "project_id",
77
+ :foreign_key => "developer_id",
78
+ :counter_sql => 'SELECT COUNT(*) AS count_all FROM projects INNER JOIN developers_projects ON projects.id = developers_projects.project_id WHERE developers_projects.developer_id =#{id}'
79
+ end
80
+
71
81
  class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
72
82
  fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
73
83
  :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
@@ -223,10 +233,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
223
233
  devel = Developer.find(1)
224
234
  proj = assert_no_queries { devel.projects.build("name" => "Projekt") }
225
235
  assert !devel.projects.loaded?
226
-
236
+
227
237
  assert_equal devel.projects.last, proj
228
238
  assert devel.projects.loaded?
229
-
239
+
230
240
  assert proj.new_record?
231
241
  devel.save
232
242
  assert !proj.new_record?
@@ -251,10 +261,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
251
261
  devel = Developer.find(1)
252
262
  proj = devel.projects.create("name" => "Projekt")
253
263
  assert !devel.projects.loaded?
254
-
264
+
255
265
  assert_equal devel.projects.last, proj
256
- assert devel.projects.loaded?
257
-
266
+ assert !devel.projects.loaded?
267
+
258
268
  assert !proj.new_record?
259
269
  assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
260
270
  end
@@ -274,10 +284,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
274
284
 
275
285
  def test_creation_respects_hash_condition
276
286
  post = categories(:general).post_with_conditions.build(:body => '')
277
-
287
+
278
288
  assert post.save
279
289
  assert_equal 'Yet Another Testing Title', post.title
280
-
290
+
281
291
  another_post = categories(:general).post_with_conditions.create(:body => '')
282
292
 
283
293
  assert !another_post.new_record?
@@ -288,7 +298,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
288
298
  dev = developers(:jamis)
289
299
  dev.projects << projects(:active_record)
290
300
  dev.projects << projects(:active_record)
291
-
301
+
292
302
  assert_equal 3, dev.projects.size
293
303
  assert_equal 1, dev.projects.uniq.size
294
304
  end
@@ -415,13 +425,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
415
425
  project.developers.class # force load target
416
426
 
417
427
  developer = project.developers.first
418
-
428
+
419
429
  assert_no_queries do
420
430
  assert project.developers.loaded?
421
431
  assert project.developers.include?(developer)
422
432
  end
423
433
  end
424
-
434
+
425
435
  def test_include_checks_if_record_exists_if_target_not_loaded
426
436
  project = projects(:active_record)
427
437
  developer = project.developers.first
@@ -636,11 +646,39 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
636
646
  assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL', :group => group.join(",")).size
637
647
  end
638
648
 
649
+ def test_find_grouped
650
+ all_posts_from_category1 = Post.find(:all, :conditions => "category_id = 1", :joins => :categories)
651
+ grouped_posts_of_category1 = Post.find(:all, :conditions => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories)
652
+ assert_equal 4, all_posts_from_category1.size
653
+ assert_equal 1, grouped_posts_of_category1.size
654
+ end
655
+
656
+ def test_find_scoped_grouped
657
+ assert_equal 4, categories(:general).posts_gruoped_by_title.size
658
+ assert_equal 1, categories(:technology).posts_gruoped_by_title.size
659
+ end
660
+
639
661
  def test_get_ids
640
662
  assert_equal projects(:active_record, :action_controller).map(&:id).sort, developers(:david).project_ids.sort
641
663
  assert_equal [projects(:active_record).id], developers(:jamis).project_ids
642
664
  end
643
665
 
666
+ def test_get_ids_for_loaded_associations
667
+ developer = developers(:david)
668
+ developer.projects(true)
669
+ assert_queries(0) do
670
+ developer.project_ids
671
+ developer.project_ids
672
+ end
673
+ end
674
+
675
+ def test_get_ids_for_unloaded_associations_does_not_load_them
676
+ developer = developers(:david)
677
+ assert !developer.projects.loaded?
678
+ assert_equal projects(:active_record, :action_controller).map(&:id).sort, developer.project_ids.sort
679
+ assert !developer.projects.loaded?
680
+ end
681
+
644
682
  def test_assign_ids
645
683
  developer = Developer.new("name" => "Joe")
646
684
  developer.project_ids = projects(:active_record, :action_controller).map(&:id)
@@ -703,4 +741,33 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
703
741
  # due to Unknown column 'authors.id'
704
742
  assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title('Welcome to the weblog')
705
743
  end
744
+
745
+ def test_counting_on_habtm_association_and_not_array
746
+ david = Developer.find(1)
747
+ # Extra parameter just to make sure we aren't falling back to
748
+ # Array#count in Ruby >=1.8.7, which would raise an ArgumentError
749
+ assert_nothing_raised { david.projects.count(:all, :conditions => '1=1') }
750
+ end
751
+
752
+ def test_count
753
+ david = Developer.find(1)
754
+ assert_equal 2, david.projects.count
755
+ end
756
+
757
+ def test_count_with_counter_sql
758
+ developer = DeveloperWithCounterSQL.create(:name => 'tekin')
759
+ developer.project_ids = [projects(:active_record).id]
760
+ developer.save
761
+ developer.reload
762
+ assert_equal 1, developer.projects.count
763
+ end
764
+
765
+ uses_mocha 'mocking Post.transaction' do
766
+ def test_association_proxy_transaction_method_starts_transaction_in_association_class
767
+ Post.expects(:transaction)
768
+ Category.find(:first).posts.transaction do
769
+ # nothing
770
+ end
771
+ end
772
+ end
706
773
  end
@@ -135,6 +135,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
135
135
  assert_equal "Microsoft", Firm.find(:first).clients_like_ms_with_hash_conditions.first.name
136
136
  end
137
137
 
138
+ def test_finding_using_primary_key
139
+ assert_equal "Summit", Firm.find(:first).clients_using_primary_key.first.name
140
+ end
141
+
138
142
  def test_finding_using_sql
139
143
  firm = Firm.find(:first)
140
144
  first_client = firm.clients_using_sql.first
@@ -244,6 +248,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
244
248
  assert_equal 1, grouped_clients_of_firm1.size
245
249
  end
246
250
 
251
+ def test_find_scoped_grouped
252
+ assert_equal 1, companies(:first_firm).clients_grouped_by_firm_id.size
253
+ assert_equal 1, companies(:first_firm).clients_grouped_by_firm_id.length
254
+ assert_equal 2, companies(:first_firm).clients_grouped_by_name.size
255
+ assert_equal 2, companies(:first_firm).clients_grouped_by_name.length
256
+ end
257
+
247
258
  def test_adding
248
259
  force_signal37_to_load_all_clients_of_firm
249
260
  natural = Client.new("name" => "Natural Company")
@@ -380,7 +391,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
380
391
  company = companies(:first_firm)
381
392
  new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
382
393
  assert !company.clients_of_firm.loaded?
383
-
394
+
384
395
  assert_equal "Another Client", new_client.name
385
396
  assert new_client.new_record?
386
397
  assert_equal new_client, company.clients_of_firm.last
@@ -412,7 +423,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
412
423
  def test_build_many
413
424
  company = companies(:first_firm)
414
425
  new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
415
-
426
+
416
427
  assert_equal 2, new_clients.size
417
428
  company.name += '-changed'
418
429
  assert_queries(3) { assert company.save }
@@ -651,10 +662,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
651
662
 
652
663
  def test_creation_respects_hash_condition
653
664
  ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.build
654
-
665
+
655
666
  assert ms_client.save
656
667
  assert_equal 'Microsoft', ms_client.name
657
-
668
+
658
669
  another_ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.create
659
670
 
660
671
  assert !another_ms_client.new_record?
@@ -826,6 +837,29 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
826
837
  assert_equal [companies(:first_client).id, companies(:second_client).id], companies(:first_firm).client_ids
827
838
  end
828
839
 
840
+ def test_get_ids_for_loaded_associations
841
+ company = companies(:first_firm)
842
+ company.clients(true)
843
+ assert_queries(0) do
844
+ company.client_ids
845
+ company.client_ids
846
+ end
847
+ end
848
+
849
+ def test_get_ids_for_unloaded_associations_does_not_load_them
850
+ company = companies(:first_firm)
851
+ assert !company.clients.loaded?
852
+ assert_equal [companies(:first_client).id, companies(:second_client).id], company.client_ids
853
+ assert !company.clients.loaded?
854
+ end
855
+
856
+ def test_get_ids_for_unloaded_finder_sql_associations_loads_them
857
+ company = companies(:first_firm)
858
+ assert !company.clients_using_sql.loaded?
859
+ assert_equal [companies(:second_client).id], company.clients_using_sql_ids
860
+ assert company.clients_using_sql.loaded?
861
+ end
862
+
829
863
  def test_assign_ids
830
864
  firm = Firm.new("name" => "Apple")
831
865
  firm.client_ids = [companies(:first_client).id, companies(:second_client).id]
@@ -896,7 +930,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
896
930
  assert_equal 4, authors(:david).limited_comments.find(:all, :conditions => "comments.type = 'SpecialComment'", :limit => 9_000).length
897
931
  assert_equal 4, authors(:david).limited_comments.find_all_by_type('SpecialComment', :limit => 9_000).length
898
932
  end
899
-
933
+
900
934
  def test_find_all_include_over_the_same_table_for_through
901
935
  assert_equal 2, people(:michael).posts.find(:all, :include => :people).length
902
936
  end
@@ -933,13 +967,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
933
967
  def test_include_loads_collection_if_target_uses_finder_sql
934
968
  firm = companies(:first_firm)
935
969
  client = firm.clients_using_sql.first
936
-
970
+
937
971
  firm.reload
938
972
  assert ! firm.clients_using_sql.loaded?
939
973
  assert firm.clients_using_sql.include?(client)
940
974
  assert firm.clients_using_sql.loaded?
941
975
  end
942
-
976
+
943
977
 
944
978
  def test_include_returns_false_for_non_matching_record_to_verify_scoping
945
979
  firm = companies(:first_firm)
@@ -982,6 +1016,19 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
982
1016
  assert firm.clients.loaded?
983
1017
  end
984
1018
 
1019
+ def test_calling_first_or_last_on_existing_record_with_create_should_not_load_association
1020
+ firm = companies(:first_firm)
1021
+ firm.clients.create(:name => 'Foo')
1022
+ assert !firm.clients.loaded?
1023
+
1024
+ assert_queries 2 do
1025
+ firm.clients.first
1026
+ firm.clients.last
1027
+ end
1028
+
1029
+ assert !firm.clients.loaded?
1030
+ end
1031
+
985
1032
  def test_calling_first_or_last_on_new_record_should_not_run_queries
986
1033
  firm = Firm.new
987
1034
 
@@ -1031,4 +1078,24 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
1031
1078
  ActiveRecord::Base.store_full_sti_class = old
1032
1079
  end
1033
1080
 
1081
+ uses_mocha 'mocking Comment.transaction' do
1082
+ def test_association_proxy_transaction_method_starts_transaction_in_association_class
1083
+ Comment.expects(:transaction)
1084
+ Post.find(:first).comments.transaction do
1085
+ # nothing
1086
+ end
1087
+ end
1088
+ end
1089
+
1090
+ def test_sending_new_to_association_proxy_should_have_same_effect_as_calling_new
1091
+ client_association = companies(:first_firm).clients
1092
+ assert_equal client_association.new.attributes, client_association.send(:new).attributes
1093
+ end
1094
+
1095
+ def test_respond_to_private_class_methods
1096
+ client_association = companies(:first_firm).clients
1097
+ assert !client_association.respond_to?(:private_method)
1098
+ assert client_association.respond_to?(:private_method, true)
1099
+ end
1034
1100
  end
1101
+