activerecord 1.15.6 → 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 (185) hide show
  1. data/CHANGELOG +2454 -34
  2. data/README +1 -1
  3. data/RUNNING_UNIT_TESTS +3 -34
  4. data/Rakefile +98 -77
  5. data/install.rb +1 -1
  6. data/lib/active_record.rb +13 -22
  7. data/lib/active_record/aggregations.rb +38 -49
  8. data/lib/active_record/associations.rb +452 -333
  9. data/lib/active_record/associations/association_collection.rb +66 -20
  10. data/lib/active_record/associations/association_proxy.rb +9 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
  12. data/lib/active_record/associations/has_many_association.rb +21 -57
  13. data/lib/active_record/associations/has_many_through_association.rb +38 -18
  14. data/lib/active_record/associations/has_one_association.rb +30 -14
  15. data/lib/active_record/attribute_methods.rb +253 -0
  16. data/lib/active_record/base.rb +719 -494
  17. data/lib/active_record/calculations.rb +62 -63
  18. data/lib/active_record/callbacks.rb +57 -83
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
  30. data/lib/active_record/fixtures.rb +503 -113
  31. data/lib/active_record/locking/optimistic.rb +72 -34
  32. data/lib/active_record/migration.rb +80 -57
  33. data/lib/active_record/observer.rb +13 -10
  34. data/lib/active_record/query_cache.rb +16 -57
  35. data/lib/active_record/reflection.rb +35 -38
  36. data/lib/active_record/schema.rb +5 -5
  37. data/lib/active_record/schema_dumper.rb +35 -13
  38. data/lib/active_record/serialization.rb +98 -0
  39. data/lib/active_record/serializers/json_serializer.rb +71 -0
  40. data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
  41. data/lib/active_record/timestamp.rb +20 -21
  42. data/lib/active_record/transactions.rb +39 -43
  43. data/lib/active_record/validations.rb +256 -107
  44. data/lib/active_record/version.rb +3 -3
  45. data/lib/activerecord.rb +1 -0
  46. data/test/aaa_create_tables_test.rb +15 -2
  47. data/test/abstract_unit.rb +24 -17
  48. data/test/active_schema_test_mysql.rb +20 -8
  49. data/test/adapter_test.rb +23 -5
  50. data/test/adapter_test_sqlserver.rb +15 -1
  51. data/test/aggregations_test.rb +16 -1
  52. data/test/all.sh +2 -2
  53. data/test/associations/ar_joins_test.rb +0 -0
  54. data/test/associations/callbacks_test.rb +51 -30
  55. data/test/associations/cascaded_eager_loading_test.rb +1 -29
  56. data/test/associations/eager_singularization_test.rb +145 -0
  57. data/test/associations/eager_test.rb +42 -6
  58. data/test/associations/extension_test.rb +6 -1
  59. data/test/associations/inner_join_association_test.rb +88 -0
  60. data/test/associations/join_model_test.rb +47 -16
  61. data/test/associations_test.rb +449 -226
  62. data/test/attribute_methods_test.rb +97 -0
  63. data/test/base_test.rb +251 -105
  64. data/test/binary_test.rb +22 -27
  65. data/test/calculations_test.rb +37 -5
  66. data/test/callbacks_test.rb +23 -0
  67. data/test/connection_test_firebird.rb +2 -2
  68. data/test/connection_test_mysql.rb +30 -0
  69. data/test/connections/native_mysql/connection.rb +3 -0
  70. data/test/connections/native_sqlite/connection.rb +5 -14
  71. data/test/connections/native_sqlite3/connection.rb +5 -14
  72. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  73. data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
  74. data/test/datatype_test_postgresql.rb +178 -27
  75. data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
  76. data/test/defaults_test.rb +8 -1
  77. data/test/deprecated_finder_test.rb +7 -128
  78. data/test/finder_test.rb +192 -54
  79. data/test/fixtures/all/developers.yml +0 -0
  80. data/test/fixtures/all/people.csv +0 -0
  81. data/test/fixtures/all/tasks.yml +0 -0
  82. data/test/fixtures/author.rb +12 -5
  83. data/test/fixtures/binaries.yml +130 -435
  84. data/test/fixtures/category.rb +6 -0
  85. data/test/fixtures/company.rb +8 -1
  86. data/test/fixtures/computer.rb +1 -0
  87. data/test/fixtures/contact.rb +16 -0
  88. data/test/fixtures/customer.rb +2 -2
  89. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  90. data/test/fixtures/db_definitions/db2.sql +4 -0
  91. data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
  92. data/test/fixtures/db_definitions/firebird.sql +6 -0
  93. data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
  94. data/test/fixtures/db_definitions/frontbase.sql +5 -0
  95. data/test/fixtures/db_definitions/openbase.sql +41 -25
  96. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  97. data/test/fixtures/db_definitions/oracle.sql +5 -0
  98. data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
  99. data/test/fixtures/db_definitions/postgresql.sql +87 -58
  100. data/test/fixtures/db_definitions/postgresql2.sql +1 -2
  101. data/test/fixtures/db_definitions/schema.rb +280 -0
  102. data/test/fixtures/db_definitions/schema2.rb +11 -0
  103. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  104. data/test/fixtures/db_definitions/sqlite.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/sybase.sql +4 -0
  107. data/test/fixtures/developer.rb +10 -0
  108. data/test/fixtures/example.log +1 -0
  109. data/test/fixtures/flowers.jpg +0 -0
  110. data/test/fixtures/item.rb +7 -0
  111. data/test/fixtures/items.yml +4 -0
  112. data/test/fixtures/joke.rb +0 -3
  113. data/test/fixtures/matey.rb +4 -0
  114. data/test/fixtures/mateys.yml +4 -0
  115. data/test/fixtures/minimalistic.rb +2 -0
  116. data/test/fixtures/minimalistics.yml +2 -0
  117. data/test/fixtures/mixins.yml +2 -100
  118. data/test/fixtures/parrot.rb +13 -0
  119. data/test/fixtures/parrots.yml +27 -0
  120. data/test/fixtures/parrots_pirates.yml +7 -0
  121. data/test/fixtures/pirate.rb +5 -0
  122. data/test/fixtures/pirates.yml +9 -0
  123. data/test/fixtures/post.rb +1 -0
  124. data/test/fixtures/project.rb +3 -2
  125. data/test/fixtures/reserved_words/distinct.yml +5 -0
  126. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  127. data/test/fixtures/reserved_words/group.yml +14 -0
  128. data/test/fixtures/reserved_words/select.yml +8 -0
  129. data/test/fixtures/reserved_words/values.yml +7 -0
  130. data/test/fixtures/ship.rb +3 -0
  131. data/test/fixtures/ships.yml +5 -0
  132. data/test/fixtures/tagging.rb +4 -0
  133. data/test/fixtures/taggings.yml +8 -1
  134. data/test/fixtures/topic.rb +13 -1
  135. data/test/fixtures/treasure.rb +4 -0
  136. data/test/fixtures/treasures.yml +10 -0
  137. data/test/fixtures_test.rb +205 -24
  138. data/test/inheritance_test.rb +7 -1
  139. data/test/json_serialization_test.rb +180 -0
  140. data/test/lifecycle_test.rb +1 -1
  141. data/test/locking_test.rb +85 -2
  142. data/test/migration_test.rb +206 -40
  143. data/test/mixin_test.rb +13 -515
  144. data/test/pk_test.rb +3 -6
  145. data/test/query_cache_test.rb +104 -0
  146. data/test/reflection_test.rb +16 -0
  147. data/test/reserved_word_test_mysql.rb +177 -0
  148. data/test/schema_dumper_test.rb +38 -3
  149. data/test/serialization_test.rb +47 -0
  150. data/test/transactions_test.rb +74 -23
  151. data/test/unconnected_test.rb +1 -1
  152. data/test/validations_test.rb +322 -32
  153. data/test/xml_serialization_test.rb +121 -44
  154. metadata +48 -41
  155. data/examples/associations.rb +0 -87
  156. data/examples/shared_setup.rb +0 -15
  157. data/examples/validation.rb +0 -85
  158. data/lib/active_record/acts/list.rb +0 -256
  159. data/lib/active_record/acts/nested_set.rb +0 -211
  160. data/lib/active_record/acts/tree.rb +0 -96
  161. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
  162. data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
  163. data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
  164. data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
  165. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
  166. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
  167. data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
  168. data/lib/active_record/deprecated_associations.rb +0 -104
  169. data/lib/active_record/deprecated_finders.rb +0 -44
  170. data/lib/active_record/vendor/simple.rb +0 -693
  171. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  172. data/lib/active_record/wrappings.rb +0 -58
  173. data/test/connections/native_sqlserver/connection.rb +0 -23
  174. data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
  175. data/test/deprecated_associations_test.rb +0 -396
  176. data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
  177. data/test/fixtures/db_definitions/mysql.sql +0 -234
  178. data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
  179. data/test/fixtures/db_definitions/mysql2.sql +0 -5
  180. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
  181. data/test/fixtures/db_definitions/sqlserver.sql +0 -243
  182. data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
  183. data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
  184. data/test/fixtures/mixin.rb +0 -63
  185. data/test/mixin_nested_set_test.rb +0 -196
