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
@@ -1,8 +1,8 @@
1
1
  module ActiveRecord
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 13
5
- TINY = 2
4
+ MINOR = 14
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/rakefile CHANGED
@@ -27,7 +27,7 @@ task :default => [ :test_mysql, :test_sqlite, :test_postgresql ]
27
27
 
28
28
  # Run the unit tests
29
29
 
30
- for adapter in %w( mysql postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oci )
30
+ for adapter in %w( mysql postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oracle sybase openbase )
31
31
  Rake::TestTask.new("test_#{adapter}") { |t|
32
32
  t.libs << "test" << "test/connections/native_#{adapter}"
33
33
  t.pattern = "test/*_test{,_#{adapter}}.rb"
@@ -59,7 +59,7 @@ task :build_postgresql_databases do
59
59
  %x( createdb activerecord_unittest )
60
60
  %x( createdb activerecord_unittest2 )
61
61
  %x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql.sql')} )
62
- %x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} )
62
+ %x( psql activerecord_unittest2 -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} )
63
63
  end
64
64
 
65
65
  desc 'Drop the PostgreSQL test databases'
@@ -76,7 +76,7 @@ task :rebuild_postgresql_databases => [:drop_postgresql_databases, :build_postgr
76
76
  Rake::RDocTask.new { |rdoc|
77
77
  rdoc.rdoc_dir = 'doc'
78
78
  rdoc.title = "Active Record -- Object-relation mapping put on rails"
79
- rdoc.options << '--line-numbers --inline-source --accessor cattr_accessor=object'
79
+ rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
80
80
  rdoc.template = "#{ENV['template']}.rb" if ENV['template']
81
81
  rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
82
82
  rdoc.rdoc_files.include('lib/**/*.rb')
@@ -106,7 +106,7 @@ spec = Gem::Specification.new do |s|
106
106
  s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
107
107
  end
108
108
 
109
- s.add_dependency('activesupport', '= 1.2.5' + PKG_BUILD)
109
+ s.add_dependency('activesupport', '= 1.3.0' + PKG_BUILD)
110
110
 
111
111
  s.files.delete "test/fixtures/fixture_database.sqlite"
112
112
  s.files.delete "test/fixtures/fixture_database_2.sqlite"
@@ -1,7 +1,9 @@
1
1
  # The filename begins with "aaa" to ensure this is the first test.
2
2
  require 'abstract_unit'
3
3
 
4
- class CreateTablesTest < Test::Unit::TestCase
4
+ class AAACreateTablesTest < Test::Unit::TestCase
5
+ self.use_transactional_fixtures = false
6
+
5
7
  def setup
6
8
  @base_path = "#{File.dirname(__FILE__)}/fixtures/db_definitions"
7
9
  end
@@ -10,6 +12,11 @@ class CreateTablesTest < Test::Unit::TestCase
10
12
  recreate ActiveRecord::Base
11
13
  assert true
12
14
  end
15
+
16
+ def test_load_schema
17
+ eval(File.read("#{File.dirname(__FILE__)}/fixtures/db_definitions/schema.rb"))
18
+ assert true
19
+ end
13
20
 
14
21
  def test_drop_and_create_courses_table
15
22
  recreate Course, '2'
@@ -25,11 +32,23 @@ class CreateTablesTest < Test::Unit::TestCase
25
32
  end
26
33
 
27
34
  def execute_sql_file(path, connection)
28
- File.read(path).split(';').each_with_index do |sql, i|
29
- begin
30
- connection.execute("\n\n-- statement ##{i}\n#{sql}\n") unless sql.blank?
31
- rescue ActiveRecord::StatementInvalid
32
- #$stderr.puts "warning: #{$!}"
35
+ # OpenBase has a different format for sql files
36
+ if current_adapter?(:OpenBaseAdapter) then
37
+ File.read(path).split("go").each_with_index do |sql, i|
38
+ begin
39
+ # OpenBase does not support comments embedded in sql
40
+ connection.execute(sql,"SQL statement ##{i}") unless sql.blank?
41
+ rescue ActiveRecord::StatementInvalid
42
+ #$stderr.puts "warning: #{$!}"
43
+ end
44
+ end
45
+ else
46
+ File.read(path).split(';').each_with_index do |sql, i|
47
+ begin
48
+ connection.execute("\n\n-- statement ##{i}\n#{sql}\n") unless sql.blank?
49
+ rescue ActiveRecord::StatementInvalid
50
+ #$stderr.puts "warning: #{$!}"
51
+ end
33
52
  end
34
53
  end
35
54
  end
@@ -16,7 +16,36 @@ class Test::Unit::TestCase #:nodoc:
16
16
  self.use_transactional_fixtures = (ENV['AR_NO_TX_FIXTURES'] != "yes")
17
17
 
18
18
  def create_fixtures(*table_names, &block)
19
- Fixtures.create_fixtures(File.dirname(__FILE__) + "/fixtures/", table_names, &block)
19
+ Fixtures.create_fixtures(File.dirname(__FILE__) + "/fixtures/", table_names, {}, &block)
20
+ end
21
+
22
+ def assert_date_from_db(expected, actual, message = nil)
23
+ # SQL Server doesn't have a separate column type just for dates,
24
+ # so the time is in the string and incorrectly formatted
25
+ if current_adapter?(:SQLServerAdapter)
26
+ assert_equal expected.strftime("%Y/%m/%d 00:00:00"), actual.strftime("%Y/%m/%d 00:00:00")
27
+ elsif current_adapter?(:SybaseAdapter)
28
+ assert_equal expected.to_s, actual.to_date.to_s, message
29
+ else
30
+ assert_equal expected.to_s, actual.to_s, message
31
+ end
32
+ end
33
+
34
+ def assert_queries(num = 1)
35
+ ActiveRecord::Base.connection.class.class_eval do
36
+ self.query_count = 0
37
+ alias_method :execute, :execute_with_query_counting
38
+ end
39
+ yield
40
+ ensure
41
+ ActiveRecord::Base.connection.class.class_eval do
42
+ alias_method :execute, :execute_without_query_counting
43
+ end
44
+ assert_equal num, ActiveRecord::Base.connection.query_count, "#{ActiveRecord::Base.connection.query_count} instead of #{num} queries were executed."
45
+ end
46
+
47
+ def assert_no_queries(&block)
48
+ assert_queries(0, &block)
20
49
  end
21
50
  end
22
51
 
@@ -25,5 +54,14 @@ def current_adapter?(type)
25
54
  ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters.const_get(type))
26
55
  end
27
56
 
57
+ ActiveRecord::Base.connection.class.class_eval do
58
+ cattr_accessor :query_count
59
+ alias_method :execute_without_query_counting, :execute
60
+ def execute_with_query_counting(sql, name = nil)
61
+ self.query_count += 1
62
+ execute_without_query_counting(sql, name)
63
+ end
64
+ end
65
+
28
66
  #ActiveRecord::Base.logger = Logger.new(STDOUT)
29
67
  #ActiveRecord::Base.colorize_logging = false
@@ -18,14 +18,18 @@ class AdapterTest < Test::Unit::TestCase
18
18
  end
19
19
 
20
20
  def test_indexes
21
+ idx_name = "accounts_idx"
22
+
21
23
  if @connection.respond_to?(:indexes)
22
24
  indexes = @connection.indexes("accounts")
23
25
  assert indexes.empty?
24
26
 
25
- @connection.add_index :accounts, :firm_id
27
+ @connection.add_index :accounts, :firm_id, :name => idx_name
26
28
  indexes = @connection.indexes("accounts")
27
29
  assert_equal "accounts", indexes.first.table
28
- assert_equal "accounts_firm_id_index", indexes.first.name
30
+ # OpenBase does not have the concept of a named index
31
+ # Indexes are merely properties of columns.
32
+ assert_equal idx_name, indexes.first.name unless current_adapter?(:OpenBaseAdapter)
29
33
  assert !indexes.first.unique
30
34
  assert_equal ["firm_id"], indexes.first.columns
31
35
  else
@@ -33,7 +37,29 @@ class AdapterTest < Test::Unit::TestCase
33
37
  end
34
38
 
35
39
  ensure
36
- @connection.remove_index :accounts, :firm_id rescue nil
40
+ @connection.remove_index(:accounts, :name => idx_name) rescue nil
41
+ end
42
+
43
+ def test_current_database
44
+ if @connection.respond_to?(:current_database)
45
+ assert_equal "activerecord_unittest", @connection.current_database
46
+ end
47
+ end
48
+
49
+ def test_table_alias
50
+ def @connection.test_table_alias_length() 10; end
51
+ class << @connection
52
+ alias_method :old_table_alias_length, :table_alias_length
53
+ alias_method :table_alias_length, :test_table_alias_length
54
+ end
55
+
56
+ assert_equal 'posts', @connection.table_alias_for('posts')
57
+ assert_equal 'posts_comm', @connection.table_alias_for('posts_comments')
58
+ assert_equal 'dbo_posts', @connection.table_alias_for('dbo.posts')
59
+
60
+ class << @connection
61
+ alias_method :table_alias_length, :old_table_alias_length
62
+ end
37
63
  end
38
64
 
39
65
  # test resetting sequences in odd tables in postgreSQL
@@ -52,7 +78,8 @@ class AdapterTest < Test::Unit::TestCase
52
78
 
53
79
  sub = Subscriber.new(:name => 'robert drake')
54
80
  sub.id = 'bob drake'
55
- assert sub.save!
81
+ assert_nothing_raised { sub.save! }
56
82
  end
57
83
  end
84
+
58
85
  end
@@ -0,0 +1,106 @@
1
+ require 'abstract_unit'
2
+ require 'active_record/acts/list'
3
+ require 'fixtures/post'
4
+ require 'fixtures/comment'
5
+ require 'fixtures/author'
6
+ require 'fixtures/category'
7
+ require 'fixtures/categorization'
8
+ require 'fixtures/mixin'
9
+ require 'fixtures/company'
10
+ require 'fixtures/topic'
11
+ require 'fixtures/reply'
12
+
13
+ class CascadedEagerLoadingTest < Test::Unit::TestCase
14
+ fixtures :authors, :mixins, :companies, :posts, :categorizations, :topics
15
+
16
+ def test_eager_association_loading_with_cascaded_two_levels
17
+ authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
18
+ assert_equal 2, authors.size
19
+ assert_equal 5, authors[0].posts.size
20
+ assert_equal 1, authors[1].posts.size
21
+ assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
22
+ end
23
+
24
+ def test_eager_association_loading_with_cascaded_two_levels_and_one_level
25
+ authors = Author.find(:all, :include=>[{:posts=>:comments}, :categorizations], :order=>"authors.id")
26
+ assert_equal 2, authors.size
27
+ assert_equal 5, authors[0].posts.size
28
+ assert_equal 1, authors[1].posts.size
29
+ assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
30
+ assert_equal 1, authors[0].categorizations.size
31
+ assert_equal 1, authors[1].categorizations.size
32
+ end
33
+
34
+ def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
35
+ authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id")
36
+ assert_equal 2, authors.size
37
+ assert_equal 5, authors[0].posts.size
38
+ assert_equal 1, authors[1].posts.size
39
+ assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
40
+ end
41
+
42
+ def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference
43
+ authors = Author.find(:all, :include=>{:posts=>[:comments, :author]}, :order=>"authors.id")
44
+ assert_equal 2, authors.size
45
+ assert_equal 5, authors[0].posts.size
46
+ assert_equal authors(:david).name, authors[0].name
47
+ assert_equal [authors(:david).name], authors[0].posts.collect{|post| post.author.name}.uniq
48
+ end
49
+
50
+ def test_eager_association_loading_with_cascaded_two_levels_with_condition
51
+ authors = Author.find(:all, :include=>{:posts=>:comments}, :conditions=>"authors.id=1", :order=>"authors.id")
52
+ assert_equal 1, authors.size
53
+ assert_equal 5, authors[0].posts.size
54
+ end
55
+
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
+ def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
67
+ firms = Firm.find(:all, :include=>{:account=>{:firm=>:account}}, :order=>"companies.id")
68
+ assert_equal 2, firms.size
69
+ assert_equal firms.first.account, firms.first.account.firm.account
70
+ assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
71
+ assert_equal companies(:first_firm).account.firm.account, assert_no_queries { firms.first.account.firm.account }
72
+ end
73
+
74
+ def test_eager_association_loading_with_has_many_sti
75
+ topics = Topic.find(:all, :include => :replies, :order => 'topics.id')
76
+ assert_equal [topics(:first), topics(:second)], topics
77
+ assert_no_queries do
78
+ assert_equal 1, topics[0].replies.size
79
+ assert_equal 0, topics[1].replies.size
80
+ end
81
+ end
82
+
83
+ def test_eager_association_loading_with_belongs_to_sti
84
+ replies = Reply.find(:all, :include => :topic, :order => 'topics.id')
85
+ assert_equal [topics(:second)], replies
86
+ assert_equal topics(:first), assert_no_queries { replies.first.topic }
87
+ end
88
+
89
+ def test_eager_association_loading_with_multiple_stis_and_order
90
+ author = Author.find(:first, :include => { :posts => [ :special_comments , :very_special_comment ] }, :order => 'authors.name, comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
91
+ assert_equal authors(:david), author
92
+ assert_no_queries do
93
+ author.posts.first.special_comments
94
+ author.posts.first.very_special_comment
95
+ end
96
+ end
97
+
98
+ def test_eager_association_loading_of_stis_with_multiple_references
99
+ authors = Author.find(:all, :include => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
100
+ assert_equal [authors(:david)], authors
101
+ assert_no_queries do
102
+ authors.first.posts.first.special_comments.first.post.special_comments
103
+ authors.first.posts.first.special_comments.first.post.very_special_comment
104
+ end
105
+ end
106
+ end
@@ -4,10 +4,12 @@ require 'fixtures/comment'
4
4
  require 'fixtures/author'
5
5
  require 'fixtures/category'
6
6
  require 'fixtures/company'
7
+ require 'fixtures/person'
8
+ require 'fixtures/reader'
7
9
 
8
10
  class EagerAssociationTest < Test::Unit::TestCase
9
11
  fixtures :posts, :comments, :authors, :categories, :categories_posts,
10
- :companies, :accounts
12
+ :companies, :accounts, :tags, :people, :readers
11
13
 
12
14
  def test_loading_with_one_association
13
15
  posts = Post.find(:all, :include => :comments)
@@ -90,17 +92,26 @@ class EagerAssociationTest < Test::Unit::TestCase
90
92
  end
91
93
 
92
94
  def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
93
- posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1)
95
+ posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :order => 'posts.id')
94
96
  assert_equal 1, posts.length
95
- assert_equal [4], posts.collect { |p| p.id }
97
+ assert_equal [1], posts.collect { |p| p.id }
96
98
  end
97
99
 
98
100
  def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations
99
- posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :offset => 1)
100
- assert_equal 0, posts.length
101
- assert_equal [], posts
101
+ posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id')
102
+ assert_equal 1, posts.length
103
+ assert_equal [2], posts.collect { |p| p.id }
102
104
  end
