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
@@ -6,10 +6,22 @@ require File.dirname(__FILE__) + '/fixtures/migrations/2_we_need_reminders'
6
6
  if ActiveRecord::Base.connection.supports_migrations?
7
7
  class Reminder < ActiveRecord::Base; end
8
8
 
9
+ class ActiveRecord::Migration
10
+ class <<self
11
+ attr_accessor :message_count
12
+ def puts(text="")
13
+ self.message_count ||= 0
14
+ self.message_count += 1
15
+ end
16
+ end
17
+ end
18
+
9
19
  class MigrationTest < Test::Unit::TestCase
10
20
  self.use_transactional_fixtures = false
11
21
 
12
22
  def setup
23
+ ActiveRecord::Migration.verbose = true
24
+ PeopleHaveLastNames.message_count = 0
13
25
  end
14
26
 
15
27
  def teardown
@@ -31,7 +43,7 @@ if ActiveRecord::Base.connection.supports_migrations?
31
43
  Person.connection.remove_column("people", "administrator") rescue nil
32
44
  Person.reset_column_information
33
45
  end
34
-
46
+
35
47
  def test_add_index
36
48
  Person.connection.add_column "people", "last_name", :string
37
49
  Person.connection.add_column "people", "administrator", :boolean
@@ -42,8 +54,11 @@ if ActiveRecord::Base.connection.supports_migrations?
42
54
  assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
43
55
  assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
44
56
 
45
- assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
46
- assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
57
+ # Sybase adapter does not support indexes on :boolean columns
58
+ unless current_adapter?(:SybaseAdapter)
59
+ assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
60
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
61
+ end
47
62
  end
48
63
 
49
64
  def test_create_table_adds_id
@@ -84,7 +99,7 @@ if ActiveRecord::Base.connection.supports_migrations?
84
99
  four = columns.detect { |c| c.name == "four" }
85
100
 
86
101
  assert_equal "hello", one.default
87
- if current_adapter?(:OCIAdapter)
102
+ if current_adapter?(:OracleAdapter)
88
103
  # Oracle doesn't support native booleans
89
104
  assert_equal true, two.default == 1
90
105
  assert_equal false, three.default != 0
@@ -98,10 +113,10 @@ if ActiveRecord::Base.connection.supports_migrations?
98
113
  Person.connection.drop_table :testings rescue nil
99
114
  end
100
115
 
101
- # SQL Server will not allow you to add a NOT NULL column
116
+ # SQL Server and Sybase will not allow you to add a NOT NULL column
102
117
  # to a table without specifying a default value, so the
103
118
  # following test must be skipped
104
- unless current_adapter?(:SQLServerAdapter)
119
+ unless current_adapter?(:SQLServerAdapter) || current_adapter?(:SybaseAdapter)
105
120
  def test_add_column_not_null_without_default
106
121
  Person.connection.create_table :testings do |t|
107
122
  t.column :foo, :string
@@ -153,8 +168,8 @@ if ActiveRecord::Base.connection.supports_migrations?
153
168
  assert_equal Fixnum, bob.age.class
154
169
  assert_equal Time, bob.birthday.class
155
170
 
156
- if current_adapter?(:SQLServerAdapter) or current_adapter?(:OCIAdapter)
157
- # SQL Server and Oracle don't differentiate between date/time
171
+ if current_adapter?(:SQLServerAdapter) || current_adapter?(:OracleAdapter) || current_adapter?(:SybaseAdapter)
172
+ # SQL Server, Sybase, and Oracle don't differentiate between date/time
158
173
  assert_equal Time, bob.favorite_day.class
159
174
  else
160
175
  assert_equal Date, bob.favorite_day.class
@@ -241,7 +256,7 @@ if ActiveRecord::Base.connection.supports_migrations?
241
256
  ActiveRecord::Base.connection.rename_table :octopuses, :octopi
242
257
 
243
258
  assert_nothing_raised do
244
- if current_adapter?(:OCIAdapter)
259
+ if current_adapter?(:OracleAdapter)
245
260
  # Oracle requires the explicit sequence for the pk