@@ -1,11 +1,9 @@
1
1
  require 'abstract_unit'
2
- require 'active_record/acts/list'
3
2
  require 'fixtures/post'
4
3
  require 'fixtures/comment'
5
4
  require 'fixtures/author'
6
5
  require 'fixtures/category'
7
6
  require 'fixtures/categorization'
8
- require 'fixtures/mixin'
9
7
  require 'fixtures/company'
10
8
  require 'fixtures/topic'
11
9
  require 'fixtures/reply'
@@ -53,16 +51,6 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
53
51
  assert_equal 5, authors[0].posts.size
54
52
  end
55
53
 
56
- def test_eager_association_loading_with_acts_as_tree
57
- roots = TreeMixin.find(:all, :include=>"children", :conditions=>"mixins.parent_id IS NULL", :order=>"mixins.id")
58
- assert_equal [mixins(:tree_1), mixins(:tree2_1), mixins(:tree3_1)], roots
59
- assert_no_queries do
60
- assert_equal 2, roots[0].children.size
61
- assert_equal 0, roots[1].children.size
62
- assert_equal 0, roots[2].children.size
63
- end
64
- end
65
-
66
54
  def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
67
55
  firms = Firm.find(:all, :include=>{:account=>{:firm=>:account}}, :order=>"companies.id")
