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,4 +1,5 @@
1
1
  $:.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ $:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib')
2
3
 
3
4
  require 'config'
4
5
  require 'test/unit'
@@ -45,3 +46,17 @@ end
45
46
  class << ActiveRecord::Base
46
47
  public :with_scope, :with_exclusive_scope
47
48
  end
49
+
50
+ unless ENV['FIXTURE_DEBUG']
51
+ module Test #:nodoc:
52
+ module Unit #:nodoc:
53
+ class << TestCase #:nodoc:
54
+ def try_to_load_dependency_with_silence(*args)
55
+ ActiveRecord::Base.logger.silence { try_to_load_dependency_without_silence(*args)}
56
+ end
57
+
58
+ alias_method_chain :try_to_load_dependency, :silence
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,41 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+ require 'models/reply'
4
+
5
+ class ActiveRecordI18nTests < Test::Unit::TestCase
6
+
7
+ def setup
8
+ I18n.backend = I18n::Backend::Simple.new
9
+ end
10
+
11
+ def test_translated_model_attributes
12
+ I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } }
13
+ assert_equal 'topic title attribute', Topic.human_attribute_name('title')
14
+ end
15
+
16
+ def test_translated_model_attributes_with_sti
17
+ I18n.backend.store_translations 'en', :activerecord => {:attributes => {:reply => {:title => 'reply title attribute'} } }
18
+ assert_equal 'reply title attribute', Reply.human_attribute_name('title')
19
+ end
20
+
21
+ def test_translated_model_attributes_with_sti_fallback
22
+ I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } }
23
+ assert_equal 'topic title attribute', Reply.human_attribute_name('title')
24
+ end
25
+
26
+ def test_translated_model_names
27
+ I18n.backend.store_translations 'en', :activerecord => {:models => {:topic => 'topic model'} }
28
+ assert_equal 'topic model', Topic.human_name
29
+ end
30
+
31
+ def test_translated_model_names_with_sti
32
+ I18n.backend.store_translations 'en', :activerecord => {:models => {:reply => 'reply model'} }
33
+ assert_equal 'reply model', Reply.human_name
34
+ end
35
+
36
+ def test_translated_model_names_with_sti_fallback
37
+ I18n.backend.store_translations 'en', :activerecord => {:models => {:topic => 'topic model'} }
38
+ assert_equal 'topic model', Reply.human_name
39
+ end
40
+ end
41
+
@@ -59,13 +59,13 @@ class InheritanceTest < ActiveRecord::TestCase
59
59
 
60
60
  def test_a_bad_type_column
61
61
  #SQLServer need to turn Identity Insert On before manually inserting into the Identity column
62
- if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
62
+ if current_adapter?(:SybaseAdapter)
63
63
  Company.connection.execute "SET IDENTITY_INSERT companies ON"
64
64
  end
65
65
  Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
66
66
 
67
67
  #We then need to turn it back Off before continuing.
68
- if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
68
+ if current_adapter?(:SybaseAdapter)
69
69
  Company.connection.execute "SET IDENTITY_INSERT companies OFF"
70
70
  end
71
71
  assert_raises(ActiveRecord::SubclassNotFound) { Company.find(100) }
File without changes
@@ -200,9 +200,9 @@ end
200
200
  # blocks, so separate script called by Kernel#system is needed.
201
201
  # (See exec vs. async_exec in the PostgreSQL adapter.)
202
202
 
203
- # TODO: The SQL Server, Sybase, and OpenBase adapters currently have no support for pessimistic locking
203
+ # TODO: The Sybase, and OpenBase adapters currently have no support for pessimistic locking
204
204
 
205
- unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :OpenBaseAdapter)
205
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
206
206
  class PessimisticLockingTest < ActiveRecord::TestCase
207
207
  self.use_transactional_fixtures = false
208
208
  fixtures :people, :readers
@@ -210,13 +210,6 @@ unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :OpenBaseAdapter)
210
210
  def setup
211
211
  # Avoid introspection queries during tests.
212
212
  Person.columns; Reader.columns
213
-
214
- @allow_concurrency = ActiveRecord::Base.allow_concurrency
215
- ActiveRecord::Base.allow_concurrency = true
216
- end
217
-
218
- def teardown
219
- ActiveRecord::Base.allow_concurrency = @allow_concurrency
220
213
  end