246
261
  ActiveRecord::Base.connection.execute "INSERT INTO octopi (id, url) VALUES (octopi_seq.nextval, 'http://www.foreverflying.com/octopus-black7.jpg')"
247
262
  else
@@ -258,8 +273,15 @@ if ActiveRecord::Base.connection.supports_migrations?
258
273
  end
259
274
 
260
275
  def test_change_column
261
- Person.connection.add_column "people", "bio", :string
262
- assert_nothing_raised { Person.connection.change_column "people", "bio", :text }
276
+ Person.connection.add_column 'people', 'age', :integer
277
+ old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
278
+ assert old_columns.find { |c| c.name == 'age' and c.type == :integer }
279
+
280
+ assert_nothing_raised { Person.connection.change_column "people", "age", :string }
281
+
282
+ new_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
283
+ assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
284
+ assert new_columns.find { |c| c.name == 'age' and c.type == :string }
263
285
  end
264
286
 
265
287
  def test_change_column_with_new_default
@@ -338,6 +360,24 @@ if ActiveRecord::Base.connection.supports_migrations?
338
360
  assert !Reminder.table_exists?
339
361
  end
340
362
 
363
+ def test_migrator_verbosity
364
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
365
+ assert PeopleHaveLastNames.message_count > 0
366
+ PeopleHaveLastNames.message_count = 0
367
+
368
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
369
+ assert PeopleHaveLastNames.message_count > 0
370
+ PeopleHaveLastNames.message_count = 0
371
+ end
372
+
373
+ def test_migrator_verbosity_off
374
+ PeopleHaveLastNames.verbose = false
375
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
376
+ assert PeopleHaveLastNames.message_count.zero?
377
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
378
+ assert PeopleHaveLastNames.message_count.zero?
379
+ end
380
+
341
381
  def test_migrator_going_down_due_to_version_target
342
382
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
343
383
  ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
@@ -415,6 +455,23 @@ if ActiveRecord::Base.connection.supports_migrations?
415
455
  Reminder.reset_sequence_name
416
456
  end
417
457
 
458
+ def test_create_table_with_binary_column
459
+ Person.connection.drop_table :binary_testings rescue nil
460
+
461
+ assert_nothing_raised {
462
+ Person.connection.create_table :binary_testings do |t|
463
+ t.column "data", :binary, :default => "", :null => false
464
+ end
465
+ }
466
+
467
+ columns = Person.connection.columns(:binary_testings)
468
+ data_column = columns.detect { |c| c.name == "data" }
469
+
470
+ assert_equal "", data_column.default
471
+
472
+ Person.connection.drop_table :binary_testings rescue nil
473
+ end
474
+
418
475
  def test_migrator_with_duplicates
419
476
  assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
420
477
  ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_duplicate/', nil)
@@ -9,7 +9,7 @@ class MixinNestedSetTest < Test::Unit::TestCase
9
9
  def test_mixing_in_methods
10
10
  ns = NestedSet.new
11
11
  assert( ns.respond_to?( :all_children ) )
12
- assert_equal( ns.scope_condition, "ROOT_ID IS NULL" )
12
+ assert_equal( ns.scope_condition, "root_id IS NULL" )
13
13
 
14
14
  check_method_mixins ns
15
15
  end
@@ -18,6 +18,11 @@ class ModulesTest < Test::Unit::TestCase
18
18
  end
19
19
 
20
20
  def test_associations_spanning_cross_modules
21
- assert MyApplication::Billing::Account.find(1).has_firm?, "37signals account should be able to backtrack"
21
+ account = MyApplication::Billing::Account.find(:first, :order => 'id')
22
+ assert_kind_of MyApplication::Business::Firm, account.firm
23
+ assert_kind_of MyApplication::Billing::Firm, account.qualified_billing_firm
24
+ assert_kind_of MyApplication::Billing::Firm, account.unqualified_billing_firm
25
+ assert_kind_of MyApplication::Billing::Nested::Firm, account.nested_qualified_billing_firm
26
+ assert_kind_of MyApplication::Billing::Nested::Firm, account.nested_unqualified_billing_firm
22
27
  end