103
105
 
106
+ def test_eager_with_has_many_through
107
+ posts_with_comments = people(:michael).posts.find(:all, :include => :comments )
108
+ posts_with_author = people(:michael).posts.find(:all, :include => :author )
109
+ posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ])
110
+ assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size }
111
+ assert_equal authors(:david), assert_no_queries { posts_with_author.first.author }
112
+ assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
113
+ end
114
+
104
115
  def test_eager_with_has_many_and_limit
105
116
  posts = Post.find(:all, :order => 'posts.id asc', :include => [ :author, :comments ], :limit => 2)
106
117
  assert_equal 2, posts.size
@@ -120,9 +131,11 @@ class EagerAssociationTest < Test::Unit::TestCase
120
131
  end
121
132
 
122
133
  def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
123
- assert_raises(ArgumentError) do
124
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
125
- end
134
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
135
+ assert_equal 2, posts.size
136
+
137
+ count = Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
138
+ assert_equal count, posts.size
126
139
  end
127
140
 
128
141
  def test_eager_with_has_many_and_limit_with_no_results
@@ -141,13 +154,19 @@ class EagerAssociationTest < Test::Unit::TestCase
141
154
  end
142
155
 
143
156
  def test_eager_with_has_many_and_limit_and_conditions_on_the_eagers