68
56
  assert_equal 2, firms.size
@@ -73,7 +61,7 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
73
61
 
74
62
  def test_eager_association_loading_with_has_many_sti
75
63
  topics = Topic.find(:all, :include => :replies, :order => 'topics.id')
76
- assert_equal [topics(:first), topics(:second)], topics
64
+ assert_equal topics(:first, :second), topics
77
65
  assert_no_queries do
78
66
  assert_equal 1, topics[0].replies.size
79
67
  assert_equal 0, topics[1].replies.size
@@ -103,24 +91,8 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
103
91
  authors.first.posts.first.special_comments.first.post.very_special_comment
104
92
  end
105
93
  end
106
-
107
- def test_eager_association_loading_with_recursive_cascading_three_levels_has_many
108
- root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:children=>{:children=>:children}}, :order => 'mixins.id')
109
- assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.children.first.children.first.children.first }
110
- end
111
-
112
- def test_eager_association_loading_with_recursive_cascading_three_levels_has_one
113
- root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:first_child=>{:first_child=>:first_child}}, :order => 'mixins.id')
114
- assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.first_child.first_child.first_child }
115
- end
116
-
117
- def test_eager_association_loading_with_recursive_cascading_three_levels_belongs_to
118
- leaf_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:parent=>{:parent=>:parent}}, :order => 'mixins.id DESC')
119
- assert_equal mixins(:recursively_cascaded_tree_1), assert_no_queries { leaf_node.parent.parent.parent }
120
- end
121
94
  end
122
95
 
123
-
124
96
  require 'fixtures/vertex'
125
97
  require 'fixtures/edge'
126
98
  class CascadedEagerLoadingTest < Test::Unit::TestCase