23
28
  end
@@ -79,7 +79,7 @@ class ReadOnlyTest < Test::Unit::TestCase
79
79
 
80
80
  # Oracle barfs on this because the join includes unqualified and
81
81
  # conflicting column names
82
- unless current_adapter?(:OCIAdapter)
82
+ unless current_adapter?(:OracleAdapter)
83
83
  Post.with_scope(:find => { :joins => ', developers' }) do
84
84
  assert Post.find(1).readonly?
85
85
  assert Post.find(1, :readonly => true).readonly?
@@ -3,14 +3,21 @@ require 'fixtures/topic'
3
3
  require 'fixtures/customer'
4
4
  require 'fixtures/company'
5
5
  require 'fixtures/company_in_module'
6
+ require 'fixtures/subscriber'
6
7
 
7
8
  class ReflectionTest < Test::Unit::TestCase
8
- fixtures :topics, :customers, :companies
9
+ fixtures :topics, :customers, :companies, :subscribers
9
10
 
10
11
  def setup
11
12
  @first = Topic.find(1)
12
13
  end
13
14
 
15
+ def test_column_null_not_null
16
+ subscriber = Subscriber.find(:first)
17
+ assert subscriber.column_for_attribute("name").null
18
+ assert !subscriber.column_for_attribute("nick").null
19
+ end
20
+
14
21
  def test_read_attribute_names
15
22
  assert_equal(
16
23
  %w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id type ).sort,
@@ -60,10 +67,9 @@ class ReflectionTest < Test::Unit::TestCase
60
67
  :composed_of, :gps_location, { }, Customer
61
68
  )
62
69
 
63
- assert_equal(
64
- [ reflection_for_address, reflection_for_balance, reflection_for_gps_location ],
65
- Customer.reflect_on_all_aggregations
66
- )
70
+ assert Customer.reflect_on_all_aggregations.include?(reflection_for_gps_location)
71
+ assert Customer.reflect_on_all_aggregations.include?(reflection_for_balance)
72
+ assert Customer.reflect_on_all_aggregations.include?(reflection_for_address)
67
73
 
68
74
  assert_equal reflection_for_address, Customer.reflect_on_aggregation(:address)
69
75
 
@@ -71,7 +77,7 @@ class ReflectionTest < Test::Unit::TestCase
71
77
  end
72
78
 
73
79
  def test_has_many_reflection
74
- reflection_for_clients = ActiveRecord::Reflection::AssociationReflection.new(:has_many, :clients, { :order => "id", :dependent => true }, Firm)
80
+ reflection_for_clients = ActiveRecord::Reflection::AssociationReflection.new(:has_many, :clients, { :order => "id", :dependent => :destroy }, Firm)
75
81
 
76
82
  assert_equal reflection_for_clients, Firm.reflect_on_association(:clients)
77
83
 
@@ -83,7 +89,7 @@ class ReflectionTest < Test::Unit::TestCase
83
89
  end
84
90
 
85
91
  def test_has_one_reflection
86
- reflection_for_account = ActiveRecord::Reflection::AssociationReflection.new(:has_one, :account, { :foreign_key => "firm_id", :dependent => true }, Firm)
92
+ reflection_for_account = ActiveRecord::Reflection::AssociationReflection.new(:has_one, :account, { :foreign_key => "firm_id", :dependent => :destroy }, Firm)
87
93
  assert_equal reflection_for_account, Firm.reflect_on_association(:account)
88
94
 
89
95
  assert_equal Account, Firm.reflect_on_association(:account).klass
@@ -91,7 +97,55 @@ class ReflectionTest < Test::Unit::TestCase
91
97
  end
92
98
 
93
99
  def test_association_reflection_in_modules