221
214
 
222
215
  # Test typical find.
@@ -264,6 +257,8 @@ unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :OpenBaseAdapter)
264
257
  end
265
258
 
266
259
  if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
260
+ use_concurrent_connections
261
+
267
262
  def test_no_locks_no_wait
268
263
  first, second = duel { Person.find 1 }
269
264
  assert first.end > second.end
@@ -1,4 +1,5 @@
1
1
  require "cases/helper"
2
+ require 'models/author'
2
3
  require 'models/developer'
3
4
  require 'models/project'
4
5
  require 'models/comment'
@@ -6,7 +7,7 @@ require 'models/post'
6
7
  require 'models/category'
7
8
 
8
9
  class MethodScopingTest < ActiveRecord::TestCase
9
- fixtures :developers, :projects, :comments, :posts, :developers_projects
10
+ fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects
10
11
 
11
12
  def test_set_conditions
12
13
  Developer.with_scope(:find => { :conditions => 'just a test...' }) do
@@ -97,6 +98,76 @@ class MethodScopingTest < ActiveRecord::TestCase
97
98
  assert_equal developers(:david).attributes, scoped_developers.first.attributes
98
99
  end
99
100
 
101
+ def test_scoped_find_using_new_style_joins
102
+ scoped_developers = Developer.with_scope(:find => { :joins => :projects }) do
103
+ Developer.find(:all, :conditions => 'projects.id = 2')
104
+ end
105
+ assert scoped_developers.include?(developers(:david))
106
+ assert !scoped_developers.include?(developers(:jamis))
107
+ assert_equal 1, scoped_developers.size
108
+ assert_equal developers(:david).attributes, scoped_developers.first.attributes
109
+ end
110
+
111
+ def test_scoped_find_merges_old_style_joins
112
+ scoped_authors = Author.with_scope(:find => { :joins => 'INNER JOIN posts ON authors.id = posts.author_id ' }) do
113
+ Author.find(:all, :select => 'DISTINCT authors.*', :joins => 'INNER JOIN comments ON posts.id = comments.post_id', :conditions => 'comments.id = 1')
114
+ end
115
+ assert scoped_authors.include?(authors(:david))
116
+ assert !scoped_authors.include?(authors(:mary))
117
+ assert_equal 1, scoped_authors.size
118
+ assert_equal authors(:david).attributes, scoped_authors.first.attributes
119
+ end
120
+
121
+ def test_scoped_find_merges_new_style_joins
122
+ scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
123
+ Author.find(:all, :select => 'DISTINCT authors.*', :joins => :comments, :conditions => 'comments.id = 1')
124
+ end
125
+ assert scoped_authors.include?(authors(:david))
126
+ assert !scoped_authors.include?(authors(:mary))
127
+ assert_equal 1, scoped_authors.size
128
+ assert_equal authors(:david).attributes, scoped_authors.first.attributes
129
+ end
130
+
131
+ def test_scoped_find_merges_new_and_old_style_joins
132
+ scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
133
+ Author.find(:all, :select => 'DISTINCT authors.*', :joins => 'JOIN comments ON posts.id = comments.post_id', :conditions => 'comments.id = 1')
134
+ end
135
+ assert scoped_authors.include?(authors(:david))
136
+ assert !scoped_authors.include?(authors(:mary))
137
+ assert_equal 1, scoped_authors.size
138
+ assert_equal authors(:david).attributes, scoped_authors.first.attributes
139
+ end
140
+
141
+ def test_scoped_find_merges_string_array_style_and_string_style_joins
142
+ scoped_authors = Author.with_scope(:find => { :joins => ["INNER JOIN posts ON posts.author_id = authors.id"]}) do
143
+ Author.find(:all, :select => 'DISTINCT authors.*', :joins => 'INNER JOIN comments ON posts.id = comments.post_id', :conditions => 'comments.id = 1')
144
+ end
145
+ assert scoped_authors.include?(authors(:david))
146
+ assert !scoped_authors.include?(authors(:mary))
147
+ assert_equal 1, scoped_authors.size
148
+ assert_equal authors(:david).attributes, scoped_authors.first.attributes
149
+ end
150
+
151
+ def test_scoped_find_merges_string_array_style_and_hash_style_joins
152
+ scoped_authors = Author.with_scope(:find => { :joins => :posts}) do
153
+ Author.find(:all, :select => 'DISTINCT authors.*', :joins => ['INNER JOIN comments ON posts.id = comments.post_id'], :conditions => 'comments.id = 1')
154
+ end
155
+ assert scoped_authors.include?(authors(:david))
156
+ assert !scoped_authors.include?(authors(:mary))
157
+ assert_equal 1, scoped_authors.size
158
+ assert_equal authors(:david).attributes, scoped_authors.first.attributes
159
+ end
160
+
161
+ def test_scoped_find_merges_joins_and_eliminates_duplicate_string_joins
162
+ scoped_authors = Author.with_scope(:find => { :joins => 'INNER JOIN posts ON posts.author_id = authors.id'}) do
163
+ Author.find(:all, :select => 'DISTINCT authors.*', :joins => ["INNER JOIN posts ON posts.author_id = authors.id", "INNER JOIN comments ON posts.id = comments.post_id"], :conditions => 'comments.id = 1')
164
+ end
165
+ assert scoped_authors.include?(authors(:david))
166
+ assert !scoped_authors.include?(authors(:mary))
167
+ assert_equal 1, scoped_authors.size
168
+ assert_equal authors(:david).attributes, scoped_authors.first.attributes
169
+ end
170
+
100
171
  def test_scoped_count_include