@@ -0,0 +1,145 @@
1
+ require 'abstract_unit'
2
+
3
+ class Virus < ActiveRecord::Base
4
+ belongs_to :octopus
5
+ end
6
+ class Octopus < ActiveRecord::Base
7
+ has_one :virus
8
+ end
9
+ class Pass < ActiveRecord::Base
10
+ belongs_to :bus
11
+ end
12
+ class Bus < ActiveRecord::Base
13
+ has_many :passes
14
+ end
15
+ class Mess < ActiveRecord::Base
16
+ has_and_belongs_to_many :crises
17
+ end
18
+ class Crisis < ActiveRecord::Base
19
+ has_and_belongs_to_many :messes
20
+ has_many :analyses, :dependent => :destroy
21
+ has_many :successes, :through => :analyses
22
+ has_many :dresses, :dependent => :destroy
23
+ has_many :compresses, :through => :dresses
24
+ end
25
+ class Analysis < ActiveRecord::Base
26
+ belongs_to :crisis
27
+ belongs_to :success
28
+ end
29
+ class Success < ActiveRecord::Base
30
+ has_many :analyses, :dependent => :destroy
31
+ has_many :crises, :through => :analyses
32
+ end
33
+ class Dress < ActiveRecord::Base
34
+ belongs_to :crisis
35
+ has_many :compresses
36
+ end
37
+ class Compress < ActiveRecord::Base
38
+ belongs_to :dress
39
+ end
40
+
41
+
42
+ class EagerSingularizationTest < Test::Unit::TestCase
43
+
44
+ def setup
45
+ if ActiveRecord::Base.connection.supports_migrations?
46
+ ActiveRecord::Base.connection.create_table :viri do |t|
47
+ t.column :octopus_id, :integer
48
+ t.column :species, :string
49
+ end
50
+ ActiveRecord::Base.connection.create_table :octopi do |t|
51
+ t.column :species, :string
52
+ end
53
+ ActiveRecord::Base.connection.create_table :passes do |t|
54
+ t.column :bus_id, :integer
55
+ t.column :rides, :integer
56
+ end
57
+ ActiveRecord::Base.connection.create_table :buses do |t|
58
+ t.column :name, :string
59
+ end
60
+ ActiveRecord::Base.connection.create_table :crises_messes, :id => false do |t|
61
+ t.column :crisis_id, :integer
62
+ t.column :mess_id, :integer
63
+ end
64
+ ActiveRecord::Base.connection.create_table :messes do |t|
65
+ t.column :name, :string
66
+ end
67
+ ActiveRecord::Base.connection.create_table :crises do |t|
68
+ t.column :name, :string
69
+ end
70
+ ActiveRecord::Base.connection.create_table :successes do |t|
71
+ t.column :name, :string
72
+ end
73
+ ActiveRecord::Base.connection.create_table :analyses do |t|
74
+ t.column :crisis_id, :integer
75
+ t.column :success_id, :integer
76
+ end
77
+ ActiveRecord::Base.connection.create_table :dresses do |t|
78
+ t.column :crisis_id, :integer
79
+ end
80
+ ActiveRecord::Base.connection.create_table :compresses do |t|
81
+ t.column :dress_id, :integer
82
+ end
83
+ @have_tables = true
84
+ else
85
+ @have_tables = false
86
+ end
87
+ end
88
+
89
+ def teardown
90
+ ActiveRecord::Base.connection.drop_table :viri
91
+ ActiveRecord::Base.connection.drop_table :octopi
92
+ ActiveRecord::Base.connection.drop_table :passes
93
+ ActiveRecord::Base.connection.drop_table :buses
94
+ ActiveRecord::Base.connection.drop_table :crises_messes
95
+ ActiveRecord::Base.connection.drop_table :messes
96
+ ActiveRecord::Base.connection.drop_table :crises
97
+ ActiveRecord::Base.connection.drop_table :successes
98
+ ActiveRecord::Base.connection.drop_table :analyses
99
+ ActiveRecord::Base.connection.drop_table :dresses
100
+ ActiveRecord::Base.connection.drop_table :compresses
101
+ end
102
+
103
+ def test_eager_no_extra_singularization_belongs_to
104
+ return unless @have_tables
105
+ assert_nothing_raised do
106
+ Virus.find(:all, :include => :octopus)
107
+ end
108
+ end
109
+
110
+ def test_eager_no_extra_singularization_has_one
111
+ return unless @have_tables
112
+ assert_nothing_raised do
113
+ Octopus.find(:all, :include => :virus)
114
+ end
115
+ end
116
+
117
+ def test_eager_no_extra_singularization_has_many
118
+ return unless @have_tables
119
+ assert_nothing_raised do
120
+ Bus.find(:all, :include => :passes)
121
+ end
122
+ end
123
+
124
+ def test_eager_no_extra_singularization_has_and_belongs_to_many
125
+ return unless @have_tables
126
+ assert_nothing_raised do
127
+ Crisis.find(:all, :include => :messes)
128
+ Mess.find(:all, :include => :crises)
129
+ end
130
+ end
131
+
132
+ def test_eager_no_extra_singularization_has_many_through_belongs_to
133
+ return unless @have_tables
134
+ assert_nothing_raised do
135
+ Crisis.find(:all, :include => :successes)
136
+ end
137
+ end
138
+
139
+ def test_eager_no_extra_singularization_has_many_through_has_many
140
+ return unless @have_tables
141
+ assert_nothing_raised do
142
+ Crisis.find(:all, :include => :compresses)
143
+ end
144
+ end
145
+ end
@@ -37,6 +37,18 @@ class EagerAssociationTest < Test::Unit::TestCase
37
37
  end