94
- assert_equal MyApplication::Business::Client, MyApplication::Business::Firm.reflect_on_association(:clients_of_firm).klass
95
- assert_equal MyApplication::Business::Firm, MyApplication::Billing::Account.reflect_on_association(:firm).klass
100
+ assert_reflection MyApplication::Business::Firm,
101
+ :clients_of_firm,
102
+ :klass => MyApplication::Business::Client,
103
+ :class_name => 'Client',
104
+ :table_name => 'companies'
105
+
106
+ assert_reflection MyApplication::Billing::Account,
107
+ :firm,
108
+ :klass => MyApplication::Business::Firm,
109
+ :class_name => 'MyApplication::Business::Firm',
110
+ :table_name => 'companies'
111
+
112
+ assert_reflection MyApplication::Billing::Account,
113
+ :qualified_billing_firm,
114
+ :klass => MyApplication::Billing::Firm,
115
+ :class_name => 'MyApplication::Billing::Firm',
116
+ :table_name => 'companies'
117
+
118
+ assert_reflection MyApplication::Billing::Account,
119
+ :unqualified_billing_firm,
120
+ :klass => MyApplication::Billing::Firm,
121
+ :class_name => 'Firm',
122
+ :table_name => 'companies'
123
+
124
+ assert_reflection MyApplication::Billing::Account,
125
+ :nested_qualified_billing_firm,
126
+ :klass => MyApplication::Billing::Nested::Firm,
127
+ :class_name => 'MyApplication::Billing::Nested::Firm',
128
+ :table_name => 'companies'
129
+
130
+ assert_reflection MyApplication::Billing::Account,
131
+ :nested_unqualified_billing_firm,
132
+ :klass => MyApplication::Billing::Nested::Firm,
133
+ :class_name => 'Nested::Firm',
134
+ :table_name => 'companies'
135
+ end
136
+
137
+ def test_reflection_of_all_associations
138
+ assert_equal 13, Firm.reflect_on_all_associations.size
139
+ assert_equal 11, Firm.reflect_on_all_associations(:has_many).size
140
+ assert_equal 2, Firm.reflect_on_all_associations(:has_one).size
141
+ assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
96
142
  end
143
+
144
+ private
145
+ def assert_reflection(klass, association, options)
146
+ assert reflection = klass.reflect_on_association(association)
147
+ options.each do |method, value|
148
+ assert_equal(value, reflection.send(method))
149
+ end
150
+ end
97
151
  end
@@ -14,6 +14,47 @@ if ActiveRecord::Base.connection.respond_to?(:tables)
14
14
  assert_match %r{create_table "authors"}, output
15
15
  assert_no_match %r{create_table "schema_info"}, output
16
16
  end
17
+
18
+ def test_schema_dump_includes_not_null_columns
19
+ stream = StringIO.new
20
+
21
+ ActiveRecord::SchemaDumper.ignore_tables = [/^[^s]/]
22
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
23
+ output = stream.string
24
+ assert_match %r{:null => false}, output
25
+ end
26
+
27
+ def test_schema_dump_with_string_ignored_table
28
+ stream = StringIO.new
29
+
30
+ ActiveRecord::SchemaDumper.ignore_tables = ['accounts']
31
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
32
+ output = stream.string
33
+ assert_no_match %r{create_table "accounts"}, output
34
+ assert_match %r{create_table "authors"}, output
35
+ assert_no_match %r{create_table "schema_info"}, output
36
+ end
37
+
38
+
39
+ def test_schema_dump_with_regexp_ignored_table
40
+ stream = StringIO.new
41
+
42
+ ActiveRecord::SchemaDumper.ignore_tables = [/^account/]
43
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
44
+ output = stream.string
45
+ assert_no_match %r{create_table "accounts"}, output
46
+ assert_match %r{create_table "authors"}, output
47
+ assert_no_match %r{create_table "schema_info"}, output
48
+ end
49
+
50
+
51
+ def test_schema_dump_illegal_ignored_table_value
52
+ stream = StringIO.new
53
+ ActiveRecord::SchemaDumper.ignore_tables = [5]
54
+ assert_raise(StandardError) do
55
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
56
+ end
57
+ end
17
58
  end
18
59
 
19
60
  end