101
172
  # with the include, will retrieve only developers for the given project
102
173
  Developer.with_scope(:find => { :include => :projects }) do
@@ -152,7 +223,7 @@ class MethodScopingTest < ActiveRecord::TestCase
152
223
  end
153
224
 
154
225
  class NestedScopingTest < ActiveRecord::TestCase
155
- fixtures :developers, :projects, :comments, :posts
226
+ fixtures :authors, :developers, :projects, :comments, :posts
156
227
 
157
228
  def test_merge_options
158
229
  Developer.with_scope(:find => { :conditions => 'salary = 80000' }) do
@@ -357,6 +428,42 @@ class NestedScopingTest < ActiveRecord::TestCase
357
428
  assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
358
429
  end
359
430
  end
431
+
432
+ def test_nested_scoped_find_merges_old_style_joins
433
+ scoped_authors = Author.with_scope(:find => { :joins => 'INNER JOIN posts ON authors.id = posts.author_id' }) do
434
+ Author.with_scope(:find => { :joins => 'INNER JOIN comments ON posts.id = comments.post_id' }) do
435
+ Author.find(:all, :select => 'DISTINCT authors.*', :conditions => 'comments.id = 1')
436
+ end
437
+ end
438
+ assert scoped_authors.include?(authors(:david))
439
+ assert !scoped_authors.include?(authors(:mary))
440
+ assert_equal 1, scoped_authors.size
441
+ assert_equal authors(:david).attributes, scoped_authors.first.attributes
442
+ end
443
+
444
+ def test_nested_scoped_find_merges_new_style_joins
445
+ scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
446
+ Author.with_scope(:find => { :joins => :comments }) do
447
+ Author.find(:all, :select => 'DISTINCT authors.*', :conditions => 'comments.id = 1')
448
+ end
449
+ end
450
+ assert scoped_authors.include?(authors(:david))
451
+ assert !scoped_authors.include?(authors(:mary))
452
+ assert_equal 1, scoped_authors.size
453
+ assert_equal authors(:david).attributes, scoped_authors.first.attributes
454
+ end
455
+
456
+ def test_nested_scoped_find_merges_new_and_old_style_joins
457
+ scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
458
+ Author.with_scope(:find => { :joins => 'INNER JOIN comments ON posts.id = comments.post_id' }) do
459
+ Author.find(:all, :select => 'DISTINCT authors.*', :joins => '', :conditions => 'comments.id = 1')
460
+ end
461
+ end
462
+ assert scoped_authors.include?(authors(:david))
463
+ assert !scoped_authors.include?(authors(:mary))
464
+ assert_equal 1, scoped_authors.size
465
+ assert_equal authors(:david).attributes, scoped_authors.first.attributes
466
+ end
360
467
  end
361
468
 
362
469
  class HasManyScopingTest< ActiveRecord::TestCase
@@ -271,9 +271,9 @@ if ActiveRecord::Base.connection.supports_migrations?
271
271
  Person.connection.drop_table table_name rescue nil