38
38
  end
39
39
 
40
+ def test_with_two_tables_in_from_without_getting_double_quoted
41
+ posts = Post.find(:all,
42
+ :select => "posts.*",
43
+ :from => "authors, posts",
44
+ :include => :comments,
45
+ :conditions => "posts.author_id = authors.id",
46
+ :order => "posts.id"
47
+ )
48
+
49
+ assert_equal 2, posts.first.comments.size
50
+ end
51
+
40
52
  def test_loading_with_multiple_associations
41
53
  posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id")
42
54
  assert_equal 2, posts.first.comments.size
@@ -103,6 +115,11 @@ class EagerAssociationTest < Test::Unit::TestCase
103
115
  assert_equal [2], posts.collect { |p| p.id }
104
116
  end
105
117
 
118
+ def test_eager_association_loading_with_explicit_join
119
+ posts = Post.find(:all, :include => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id')
120
+ assert_equal 1, posts.length
121
+ end
122
+
106
123
  def test_eager_with_has_many_through
107
124
  posts_with_comments = people(:michael).posts.find(:all, :include => :comments)
108
125
  posts_with_author = people(:michael).posts.find(:all, :include => :author )
@@ -135,13 +152,21 @@ class EagerAssociationTest < Test::Unit::TestCase
135
152
  end
136
153
 
137
154
  def test_eager_with_has_many_and_limit_and_conditions
138
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
155
+ if current_adapter?(:OpenBaseAdapter)
156
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "FETCHBLOB(posts.body) = 'hello'", :order => "posts.id")
157
+ else
158
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
159
+ end
139
160
  assert_equal 2, posts.size
140
161
  assert_equal [4,5], posts.collect { |p| p.id }
141
162
  end
142
163
 
143
164
  def test_eager_with_has_many_and_limit_and_conditions_array
144
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
165
+ if current_adapter?(:OpenBaseAdapter)
166
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "FETCHBLOB(posts.body) = ?", 'hello' ], :order => "posts.id")
167
+ else
168
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
169
+ end
145
170
  assert_equal 2, posts.size
146
171
  assert_equal [4,5], posts.collect { |p| p.id }
147
172
  end
@@ -237,6 +262,15 @@ class EagerAssociationTest < Test::Unit::TestCase
237
262
  assert_equal count, posts.size
238
263
  end
239
264
  end
265
+
266
+ def test_eager_with_scoped_order_using_association_limiting_without_explicit_scope
267
+ posts_with_explicit_order = Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :order => 'posts.id DESC', :limit => 2)
268
+ posts_with_scoped_order = Post.with_scope(:find => {:order => 'posts.id DESC'}) do
269
+ Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :limit => 2)
270
+ end
271
+ assert_equal posts_with_explicit_order, posts_with_scoped_order
272
+ end
273
+
240
274
  def test_eager_association_loading_with_habtm
241
275
  posts = Post.find(:all, :include => :categories, :order => "posts.id")
242
276
  assert_equal 2, posts[0].categories.size
@@ -305,13 +339,13 @@ class EagerAssociationTest < Test::Unit::TestCase
305
339
  end
306
340
 
307
341
  def test_limited_eager_with_order