@@ -10,7 +10,7 @@ class TestOracleSynonym < Test::Unit::TestCase
10
10
 
11
11
  def test_oracle_synonym
12
12
  topic = Topic.new
13
- subject = Subject.new
13
+ subject = Subject.new
14
14
  assert_equal(topic.attributes, subject.attributes)
15
15
  end
16
16
 
@@ -9,6 +9,16 @@ class ThreadedConnectionsTest < Test::Unit::TestCase
9
9
  def setup
10
10
  @connection = ActiveRecord::Base.remove_connection
11
11
  @connections = []
12
+ @allow_concurrency = ActiveRecord::Base.allow_concurrency
13
+ end
14
+
15
+ def teardown
16
+ # clear the connection cache
17
+ ActiveRecord::Base.send(:clear_all_cached_connections!)
18
+ # set allow_concurrency to saved value
19
+ ActiveRecord::Base.allow_concurrency = @allow_concurrency
20
+ # reestablish old connection
21
+ ActiveRecord::Base.establish_connection(@connection)
12
22
  end
13
23
 
14
24
  def gather_connections(use_threaded_connections)
@@ -7,19 +7,26 @@ class TestUnconnectedAdaptor < Test::Unit::TestCase
7
7
  self.use_transactional_fixtures = false
8
8
 
9
9
  def setup
10
- @connection = ActiveRecord::Base.remove_connection
10
+ @underlying = ActiveRecord::Base.connection
11
+ @specification = ActiveRecord::Base.remove_connection
11
12
  end
12
13
 
13
14
  def teardown
14
- ActiveRecord::Base.establish_connection(@connection)
15
+ @underlying = nil
16
+ ActiveRecord::Base.establish_connection(@specification)
15
17
  end
16
18
 
17
- def test_unconnected
19
+ def test_connection_no_longer_established
18
20
  assert_raise(ActiveRecord::ConnectionNotEstablished) do
19
- TestRecord.find(1)
21
+ TestRecord.find(1)
20
22
  end
23
+
21
24
  assert_raise(ActiveRecord::ConnectionNotEstablished) do
22
- TestRecord.new.save
25
+ TestRecord.new.save
23
26
  end
24
27
  end
28
+
29
+ def test_underlying_adapter_no_longer_active
30
+ assert !@underlying.active?, "Removed adapter should no longer be active"
31
+ end
25
32
  end
@@ -156,11 +156,11 @@ class ValidationsTest < Test::Unit::TestCase
156
156
  def test_errors_on_boundary_breaking
157
157
  developer = Developer.new("name" => "xs")
158
158
  assert !developer.save
159
- assert_equal "is too short (min is 3 characters)", developer.errors.on("name")
159
+ assert_equal "is too short (minimum is 3 characters)", developer.errors.on("name")
160
160
 
161
161
  developer.name = "All too very long for this boundary, it really is"
162
162
  assert !developer.save
163
- assert_equal "is too long (max is 20 characters)", developer.errors.on("name")
163
+ assert_equal "is too long (maximum is 20 characters)", developer.errors.on("name")
164
164
 
165
165
  developer.name = "Just right"
166
166
  assert developer.save
@@ -280,6 +280,30 @@ class ValidationsTest < Test::Unit::TestCase
280
280
  assert r3.valid?, "Saving r3"
281
281
  end
282
282
 
283
+ def test_validate_uniqueness_with_scope_array
284
+ Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
285
+
286
+ t = Topic.create("title" => "The earth is actually flat!")
287
+
288
+ r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
289
+ assert r1.valid?, "Saving r1"
290
+
291
+ r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
292
+ assert !r2.valid?, "Saving r2. Double reply by same author."
293
+
294
+ r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
295
+ assert r2.save, "Saving r2 the second time."
296
+
297
+ r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
298
+ assert !r3.valid?, "Saving r3"
299
+
300
+ r3.author_name = "jj"
301
+ assert r3.save, "Saving r3 the second time."
302
+
303
+ r3.author_name = "jeremy"
304
+ assert !r3.save, "Saving r3 the third time."
305
+ end
306
+
283
307
  def test_validate_format