272
272
  end
273
273
 
274
- # SQL Server, Sybase, and SQLite3 will not allow you to add a NOT NULL
274
+ # Sybase, and SQLite3 will not allow you to add a NOT NULL
275
275
  # column to a table without a default value.
276
- unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :SQLiteAdapter)
276
+ unless current_adapter?(:SybaseAdapter, :SQLiteAdapter)
277
277
  def test_add_column_not_null_without_default
278
278
  Person.connection.create_table :testings do |t|
279
279
  t.column :foo, :string
@@ -410,7 +410,7 @@ if ActiveRecord::Base.connection.supports_migrations?
410
410
  assert_equal Fixnum, bob.age.class
411
411
  assert_equal Time, bob.birthday.class
412
412
 
413
- if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
413
+ if current_adapter?(:OracleAdapter, :SybaseAdapter)
414
414
  # Sybase, and Oracle don't differentiate between date/time
415
415
  assert_equal Time, bob.favorite_day.class
416
416
  else
@@ -851,10 +851,6 @@ if ActiveRecord::Base.connection.supports_migrations?
851
851
  # - SQLite3 stores a float, in violation of SQL
852
852
  assert_kind_of BigDecimal, b.value_of_e
853
853
  assert_equal BigDecimal("2.71828182845905"), b.value_of_e
854
- elsif current_adapter?(:SQLServer)
855
- # - SQL Server rounds instead of truncating
856
- assert_kind_of Fixnum, b.value_of_e
857
- assert_equal 3, b.value_of_e
858
854
  else
859
855
  # - SQL standard is an integer
860
856
  assert_kind_of Fixnum, b.value_of_e
@@ -934,6 +930,21 @@ if ActiveRecord::Base.connection.supports_migrations?
934
930
  assert_equal(0, ActiveRecord::Migrator.current_version)
935
931
  end
936
932
 
933
+ if current_adapter?(:PostgreSQLAdapter)
934
+ def test_migrator_one_up_with_exception_and_rollback
935
+ assert !Person.column_methods_hash.include?(:last_name)
936
+
937
+ e = assert_raises(StandardError) do
938
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
939
+ end
940
+
941
+ assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
942
+
943
+ Person.reset_column_information
944
+ assert !Person.column_methods_hash.include?(:last_name)
945
+ end
946
+ end
947
+
937
948
  def test_finds_migrations
938
949
  migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