308
- assert_equal [posts(:thinking), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
309
- assert_equal [posts(:sti_post_and_comments), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
342
+ assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
343
+ assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
310
344
  end
311
345
 
312
346
  def test_limited_eager_with_multiple_order_columns
313
- assert_equal [posts(:thinking), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1)
314
- assert_equal [posts(:sti_post_and_comments), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
347
+ assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1)
348
+ assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
315
349
  end
316
350
 
317
351
  def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
@@ -399,6 +433,8 @@ class EagerAssociationTest < Test::Unit::TestCase
399
433
  def test_count_with_include
400
434
  if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
401
435
  assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
436
+ elsif current_adapter?(:OpenBaseAdapter)
437
+ assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15")
402
438
  else
403
439
  assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
404
440
  end
@@ -23,7 +23,12 @@ class AssociationsExtensionsTest < Test::Unit::TestCase
23
23
  assert_equal projects(:action_controller), developers(:david).projects_extended_by_name_twice.find_most_recent
24
24
  assert_equal projects(:active_record), developers(:david).projects_extended_by_name_twice.find_least_recent
25
25
  end
26
-
26
+
27
+ def test_named_extension_and_block_on_habtm
28
+ assert_equal projects(:action_controller), developers(:david).projects_extended_by_name_and_block.find_most_recent
29
+ assert_equal projects(:active_record), developers(:david).projects_extended_by_name_and_block.find_least_recent
30
+ end
31
+
27
32
  def test_marshalling_extensions
28
33
  david = developers(:david)
29
34
  assert_equal projects(:action_controller), david.projects.find_most_recent
@@ -0,0 +1,88 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/post'
3
+ require 'fixtures/comment'
4
+ require 'fixtures/author'
5
+ require 'fixtures/category'
6
+ require 'fixtures/categorization'
7
+
8
+ class InnerJoinAssociationTest < Test::Unit::TestCase
9
+ fixtures :authors, :posts, :comments, :categories, :categories_posts, :categorizations
10
+
11
+ def test_construct_finder_sql_creates_inner_joins
12
+ sql = Author.send(:construct_finder_sql, :joins => :posts)
13
+ assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql
14
+ end
15
+
16
+ def test_construct_finder_sql_cascades_inner_joins
17
+ sql = Author.send(:construct_finder_sql, :joins => {:posts => :comments})
18
+ assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql
19
+ assert_match /INNER JOIN `?comments`? ON `?comments`?.post_id = posts.id/, sql
20
+ end
21
+
22
+ def test_construct_finder_sql_inner_joins_through_associations
23
+ sql = Author.send(:construct_finder_sql, :joins => :categorized_posts)
24
+ assert_match /INNER JOIN `?categorizations`?.*INNER JOIN `?posts`?/, sql
25
+ end
26
+
27
+ def test_construct_finder_sql_applies_association_conditions
28
+ sql = Author.send(:construct_finder_sql, :joins => :categories_like_general, :conditions => "TERMINATING_MARKER")
29
+ assert_match /INNER JOIN `?categories`? ON.*AND.*`?General`?.*TERMINATING_MARKER/, sql
30
+ end
31
+
32
+ def test_construct_finder_sql_unpacks_nested_joins
33
+ sql = Author.send(:construct_finder_sql, :joins => {:posts => [[:comments]]})
34
+ assert_no_match /inner join.*inner join.*inner join/i, sql, "only two join clauses should be present"
35
+ assert_match /INNER JOIN `?posts`? ON `?posts`?.author_id = authors.id/, sql
36
+ assert_match /INNER JOIN `?comments`? ON `?comments`?.post_id = `?posts`?.id/, sql
37
+ end
38
+
39
+ def test_construct_finder_sql_ignores_empty_joins_hash
40
+ sql = Author.send(:construct_finder_sql, :joins => {})
41
+ assert_no_match /JOIN/i, sql
42
+ end
43
+
44
+ def test_construct_finder_sql_ignores_empty_joins_array
45
+ sql = Author.send(:construct_finder_sql, :joins => [])
46
+ assert_no_match /JOIN/i, sql
47
+ end
48
+
49
+ def test_find_with_implicit_inner_joins_honors_readonly_without_select
50
+ authors = Author.find(:all, :joins => :posts)
51
+ assert !authors.empty?, "expected authors to be non-empty"
52
+ assert authors.all? {|a| a.readonly? }, "expected all authors to be readonly"
53
+ end
54
+
55
+ def test_find_with_implicit_inner_joins_honors_readonly_with_select
56
+ authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
57
+ assert !authors.empty?, "expected authors to be non-empty"
58
+ assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
59
+ end
60
+
61
+ def test_find_with_implicit_inner_joins_honors_readonly_false
62
+ authors = Author.find(:all, :joins => :posts, :readonly => false)
63
+ assert !authors.empty?, "expected authors to be non-empty"
64
+ assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
65
+ end
66
+
67
+ def test_find_with_implicit_inner_joins_does_not_set_associations
68
+ authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
69
+ assert !authors.empty?, "expected authors to be non-empty"
70
+ assert authors.all? {|a| !a.send(:instance_variables).include?("@posts")}, "expected no authors to have the @posts association loaded"
71
+ end
72
+
73
+ def test_count_honors_implicit_inner_joins
74
+ real_count = Author.find(:all).sum{|a| a.posts.count }
75
+ assert_equal real_count, Author.count(:joins => :posts), "plain inner join count should match the number of referenced posts records"
76
+ end
77
+
78
+ def test_calculate_honors_implicit_inner_joins
79
+ real_count = Author.find(:all).sum{|a| a.posts.count }
80
+ assert_equal real_count, Author.calculate(:count, 'authors.id', :joins => :posts), "plain inner join count should match the number of referenced posts records"
81
+ end
82
+
83
+ def test_calculate_honors_implicit_inner_joins_and_distinct_and_conditions
84
+ real_count = Author.find(:all).select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
85
+ authors_with_welcoming_post_titles = Author.calculate(:count, 'authors.id', :joins => :posts, :distinct => true, :conditions => "posts.title like 'Welcome%'")
86
+ assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'"
87
+ end
88
+ end
@@ -2,6 +2,7 @@ require 'abstract_unit'
2
2
  require 'fixtures/tag'
3
3
  require 'fixtures/tagging'
4
4
  require 'fixtures/post'
5
+ require 'fixtures/item'
5
6
  require 'fixtures/comment'
6
7
  require 'fixtures/author'
7
8
  require 'fixtures/category'
@@ -13,7 +14,7 @@ require 'fixtures/citation'
13
14
 
14
15
  class AssociationsJoinModelTest < Test::Unit::TestCase
15
16
  self.use_transactional_fixtures = false
16
- fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :vertices, :books
17
+ fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :vertices, :items, :books
17
18
 
18
19
  def test_has_many
19
20
  assert authors(:david).categories.include?(categories(:general))
@@ -36,8 +37,8 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
36
37
  author = authors(:mary)
37
38
  assert !authors(:mary).unique_categorized_posts.loaded?
38
39
  assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
39
- assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count(:title, {}) }
40
- assert_queries(1) { assert_equal 0, author.unique_categorized_posts.count(:title, { :conditions => "title is NULL" }) }
40
+ assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count(:title) }
41
+ assert_queries(1) { assert_equal 0, author.unique_categorized_posts.count(:title, :conditions => "title is NULL") }
41
42
  assert !authors(:mary).unique_categorized_posts.loaded?