284
308
  Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
285
309
 
@@ -296,6 +320,34 @@ class ValidationsTest < Test::Unit::TestCase
296
320
 
297
321
  assert_raise(ArgumentError) { Topic.validates_format_of(:title, :content) }
298
322
  end
323
+
324
+ # testing ticket #3142
325
+ def test_validate_format_numeric
326
+ Topic.validates_format_of(:title, :content, :with => /^[1-9][0-9]*$/, :message => "is bad data")
327
+
328
+ t = Topic.create("title" => "72x", "content" => "6789")
329
+ assert !t.valid?, "Shouldn't be valid"
330
+ assert !t.save, "Shouldn't save because it's invalid"
331
+ assert_equal "is bad data", t.errors.on(:title)
332
+ assert_nil t.errors.on(:content)
333
+
334
+ t.title = "-11"
335
+ assert !t.valid?, "Shouldn't be valid"
336
+
337
+ t.title = "03"
338
+ assert !t.valid?, "Shouldn't be valid"
339
+
340
+ t.title = "z44"
341
+ assert !t.valid?, "Shouldn't be valid"
342
+
343
+ t.title = "5v7"
344
+ assert !t.valid?, "Shouldn't be valid"
345
+
346
+ t.title = "1"
347
+
348
+ assert t.save
349
+ assert_nil t.errors.on(:title)
350
+ end
299
351
 
300
352
  def test_validates_inclusion_of
301
353
  Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) )
@@ -350,17 +402,17 @@ class ValidationsTest < Test::Unit::TestCase
350
402
  t.title = "not"
351
403
  assert !t.valid?
352
404
  assert t.errors.on(:title)
353
- assert_equal "is too short (min is 5 characters)", t.errors["title"]
405
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
354
406
 
355
407
  t.title = ""
356
408
  assert !t.valid?
357
409
  assert t.errors.on(:title)
358
- assert_equal "is too short (min is 5 characters)", t.errors["title"]
410
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
359
411
 
360
412
  t.title = nil
361
413
  assert !t.valid?
362
414
  assert t.errors.on(:title)
363
- assert_equal "is too short (min is 5 characters)", t.errors["title"]
415
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
364
416
  end
365
417
 
366
418
  def test_optionally_validates_length_of_using_minimum
@@ -382,7 +434,7 @@ class ValidationsTest < Test::Unit::TestCase
382
434
  t.title = "notvalid"
383
435
  assert !t.valid?
384
436
  assert t.errors.on(:title)
385
- assert_equal "is too long (max is 5 characters)", t.errors["title"]
437
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
386
438
 
387
439
  t.title = ""
388
440
  assert t.valid?
@@ -406,14 +458,14 @@ class ValidationsTest < Test::Unit::TestCase
406
458
 
407
459
  t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long")
408
460
  assert !t.valid?
409
- assert_equal "is too short (min is 3 characters)", t.errors.on(:title)
410
- assert_equal "is too long (max is 5 characters)", t.errors.on(:content)
461
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
462
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
411
463
 
412
464
  t.title = nil
413
465
  t.content = nil
414
466
  assert !t.valid?
415
- assert_equal "is too short (min is 3 characters)", t.errors.on(:title)
416
- assert_equal "is too short (min is 3 characters)", t.errors.on(:content)
467
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
468
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:content)
417
469
 
418
470
  t.title = "abe"
419
471
  t.content = "mad"
@@ -590,6 +642,141 @@ class ValidationsTest < Test::Unit::TestCase
590
642
  assert_equal "hoo 5", t.errors["title"]
591
643
  end
592
644
 