939
950
  [['1', 'people_have_last_names'],
@@ -952,6 +963,26 @@ if ActiveRecord::Base.connection.supports_migrations?
952
963
  migrations[0].name == 'innocent_jointable'
953
964
  end
954
965
 
966
+ def test_only_loads_pending_migrations
967
+ # migrate up to 1
968
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
969
+
970
+ # now unload the migrations that have been defined
971
+ PeopleHaveLastNames.unloadable
972
+ ActiveSupport::Dependencies.remove_unloadable_constants!
973
+
974
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
975
+
976
+ assert !defined? PeopleHaveLastNames
977
+
978
+ %w(WeNeedReminders, InnocentJointable).each do |migration|
979
+ assert defined? migration
980
+ end
981
+
982
+ ensure
983
+ load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
984
+ end
985
+
955
986
  def test_migrator_interleaved_migrations
956
987
  ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
957
988
 
@@ -1098,7 +1129,11 @@ if ActiveRecord::Base.connection.supports_migrations?
1098
1129
  columns = Person.connection.columns(:binary_testings)
1099
1130
  data_column = columns.detect { |c| c.name == "data" }
1100
1131
 
1101
- assert_nil data_column.default
1132
+ if current_adapter?(:MysqlAdapter)
1133
+ assert_equal '', data_column.default
1134
+ else
1135
+ assert_nil data_column.default
1136
+ end
1102
1137
 
1103
1138
  Person.connection.drop_table :binary_testings rescue nil
1104
1139
  end
@@ -57,4 +57,29 @@ class MultipleDbTest < ActiveRecord::TestCase
57
57
 
58
58
  assert Course.connection
59
59
  end
60
+
61
+ def test_transactions_across_databases
62
+ c1 = Course.find(1)
63
+ e1 = Entrant.find(1)
64
+
65
+ begin
66
+ Course.transaction do
67
+ Entrant.transaction do
68
+ c1.name = "Typo"
69
+ e1.name = "Typo"
70
+ c1.save
71
+ e1.save
72
+ raise "No I messed up."
73
+ end
74
+ end
75
+ rescue
76
+ # Yup caught it
77
+ end
78
+
79
+ assert_equal "Typo", c1.name
80
+ assert_equal "Typo", e1.name
81
+
82
+ assert_equal "Ruby Development", Course.find(1).name
83
+ assert_equal "Ruby Developer", Entrant.find(1).name
84
+ end
60
85
  end
@@ -77,6 +77,10 @@ class NamedScopeTest < ActiveRecord::TestCase
77
77
  assert_equal Topic.replied.approved, Topic.replied.approved_as_string
78
78
  end
79
79
 
80
+ def test_scopes_can_be_specified_with_deep_hash_conditions
81
+ assert_equal Topic.replied.approved, Topic.replied.approved_as_hash_condition
82
+ end
83
+
80
84
  def test_scopes_are_composable
81
85
  assert_equal (approved = Topic.find(:all, :conditions => {:approved => true})), Topic.approved
82
86
  assert_equal (replied = Topic.find(:all, :conditions => 'replies_count > 0')), Topic.replied
@@ -192,6 +196,55 @@ class NamedScopeTest < ActiveRecord::TestCase
192
196
  end
193
197
  end
194
198
 
199
+ def test_any_should_not_load_results
200
+ topics = Topic.base
201
+ assert_queries(2) do
202
+ topics.any? # use count query
203
+ topics.collect # force load
204
+ topics.any? # use loaded (no query)
205
+ end
206
+ end
207
+
208
+ def test_any_should_call_proxy_found_if_using_a_block
209
+ topics = Topic.base
210
+ assert_queries(1) do
211
+ topics.expects(:empty?).never
212
+ topics.any? { true }
213
+ end
214
+ end
215
+
216
+ def test_any_should_not_fire_query_if_named_scope_loaded
217
+ topics = Topic.base
218
+ topics.collect # force load
219
+ assert_no_queries { assert topics.any? }
220
+ end
221
+
222
+ def test_should_build_with_proxy_options
223
+ topic = Topic.approved.build({})
224
+ assert topic.approved
225
+ end
226
+
227
+ def test_should_build_new_with_proxy_options
228
+ topic = Topic.approved.new
229
+ assert topic.approved
230
+ end
231
+
232
+ def test_should_create_with_proxy_options
233
+ topic = Topic.approved.create({})
234
+ assert topic.approved
235
+ end
236
+
237
+ def test_should_create_with_bang_with_proxy_options
238
+ topic = Topic.approved.create!({})
239
+ assert topic.approved
240
+ end
241
+
242
+ def test_should_build_with_proxy_options_chained
243
+ topic = Topic.approved.by_lifo.build({})
244
+ assert topic.approved
245
+ assert_equal 'lifo', topic.author_name
246
+ end
247
+
195
248
  def test_find_all_should_behave_like_select
196
249
  assert_equal Topic.base.select(&:approved), Topic.base.find_all(&:approved)
197
250
  end
@@ -203,4 +256,25 @@ class NamedScopeTest < ActiveRecord::TestCase
203
256
  def test_should_use_where_in_query_for_named_scope
204
257
  assert_equal Developer.find_all_by_name('Jamis'), Developer.find_all_by_id(Developer.jamises)
205
258
  end
259
+
260
+ def test_size_should_use_count_when_results_are_not_loaded
261
+ topics = Topic.base
262
+ assert_queries(1) do
263
+ assert_sql(/COUNT/i) { topics.size }
264
+ end
265
+ end
266
+
267
+ def test_size_should_use_length_when_results_are_loaded
268
+ topics = Topic.base
269
+ topics.reload # force load
270
+ assert_no_queries do
271
+ topics.size # use loaded (no query)
272
+ end
273
+ end
274
+
275
+ def test_chaining_with_duplicate_joins
276
+ join = "INNER JOIN comments ON comments.post_id = posts.id"
277
+ post = Post.find(1)
278
+ assert_equal post.comments.size, Post.scoped(:joins => join).scoped(:joins => join, :conditions => "posts.id = #{post.id}").size
279
+ end
206
280
  end