42
43
  end
43
44
 
@@ -240,7 +241,15 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
240
241
  assert_equal tagging, post.tagging
241
242
  end
242
243
  end
243
-
244
+
245
+ def test_include_polymorphic_has_one_defined_in_abstract_parent
246
+ item = Item.find_by_id(items(:dvd).id, :include => :tagging)
247
+ tagging = taggings(:godfather)
248
+ assert_no_queries do
249
+ assert_equal tagging, item.tagging
250
+ end
251
+ end
252
+
244
253
  def test_include_polymorphic_has_many_through
245
254
  posts = Post.find(:all, :order => 'posts.id')
246
255
  posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
@@ -275,10 +284,10 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
275
284
  assert_equal categories(:general), authors(:david).categories.find(:first, :conditions => "categories.name = 'General'")
276
285
  assert_equal nil, authors(:david).categories.find(:first, :conditions => "categories.name = 'Technology'")
277
286
  end
278
-
287
+
279
288
  def test_has_many_class_methods_called_by_method_missing
280
289
  assert_equal categories(:general), authors(:david).categories.find_all_by_name('General').first
281
- # assert_equal nil, authors(:david).categories.find_by_name('Technology')
290
+ assert_equal nil, authors(:david).categories.find_by_name('Technology')
282
291
  end
283
292
 
284
293
  def test_has_many_going_through_join_model_with_custom_foreign_key
@@ -305,20 +314,20 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
305
314
 
306
315
  def test_has_many_polymorphic
307
316
  assert_raises ActiveRecord::HasManyThroughAssociationPolymorphicError do
308
- assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggables
317
+ assert_equal posts(:welcome, :thinking), tags(:general).taggables
309
318
  end
310
319
  assert_raises ActiveRecord::EagerLoadPolymorphicError do
311
- assert_equal [posts(:welcome), posts(:thinking)], tags(:general).taggings.find(:all, :include => :taggable)
320
+ assert_equal posts(:welcome, :thinking), tags(:general).taggings.find(:all, :include => :taggable)
312
321
  end
313
322
  end
314
323
 
315
324
  def test_has_many_polymorphic_with_source_type
316
- assert_equal [posts(:welcome), posts(:thinking)], tags(:general).tagged_posts
325
+ assert_equal posts(:welcome, :thinking), tags(:general).tagged_posts
317
326
  end
318
327
 
319
328
  def test_eager_has_many_polymorphic_with_source_type