645
+ def kcode_scope(kcode)
646
+ orig_kcode = $KCODE
647
+ $KCODE = kcode
648
+ begin
649
+ yield
650
+ ensure
651
+ $KCODE = orig_kcode
652
+ end
653
+ end
654
+
655
+ def test_validates_length_of_using_minimum_utf8
656
+ kcode_scope('UTF8') do
657
+ Topic.validates_length_of :title, :minimum => 5
658
+
659
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
660
+ assert t.valid?
661
+
662
+ t.title = "一二三四"
663
+ assert !t.valid?
664
+ assert t.errors.on(:title)
665
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
666
+ end
667
+ end
668
+
669
+ def test_validates_length_of_using_maximum_utf8
670
+ kcode_scope('UTF8') do
671
+ Topic.validates_length_of :title, :maximum => 5
672
+
673
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
674
+ assert t.valid?
675
+
676
+ t.title = "一二34五六"
677
+ assert !t.valid?
678
+ assert t.errors.on(:title)
679
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
680
+ end
681
+ end
682
+
683
+ def test_validates_length_of_using_within_utf8
684
+ kcode_scope('UTF8') do
685
+ Topic.validates_length_of(:title, :content, :within => 3..5)
686
+
687
+ t = Topic.new("title" => "一二", "content" => "12三四五六七")
688
+ assert !t.valid?
689
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
690
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
691
+ t.title = "一二三"
692
+ t.content = "12三"
693
+ assert t.valid?
694
+ end
695
+ end
696
+
697
+ def test_optionally_validates_length_of_using_within_utf8
698
+ kcode_scope('UTF8') do
699
+ Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
700
+
701
+ t = Topic.create('title' => '一二三', 'content' => '一二三四五')
702
+ assert t.valid?
703
+
704
+ t.title = nil
705
+ assert t.valid?
706
+ end
707
+ end
708
+
709
+ def test_optionally_validates_length_of_using_within_on_create_utf8
710
+ kcode_scope('UTF8') do
711
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "長すぎます: %d"
712
+
713
+ t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
714
+ assert !t.save
715
+ assert t.errors.on(:title)
716
+ assert_equal "長すぎます: 10", t.errors[:title]
717
+
718
+ t.title = "一二三四五六七八九"
719
+ assert t.save
720
+
721
+ t.title = "一二3"
722
+ assert t.save
723
+
724
+ t.content = "一二三四五六七八九十"
725
+ assert t.save
726
+
727
+ t.content = t.title = "一二三四五六"
728
+ assert t.save
729
+ end
730
+ end
731
+
732
+ def test_optionally_validates_length_of_using_within_on_update_utf8
733
+ kcode_scope('UTF8') do
734
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "短すぎます: %d"
735
+
736
+ t = Topic.create("title" => "一二三4", "content" => "whatever")
737
+ assert !t.save
738
+ assert t.errors.on(:title)
739
+
740
+ t.title = "1二三4"
741
+ assert !t.save
742
+ assert t.errors.on(:title)
743
+ assert_equal "短すぎます: 5", t.errors[:title]
744
+
745
+ t.title = "valid"
746
+ t.content = "一二三四五六七八九十A"
747
+ assert !t.save
748
+ assert t.errors.on(:content)
749
+
750
+ t.content = "一二345"
751
+ assert t.save
752
+ end
753
+ end
754
+
755
+ def test_validates_length_of_using_is_utf8
756
+ kcode_scope('UTF8') do
757
+ Topic.validates_length_of :title, :is => 5
758
+
759
+ t = Topic.create("title" => "一二345", "content" => "whatever")
760
+ assert t.valid?
761
+
762
+ t.title = "一二345六"
763
+ assert !t.valid?
764
+ assert t.errors.on(:title)
765
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
766
+ end
767
+ end
768
+
769
+ def test_validates_size_of_association_utf8
770
+ kcode_scope('UTF8') do
771
+ assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
772
+ t = Topic.new('title' => 'あいうえお', 'content' => 'かきくけこ')
773
+ assert !t.save
774
+ assert t.errors.on(:replies)
775
+ t.replies.create('title' => 'あいうえお', 'content' => 'かきくけこ')
776
+ assert t.valid?
777
+ end
778
+ end
779
+
593
780
  def test_validates_associated_many
594
781
  Topic.validates_associated( :replies )
595
782
  t = Topic.create("title" => "uhohuhoh", "content" => "whatever")