144
- assert_raises(ArgumentError) do
145
- posts = authors(:david).posts.find(:all,
146
- :include => :comments,
147
- :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
148
- :limit => 2
149
- )
150
- end
157
+ posts = authors(:david).posts.find(:all,
158
+ :include => :comments,
159
+ :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
160
+ :limit => 2
161
+ )
162
+ assert_equal 2, posts.size
163
+
164
+ count = Post.count(
165
+ :include => [ :comments, :author ],
166
+ :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
167
+ :limit => 2
168
+ )
169
+ assert_equal count, posts.size
151
170
  end
152
171
 
153
172
  def test_eager_association_loading_with_habtm
@@ -205,7 +224,57 @@ class EagerAssociationTest < Test::Unit::TestCase
205
224
  post = Post.find(6, :include=>[ :monkeys, :elephants ])
206
225
  }
207
226
  end
227
+
228
+ def find_all_ordered(className, include=nil)
229
+ className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :include=>include)
230
+ end
208
231
 
232
+ def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
233
+ # Eager includes of has many and habtm associations aren't necessarily sorted in the same way
234
+ def assert_equal_after_sort(item1, item2, item3 = nil)
235
+ assert_equal(item1.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id})
236
+ assert_equal(item3.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) if item3
237
+ end
238
+ # Test regular association, association with conditions, association with
239
+ # STI, and association with conditions assured not to be true
240
+ post_types = [:posts, :hello_posts, :special_posts, :nonexistent_posts]
241
+ # test both has_many and has_and_belongs_to_many
242
+ [Author, Category].each do |className|
243
+ d1 = find_all_ordered(className)
244
+ # test including all post types at once
245
+ d2 = find_all_ordered(className, post_types)
246
+ d1.each_index do |i|
247
+ assert_equal(d1[i], d2[i])
248
+ assert_equal_after_sort(d1[i].posts, d2[i].posts)
249
+ post_types[1..-1].each do |post_type|
250
+ # test including post_types together
251
+ d3 = find_all_ordered(className, [:posts, post_type])
252
+ assert_equal(d1[i], d3[i])
253
+ assert_equal_after_sort(d1[i].posts, d3[i].posts)
254
+ assert_equal_after_sort(d1[i].send(post_type), d2[i].send(post_type), d3[i].send(post_type))
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ def test_eager_with_multiple_associations_with_same_table_has_one
261
+ d1 = find_all_ordered(Firm)
262
+ d2 = find_all_ordered(Firm, :account)
263
+ d1.each_index do |i|
264
+ assert_equal(d1[i], d2[i])
265
+ assert_equal(d1[i].account, d2[i].account)
266
+ end
267
+ end
268
+
269
+ def test_eager_with_multiple_associations_with_same_table_belongs_to
270
+ firm_types = [:firm, :firm_with_basic_id, :firm_with_other_name, :firm_with_condition]
271
+ d1 = find_all_ordered(Client)
272
+ d2 = find_all_ordered(Client, firm_types)
273
+ d1.each_index do |i|
274
+ assert_equal(d1[i], d2[i])
275
+ firm_types.each { |type| assert_equal(d1[i].send(type), d2[i].send(type)) }
276
+ end
277
+ end
209
278
  def test_eager_with_valid_association_as_string_not_symbol
210
279
  assert_nothing_raised { Post.find(:all, :include => 'comments') }
211
280
  end