320
329
  tag_with_include = Tag.find(tags(:general).id, :include => :tagged_posts)
321
- desired = [posts(:welcome), posts(:thinking)]
330
+ desired = posts(:welcome, :thinking)
322
331
  assert_no_queries do
323
332
  assert_equal desired, tag_with_include.tagged_posts
324
333
  end
@@ -350,12 +359,12 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
350
359
  end
351
360
 
352
361
  def test_has_many_through_polymorphic_has_many
353
- assert_equal [taggings(:welcome_general), taggings(:thinking_general)], authors(:david).taggings.uniq.sort_by { |t| t.id }
362
+ assert_equal taggings(:welcome_general, :thinking_general), authors(:david).taggings.uniq.sort_by { |t| t.id }
354
363
  end
355
364
 
356
365
  def test_include_has_many_through_polymorphic_has_many
357
366
  author = Author.find_by_id(authors(:david).id, :include => :taggings)
358
- expected_taggings = [taggings(:welcome_general), taggings(:thinking_general)]
367
+ expected_taggings = taggings(:welcome_general, :thinking_general)
359
368
  assert_no_queries do
360
369
  assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id }
361
370
  end
@@ -401,6 +410,12 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
401
410
  authors(:david).author_favorites.create :favorite_author => new_author
402
411
  assert_equal new_author, authors(:david).reload.favorite_authors.first
403
412
  end
413
+
414
+ def test_has_many_through_uses_conditions_specified_on_the_has_many_association
415
+ author = Author.find(:first)
416
+ assert !author.comments.blank?
417
+ assert author.nonexistant_comments.blank?
418
+ end
404
419
 
405
420
  def test_has_many_through_uses_correct_attributes
406
421
  assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"]
@@ -410,6 +425,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
410
425
  assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags << tags(:general).clone }
411
426
  assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).clone.tags << tags(:general) }
412
427
  assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags.build }
428
+ assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags.new }
413
429
  end
414
430
 
415
431
  def test_create_associate_when_adding_to_has_many_through
@@ -424,7 +440,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
424
440
  assert_equal(count + 1, post_thinking.tags.size)
425
441
  assert_equal(count + 1, post_thinking.tags(true).size)
426
442
 
427
- assert_nothing_raised { post_thinking.tags.create!(:name => 'foo') }
443
+ assert_kind_of Tag, post_thinking.tags.create!(:name => 'foo')
428
444
  assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
429
445
  message = "Expected a Tag in tags collection, got #{wrong.class}.")
430
446
  assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
@@ -444,6 +460,21 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
444
460
  assert_nothing_raised { vertices(:vertex_1).sinks << vertices(:vertex_5) }
445
461
  end
446
462
 
463
+ def test_has_many_through_collection_size_doesnt_load_target_if_not_loaded
464
+ author = authors(:david)
465
+ assert_equal 9, author.comments.size
466
+ assert !author.comments.loaded?
467
+ end
468
+
469
+ uses_mocha('has_many_through_collection_size_uses_counter_cache_if_it_exists') do
470
+ def test_has_many_through_collection_size_uses_counter_cache_if_it_exists
471
+ author = authors(:david)
472
+ author.stubs(:read_attribute).with('comments_count').returns(100)
473
+ assert_equal 100, author.comments.size
474
+ assert !author.comments.loaded?
475
+ end
476
+ end
477
+
447
478
  def test_adding_junk_to_has_many_through_should_raise_type_mismatch
448
479
  assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags << "Uhh what now?" }
449
480
  end
@@ -452,8 +483,8 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
452
483
  tags = posts(:thinking).tags
453
484
  assert_equal tags, posts(:thinking).tags.push(tags(:general))
454
485
  end
455
-
456
- def test_delete_associate_when_deleting_from_has_many_through_with_non_standard_id
486
+
487
+ def test_delete_associate_when_deleting_from_has_many_through_with_nonstandard_id
457
488
  count = books(:awdr).references.count
458
489
  references_before = books(:awdr).references
459
490
  book = Book.create!(:name => 'Getting Real')
@@ -466,7 +497,7 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
466
497
  assert_equal(count, book_awdr.references(true).size)
467
498
  assert_equal(references_before.sort, book_awdr.references.sort)
468
499
  end
469
-
500
+
470
501
  def test_delete_associate_when_deleting_from_has_many_through
471
502
  count = posts(:thinking).tags.count
472
503
  tags_before = posts(:thinking).tags