activerecord 2.1.0 → 2.1.1
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.
- data/CHANGELOG +34 -0
- data/README +0 -0
- data/Rakefile +6 -5
- data/lib/active_record.rb +8 -10
- data/lib/active_record/association_preload.rb +17 -12
- data/lib/active_record/associations.rb +45 -27
- data/lib/active_record/associations/association_collection.rb +8 -5
- data/lib/active_record/associations/association_proxy.rb +2 -6
- data/lib/active_record/associations/belongs_to_association.rb +0 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -3
- data/lib/active_record/associations/has_many_association.rb +16 -4
- data/lib/active_record/associations/has_many_through_association.rb +1 -1
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/has_one_through_association.rb +4 -0
- data/lib/active_record/base.rb +33 -15
- data/lib/active_record/calculations.rb +20 -7
- data/lib/active_record/callbacks.rb +0 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +17 -10
- data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +53 -24
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +66 -20
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +12 -0
- data/lib/active_record/dirty.rb +10 -3
- data/lib/active_record/fixtures.rb +0 -0
- data/lib/active_record/locking/optimistic.rb +1 -0
- data/lib/active_record/migration.rb +35 -8
- data/lib/active_record/named_scope.rb +6 -1
- data/lib/active_record/observer.rb +7 -5
- data/lib/active_record/test_case.rb +13 -2
- data/lib/active_record/validations.rb +19 -9
- data/lib/active_record/version.rb +1 -1
- data/test/cases/active_schema_test_postgresql.rb +2 -2
- data/test/cases/adapter_test.rb +1 -1
- data/test/cases/associations/belongs_to_associations_test.rb +19 -0
- data/test/cases/associations/cascaded_eager_loading_test.rb +13 -1
- data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
- data/test/cases/associations/eager_test.rb +25 -1
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +27 -5
- data/test/cases/associations/has_many_associations_test.rb +106 -4
- data/test/cases/associations/has_many_through_associations_test.rb +10 -0
- data/test/cases/associations/has_one_associations_test.rb +22 -0
- data/test/cases/associations/has_one_through_associations_test.rb +44 -5
- data/test/cases/associations/join_model_test.rb +7 -0
- data/test/cases/associations_test.rb +2 -2
- data/test/cases/attribute_methods_test.rb +10 -10
- data/test/cases/base_test.rb +39 -16
- data/test/cases/calculations_test.rb +53 -1
- data/test/cases/column_definition_test.rb +36 -0
- data/test/cases/database_statements_test.rb +12 -0
- data/test/cases/defaults_test.rb +1 -1
- data/test/cases/deprecated_finder_test.rb +0 -0
- data/test/cases/dirty_test.rb +94 -0
- data/test/cases/finder_test.rb +7 -0
- data/test/cases/fixtures_test.rb +0 -0
- data/test/cases/helper.rb +5 -5
- data/test/cases/inheritance_test.rb +9 -2
- data/test/cases/lifecycle_test.rb +54 -1
- data/test/cases/locking_test.rb +20 -0
- data/test/cases/method_scoping_test.rb +11 -1
- data/test/cases/migration_test.rb +147 -22
- data/test/cases/multiple_db_test.rb +1 -1
- data/test/cases/named_scope_test.rb +50 -1
- data/test/cases/query_cache_test.rb +4 -3
- data/test/cases/readonly_test.rb +0 -0
- data/test/cases/reflection_test.rb +3 -3
- data/test/cases/schema_dumper_test.rb +46 -0
- data/test/cases/unconnected_test.rb +0 -0
- data/test/cases/validations_test.rb +30 -5
- data/test/debug.log +358 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/models/author.rb +4 -0
- data/test/models/category.rb +1 -0
- data/test/models/company.rb +10 -1
- data/test/models/developer.rb +4 -1
- data/test/models/person.rb +1 -1
- data/test/models/post.rb +6 -1
- data/test/models/project.rb +1 -1
- data/test/models/reply.rb +0 -0
- data/test/models/topic.rb +1 -0
- data/test/schema/mysql_specific_schema.rb +2 -2
- data/test/schema/schema.rb +8 -0
- metadata +11 -5
- data/lib/active_record/vendor/db2.rb +0 -362
@@ -0,0 +1,12 @@
|
|
1
|
+
require "cases/helper"
|
2
|
+
|
3
|
+
class DatabaseStatementsTest < ActiveRecord::TestCase
|
4
|
+
def setup
|
5
|
+
@connection = ActiveRecord::Base.connection
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_insert_should_return_the_inserted_id
|
9
|
+
id = @connection.insert("INSERT INTO accounts (firm_id,credit_limit) VALUES (42,5000)")
|
10
|
+
assert_not_nil id
|
11
|
+
end
|
12
|
+
end
|
data/test/cases/defaults_test.rb
CHANGED
@@ -5,7 +5,7 @@ require 'models/entrant'
|
|
5
5
|
class DefaultTest < ActiveRecord::TestCase
|
6
6
|
def test_nil_defaults_for_not_null_columns
|
7
7
|
column_defaults =
|
8
|
-
if current_adapter?(:MysqlAdapter) && Mysql.client_version < 50051
|
8
|
+
if current_adapter?(:MysqlAdapter) && (Mysql.client_version < 50051 || (50100..50122).include?(Mysql.client_version))
|
9
9
|
{ 'id' => nil, 'name' => '', 'course_id' => nil }
|
10
10
|
else
|
11
11
|
{ 'id' => nil, 'name' => nil, 'course_id' => nil }
|
File without changes
|
data/test/cases/dirty_test.rb
CHANGED
@@ -2,6 +2,7 @@ require 'cases/helper'
|
|
2
2
|
require 'models/topic' # For booleans
|
3
3
|
require 'models/pirate' # For timestamps
|
4
4
|
require 'models/parrot'
|
5
|
+
require 'models/person' # For optimistic locking
|
5
6
|
|
6
7
|
class Pirate # Just reopening it, not defining it
|
7
8
|
attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
|
@@ -54,6 +55,33 @@ class DirtyTest < ActiveRecord::TestCase
|
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
58
|
+
def test_zero_to_blank_marked_as_changed
|
59
|
+
pirate = Pirate.new
|
60
|
+
pirate.catchphrase = "Yarrrr, me hearties"
|
61
|
+
pirate.parrot_id = 1
|
62
|
+
pirate.save
|
63
|
+
|
64
|
+
# check the change from 1 to ''
|
65
|
+
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
|
66
|
+
pirate.parrot_id = ''
|
67
|
+
assert pirate.parrot_id_changed?
|
68
|
+
assert_equal([1, nil], pirate.parrot_id_change)
|
69
|
+
pirate.save
|
70
|
+
|
71
|
+
# check the change from nil to 0
|
72
|
+
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
|
73
|
+
pirate.parrot_id = 0
|
74
|
+
assert pirate.parrot_id_changed?
|
75
|
+
assert_equal([nil, 0], pirate.parrot_id_change)
|
76
|
+
pirate.save
|
77
|
+
|
78
|
+
# check the change from 0 to ''
|
79
|
+
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
|
80
|
+
pirate.parrot_id = ''
|
81
|
+
assert pirate.parrot_id_changed?
|
82
|
+
assert_equal([0, nil], pirate.parrot_id_change)
|
83
|
+
end
|
84
|
+
|
57
85
|
def test_object_should_be_changed_if_any_attribute_is_changed
|
58
86
|
pirate = Pirate.new
|
59
87
|
assert !pirate.changed?
|
@@ -125,6 +153,24 @@ class DirtyTest < ActiveRecord::TestCase
|
|
125
153
|
end
|
126
154
|
end
|
127
155
|
|
156
|
+
def test_partial_update_with_optimistic_locking
|
157
|
+
person = Person.new(:first_name => 'foo')
|
158
|
+
old_lock_version = 1
|
159
|
+
|
160
|
+
with_partial_updates Person, false do
|
161
|
+
assert_queries(2) { 2.times { person.save! } }
|
162
|
+
Person.update_all({ :first_name => 'baz' }, :id => person.id)
|
163
|
+
end
|
164
|
+
|
165
|
+
with_partial_updates Person, true do
|
166
|
+
assert_queries(0) { 2.times { person.save! } }
|
167
|
+
assert_equal old_lock_version, person.reload.lock_version
|
168
|
+
|
169
|
+
assert_queries(1) { person.first_name = 'bar'; person.save! }
|
170
|
+
assert_not_equal old_lock_version, person.reload.lock_version
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
128
174
|
def test_changed_attributes_should_be_preserved_if_save_failure
|
129
175
|
pirate = Pirate.new
|
130
176
|
pirate.parrot_id = 1
|
@@ -145,6 +191,54 @@ class DirtyTest < ActiveRecord::TestCase
|
|
145
191
|
assert !pirate.changed?
|
146
192
|
end
|
147
193
|
|
194
|
+
def test_reverted_changes_are_not_dirty
|
195
|
+
phrase = "shiver me timbers"
|
196
|
+
pirate = Pirate.create!(:catchphrase => phrase)
|
197
|
+
pirate.catchphrase = "*hic*"
|
198
|
+
assert pirate.changed?
|
199
|
+
pirate.catchphrase = phrase
|
200
|
+
assert !pirate.changed?
|
201
|
+
end
|
202
|
+
|
203
|
+
def test_reverted_changes_are_not_dirty_after_multiple_changes
|
204
|
+
phrase = "shiver me timbers"
|
205
|
+
pirate = Pirate.create!(:catchphrase => phrase)
|
206
|
+
10.times do |i|
|
207
|
+
pirate.catchphrase = "*hic*" * i
|
208
|
+
assert pirate.changed?
|
209
|
+
end
|
210
|
+
assert pirate.changed?
|
211
|
+
pirate.catchphrase = phrase
|
212
|
+
assert !pirate.changed?
|
213
|
+
end
|
214
|
+
|
215
|
+
|
216
|
+
def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back
|
217
|
+
pirate = Pirate.create!(:catchphrase => "Yar!")
|
218
|
+
|
219
|
+
pirate.parrot_id = 1
|
220
|
+
assert pirate.changed?
|
221
|
+
assert pirate.parrot_id_changed?
|
222
|
+
assert !pirate.catchphrase_changed?
|
223
|
+
|
224
|
+
pirate.parrot_id = nil
|
225
|
+
assert !pirate.changed?
|
226
|
+
assert !pirate.parrot_id_changed?
|
227
|
+
assert !pirate.catchphrase_changed?
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_save_should_store_serialized_attributes_even_with_partial_updates
|
231
|
+
with_partial_updates(Topic) do
|
232
|
+
topic = Topic.create!(:content => {:a => "a"})
|
233
|
+
topic.content[:b] = "b"
|
234
|
+
#assert topic.changed? # Known bug, will fail
|
235
|
+
topic.save!
|
236
|
+
assert_equal "b", topic.content[:b]
|
237
|
+
topic.reload
|
238
|
+
assert_equal "b", topic.content[:b]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
148
242
|
private
|
149
243
|
def with_partial_updates(klass, on = true)
|
150
244
|
old = klass.partial_updates?
|
data/test/cases/finder_test.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "cases/helper"
|
2
2
|
require 'models/author'
|
3
|
+
require 'models/categorization'
|
3
4
|
require 'models/comment'
|
4
5
|
require 'models/company'
|
5
6
|
require 'models/topic'
|
@@ -394,6 +395,12 @@ class FinderTest < ActiveRecord::TestCase
|
|
394
395
|
assert_equal '1,1,1', bind('?', os)
|
395
396
|
end
|
396
397
|
|
398
|
+
def test_named_bind_with_postgresql_type_casts
|
399
|
+
l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
|
400
|
+
assert_nothing_raised(&l)
|
401
|
+
assert_equal "#{ActiveRecord::Base.quote_value('10')}::integer '2009-01-01'::date", l.call
|
402
|
+
end
|
403
|
+
|
397
404
|
def test_string_sanitation
|
398
405
|
assert_not_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
|
399
406
|
assert_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something; select table'", ActiveRecord::Base.sanitize("something; select table")
|
data/test/cases/fixtures_test.rb
CHANGED
File without changes
|
data/test/cases/helper.rb
CHANGED
@@ -32,13 +32,13 @@ end
|
|
32
32
|
ActiveRecord::Base.connection.class.class_eval do
|
33
33
|
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/]
|
34
34
|
|
35
|
-
def
|
36
|
-
$
|
37
|
-
$
|
38
|
-
|
35
|
+
def execute_with_query_record(sql, name = nil, &block)
|
36
|
+
$queries_executed ||= []
|
37
|
+
$queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
|
38
|
+
execute_without_query_record(sql, name, &block)
|
39
39
|
end
|
40
40
|
|
41
|
-
alias_method_chain :execute, :
|
41
|
+
alias_method_chain :execute, :query_record
|
42
42
|
end
|
43
43
|
|
44
44
|
# Make with_scope public for tests
|
@@ -191,6 +191,13 @@ class InheritanceTest < ActiveRecord::TestCase
|
|
191
191
|
assert_not_nil account.instance_variable_get("@firm"), "nil proves eager load failed"
|
192
192
|
end
|
193
193
|
|
194
|
+
def test_eager_load_belongs_to_primary_key_quoting
|
195
|
+
con = Account.connection
|
196
|
+
assert_sql(/\(#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} IN \(1\)\)/) do
|
197
|
+
Account.find(1, :include => :firm)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
194
201
|
def test_alt_eager_loading
|
195
202
|
switch_to_alt_inheritance_column
|
196
203
|
test_eager_load_belongs_to_something_inherited
|
@@ -223,11 +230,11 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase
|
|
223
230
|
fixtures :companies
|
224
231
|
|
225
232
|
def setup
|
226
|
-
Dependencies.log_activity = true
|
233
|
+
ActiveSupport::Dependencies.log_activity = true
|
227
234
|
end
|
228
235
|
|
229
236
|
def teardown
|
230
|
-
Dependencies.log_activity = false
|
237
|
+
ActiveSupport::Dependencies.log_activity = false
|
231
238
|
self.class.const_remove :FirmOnTheFly rescue nil
|
232
239
|
Firm.const_remove :FirmOnTheFly rescue nil
|
233
240
|
end
|
@@ -2,6 +2,7 @@ require "cases/helper"
|
|
2
2
|
require 'models/topic'
|
3
3
|
require 'models/developer'
|
4
4
|
require 'models/reply'
|
5
|
+
require 'models/minimalistic'
|
5
6
|
|
6
7
|
class Topic; def after_find() end end
|
7
8
|
class Developer; def after_find() end end
|
@@ -44,6 +45,14 @@ class TopicObserver < ActiveRecord::Observer
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
48
|
+
class MinimalisticObserver < ActiveRecord::Observer
|
49
|
+
attr_reader :minimalistic
|
50
|
+
|
51
|
+
def after_find(minimalistic)
|
52
|
+
@minimalistic = minimalistic
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
47
56
|
class MultiObserver < ActiveRecord::Observer
|
48
57
|
attr_reader :record
|
49
58
|
|
@@ -65,7 +74,7 @@ class MultiObserver < ActiveRecord::Observer
|
|
65
74
|
end
|
66
75
|
|
67
76
|
class LifecycleTest < ActiveRecord::TestCase
|
68
|
-
fixtures :topics, :developers
|
77
|
+
fixtures :topics, :developers, :minimalistics
|
69
78
|
|
70
79
|
def test_before_destroy
|
71
80
|
original_count = Topic.count
|
@@ -134,6 +143,50 @@ class LifecycleTest < ActiveRecord::TestCase
|
|
134
143
|
assert_equal developer.name, multi_observer.record.name
|
135
144
|
end
|
136
145
|
|
146
|
+
def test_after_find_can_be_observed_when_its_not_defined_on_the_model
|
147
|
+
observer = MinimalisticObserver.instance
|
148
|
+
assert_equal Minimalistic, MinimalisticObserver.observed_class
|
149
|
+
|
150
|
+
minimalistic = Minimalistic.find(1)
|
151
|
+
assert_equal minimalistic, observer.minimalistic
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_after_find_can_be_observed_when_its_defined_on_the_model
|
155
|
+
observer = TopicObserver.instance
|
156
|
+
assert_equal Topic, TopicObserver.observed_class
|
157
|
+
|
158
|
+
topic = Topic.find(1)
|
159
|
+
assert_equal topic, observer.topic
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_after_find_is_not_created_if_its_not_used
|
163
|
+
# use a fresh class so an observer can't have defined an
|
164
|
+
# after_find on it
|
165
|
+
model_class = Class.new(ActiveRecord::Base)
|
166
|
+
observer_class = Class.new(ActiveRecord::Observer)
|
167
|
+
observer_class.observe(model_class)
|
168
|
+
|
169
|
+
observer = observer_class.instance
|
170
|
+
|
171
|
+
assert !model_class.method_defined?(:after_find)
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_after_find_is_not_clobbered_if_it_already_exists
|
175
|
+
# use a fresh observer class so we can instantiate it (Observer is
|
176
|
+
# a Singleton)
|
177
|
+
model_class = Class.new(ActiveRecord::Base) do
|
178
|
+
def after_find; end
|
179
|
+
end
|
180
|
+
original_method = model_class.instance_method(:after_find)
|
181
|
+
observer_class = Class.new(ActiveRecord::Observer) do
|
182
|
+
def after_find; end
|
183
|
+
end
|
184
|
+
observer_class.observe(model_class)
|
185
|
+
|
186
|
+
observer = observer_class.instance
|
187
|
+
assert_equal original_method, model_class.instance_method(:after_find)
|
188
|
+
end
|
189
|
+
|
137
190
|
def test_invalid_observer
|
138
191
|
assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
|
139
192
|
end
|
data/test/cases/locking_test.rb
CHANGED
@@ -29,10 +29,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|
29
29
|
assert_equal 0, p1.lock_version
|
30
30
|
assert_equal 0, p2.lock_version
|
31
31
|
|
32
|
+
p1.first_name = 'stu'
|
32
33
|
p1.save!
|
33
34
|
assert_equal 1, p1.lock_version
|
34
35
|
assert_equal 0, p2.lock_version
|
35
36
|
|
37
|
+
p2.first_name = 'sue'
|
36
38
|
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
|
37
39
|
end
|
38
40
|
|
@@ -42,11 +44,14 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|
42
44
|
assert_equal 0, p1.lock_version
|
43
45
|
assert_equal 0, p2.lock_version
|
44
46
|
|
47
|
+
p1.first_name = 'stu'
|
45
48
|
p1.save!
|
46
49
|
assert_equal 1, p1.lock_version
|
47
50
|
assert_equal 0, p2.lock_version
|
48
51
|
|
52
|
+
p2.first_name = 'sue'
|
49
53
|
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
|
54
|
+
p2.first_name = 'sue2'
|
50
55
|
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
|
51
56
|
end
|
52
57
|
|
@@ -54,15 +59,18 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|
54
59
|
p1 = Person.new(:first_name => 'anika')
|
55
60
|
assert_equal 0, p1.lock_version
|
56
61
|
|
62
|
+
p1.first_name = 'anika2'
|
57
63
|
p1.save!
|
58
64
|
p2 = Person.find(p1.id)
|
59
65
|
assert_equal 0, p1.lock_version
|
60
66
|
assert_equal 0, p2.lock_version
|
61
67
|
|
68
|
+
p1.first_name = 'anika3'
|
62
69
|
p1.save!
|
63
70
|
assert_equal 1, p1.lock_version
|
64
71
|
assert_equal 0, p2.lock_version
|
65
72
|
|
73
|
+
p2.first_name = 'sue'
|
66
74
|
assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
|
67
75
|
end
|
68
76
|
|
@@ -81,10 +89,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|
81
89
|
assert_equal 0, t1.version
|
82
90
|
assert_equal 0, t2.version
|
83
91
|
|
92
|
+
t1.tps_report_number = 700
|
84
93
|
t1.save!
|
85
94
|
assert_equal 1, t1.version
|
86
95
|
assert_equal 0, t2.version
|
87
96
|
|
97
|
+
t2.tps_report_number = 800
|
88
98
|
assert_raises(ActiveRecord::StaleObjectError) { t2.save! }
|
89
99
|
end
|
90
100
|
|
@@ -93,6 +103,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|
93
103
|
assert_equal 0, p1.lock_version
|
94
104
|
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
|
95
105
|
|
106
|
+
p1.first_name = 'bianca2'
|
96
107
|
p1.save!
|
97
108
|
assert_equal 1, p1.lock_version
|
98
109
|
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
|
@@ -146,6 +157,15 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|
146
157
|
assert ref.save
|
147
158
|
end
|
148
159
|
|
160
|
+
# Useful for partial updates, don't only update the lock_version if there
|
161
|
+
# is nothing else being updated.
|
162
|
+
def test_update_without_attributes_does_not_only_update_lock_version
|
163
|
+
assert_nothing_raised do
|
164
|
+
p1 = Person.new(:first_name => 'anika')
|
165
|
+
p1.send(:update_with_lock, [])
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
149
169
|
private
|
150
170
|
|
151
171
|
def add_counter_column_to(model)
|
@@ -6,7 +6,7 @@ require 'models/post'
|
|
6
6
|
require 'models/category'
|
7
7
|
|
8
8
|
class MethodScopingTest < ActiveRecord::TestCase
|
9
|
-
fixtures :developers, :projects, :comments, :posts
|
9
|
+
fixtures :developers, :projects, :comments, :posts, :developers_projects
|
10
10
|
|
11
11
|
def test_set_conditions
|
12
12
|
Developer.with_scope(:find => { :conditions => 'just a test...' }) do
|
@@ -87,6 +87,16 @@ class MethodScopingTest < ActiveRecord::TestCase
|
|
87
87
|
assert_equal 1, scoped_developers.size
|
88
88
|
end
|
89
89
|
|
90
|
+
def test_scoped_find_joins
|
91
|
+
scoped_developers = Developer.with_scope(:find => { :joins => 'JOIN developers_projects ON id = developer_id' } ) do
|
92
|
+
Developer.find(:all, :conditions => 'developers_projects.project_id = 2')
|
93
|
+
end
|
94
|
+
assert scoped_developers.include?(developers(:david))
|
95
|
+
assert !scoped_developers.include?(developers(:jamis))
|
96
|
+
assert_equal 1, scoped_developers.size
|
97
|
+
assert_equal developers(:david).attributes, scoped_developers.first.attributes
|
98
|
+
end
|
99
|
+
|
90
100
|
def test_scoped_count_include
|
91
101
|
# with the include, will retrieve only developers for the given project
|
92
102
|
Developer.with_scope(:find => { :include => :projects }) do
|
@@ -3,6 +3,7 @@ require 'bigdecimal/util'
|
|
3
3
|
|
4
4
|
require 'models/person'
|
5
5
|
require 'models/topic'
|
6
|
+
require 'models/developer'
|
6
7
|
|
7
8
|
require MIGRATIONS_ROOT + "/valid/1_people_have_last_names"
|
8
9
|
require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
|
@@ -153,9 +154,10 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
153
154
|
|
154
155
|
t.column :default_int, :integer
|
155
156
|
|
156
|
-
t.column :one_int,
|
157
|
-
t.column :four_int,
|
158
|
-
t.column :eight_int,
|
157
|
+
t.column :one_int, :integer, :limit => 1
|
158
|
+
t.column :four_int, :integer, :limit => 4
|
159
|
+
t.column :eight_int, :integer, :limit => 8
|
160
|
+
t.column :eleven_int, :integer, :limit => 11
|
159
161
|
end
|
160
162
|
end
|
161
163
|
|
@@ -167,12 +169,20 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
167
169
|
one = columns.detect { |c| c.name == "one_int" }
|
168
170
|
four = columns.detect { |c| c.name == "four_int" }
|
169
171
|
eight = columns.detect { |c| c.name == "eight_int" }
|
172
|
+
eleven = columns.detect { |c| c.name == "eleven_int" }
|
170
173
|
|
171
174
|
if current_adapter?(:PostgreSQLAdapter)
|
172
175
|
assert_equal 'integer', default.sql_type
|
173
176
|
assert_equal 'smallint', one.sql_type
|
174
177
|
assert_equal 'integer', four.sql_type
|
175
178
|
assert_equal 'bigint', eight.sql_type
|
179
|
+
assert_equal 'integer', eleven.sql_type
|
180
|
+
elsif current_adapter?(:MysqlAdapter)
|
181
|
+
assert_match 'int(11)', default.sql_type
|
182
|
+
assert_match 'tinyint', one.sql_type
|
183
|
+
assert_match 'int', four.sql_type
|
184
|
+
assert_match 'bigint', eight.sql_type
|
185
|
+
assert_match 'int(11)', eleven.sql_type
|
176
186
|
elsif current_adapter?(:OracleAdapter)
|
177
187
|
assert_equal 'NUMBER(38)', default.sql_type
|
178
188
|
assert_equal 'NUMBER(1)', one.sql_type
|
@@ -227,6 +237,39 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
227
237
|
end
|
228
238
|
end
|
229
239
|
|
240
|
+
def test_create_table_with_timestamps_should_create_datetime_columns
|
241
|
+
table_name = :testings
|
242
|
+
|
243
|
+
Person.connection.create_table table_name do |t|
|
244
|
+
t.timestamps
|
245
|
+
end
|
246
|
+
created_columns = Person.connection.columns(table_name)
|
247
|
+
|
248
|
+
created_at_column = created_columns.detect {|c| c.name == 'created_at' }
|
249
|
+
updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
|
250
|
+
|
251
|
+
assert created_at_column.null
|
252
|
+
assert updated_at_column.null
|
253
|
+
ensure
|
254
|
+
Person.connection.drop_table table_name rescue nil
|
255
|
+
end
|
256
|
+
|
257
|
+
def test_create_table_with_timestamps_should_create_datetime_columns_with_options
|
258
|
+
table_name = :testings
|
259
|
+
|
260
|
+
Person.connection.create_table table_name do |t|
|
261
|
+
t.timestamps :null => false
|
262
|
+
end
|
263
|
+
created_columns = Person.connection.columns(table_name)
|
264
|
+
|
265
|
+
created_at_column = created_columns.detect {|c| c.name == 'created_at' }
|
266
|
+
updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
|
267
|
+
|
268
|
+
assert !created_at_column.null
|
269
|
+
assert !updated_at_column.null
|
270
|
+
ensure
|
271
|
+
Person.connection.drop_table table_name rescue nil
|
272
|
+
end
|
230
273
|
|
231
274
|
# SQL Server, Sybase, and SQLite3 will not allow you to add a NOT NULL
|
232
275
|
# column to a table without a default value.
|
@@ -399,10 +442,7 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
399
442
|
|
400
443
|
ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
|
401
444
|
Person.reset_column_information
|
402
|
-
Person.
|
403
|
-
jonnyg = Person.find(:first)
|
404
|
-
assert_equal 127, jonnyg.intelligence_quotient
|
405
|
-
jonnyg.destroy
|
445
|
+
assert_match /tinyint/, Person.columns_hash['intelligence_quotient'].sql_type
|
406
446
|
ensure
|
407
447
|
ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
|
408
448
|
end
|
@@ -483,6 +523,37 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
483
523
|
end
|
484
524
|
end
|
485
525
|
|
526
|
+
def test_rename_column_preserves_default_value_not_null
|
527
|
+
begin
|
528
|
+
default_before = Developer.connection.columns("developers").find { |c| c.name == "salary" }.default
|
529
|
+
assert_equal 70000, default_before
|
530
|
+
Developer.connection.rename_column "developers", "salary", "anual_salary"
|
531
|
+
Developer.reset_column_information
|
532
|
+
assert Developer.column_names.include?("anual_salary")
|
533
|
+
default_after = Developer.connection.columns("developers").find { |c| c.name == "anual_salary" }.default
|
534
|
+
assert_equal 70000, default_after
|
535
|
+
ensure
|
536
|
+
Developer.connection.rename_column "developers", "anual_salary", "salary"
|
537
|
+
Developer.reset_column_information
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
def test_rename_nonexistent_column
|
542
|
+
ActiveRecord::Base.connection.create_table(:hats) do |table|
|
543
|
+
table.column :hat_name, :string, :default => nil
|
544
|
+
end
|
545
|
+
exception = if current_adapter?(:PostgreSQLAdapter)
|
546
|
+
ActiveRecord::StatementInvalid
|
547
|
+
else
|
548
|
+
ActiveRecord::ActiveRecordError
|
549
|
+
end
|
550
|
+
assert_raises(exception) do
|
551
|
+
Person.connection.rename_column "hats", "nonexistent", "should_fail"
|
552
|
+
end
|
553
|
+
ensure
|
554
|
+
ActiveRecord::Base.connection.drop_table(:hats)
|
555
|
+
end
|
556
|
+
|
486
557
|
def test_rename_column_with_sql_reserved_word
|
487
558
|
begin
|
488
559
|
assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
|
@@ -662,6 +733,55 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
662
733
|
Person.connection.drop_table :testings rescue nil
|
663
734
|
end
|
664
735
|
|
736
|
+
def test_keeping_default_and_notnull_constaint_on_change
|
737
|
+
Person.connection.create_table :testings do |t|
|
738
|
+
t.column :title, :string
|
739
|
+
end
|
740
|
+
person_klass = Class.new(Person)
|
741
|
+
person_klass.set_table_name 'testings'
|
742
|
+
|
743
|
+
person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
|
744
|
+
person_klass.reset_column_information
|
745
|
+
assert_equal 99, person_klass.columns_hash["wealth"].default
|
746
|
+
assert_equal false, person_klass.columns_hash["wealth"].null
|
747
|
+
assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
|
748
|
+
|
749
|
+
# change column default to see that column doesn't lose its not null definition
|
750
|
+
person_klass.connection.change_column_default "testings", "wealth", 100
|
751
|
+
person_klass.reset_column_information
|
752
|
+
assert_equal 100, person_klass.columns_hash["wealth"].default
|
753
|
+
assert_equal false, person_klass.columns_hash["wealth"].null
|
754
|
+
|
755
|
+
# rename column to see that column doesn't lose its not null and/or default definition
|
756
|
+
person_klass.connection.rename_column "testings", "wealth", "money"
|
757
|
+
person_klass.reset_column_information
|
758
|
+
assert_nil person_klass.columns_hash["wealth"]
|
759
|
+
assert_equal 100, person_klass.columns_hash["money"].default
|
760
|
+
assert_equal false, person_klass.columns_hash["money"].null
|
761
|
+
|
762
|
+
# change column
|
763
|
+
person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
|
764
|
+
person_klass.reset_column_information
|
765
|
+
assert_equal 1000, person_klass.columns_hash["money"].default
|
766
|
+
assert_equal false, person_klass.columns_hash["money"].null
|
767
|
+
|
768
|
+
# change column, make it nullable and clear default
|
769
|
+
person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
|
770
|
+
person_klass.reset_column_information
|
771
|
+
assert_nil person_klass.columns_hash["money"].default
|
772
|
+
assert_equal true, person_klass.columns_hash["money"].null
|
773
|
+
|
774
|
+
# change_column_null, make it not nullable and set null values to a default value
|
775
|
+
person_klass.connection.execute('UPDATE testings SET money = NULL')
|
776
|
+
person_klass.connection.change_column_null "testings", "money", false, 2000
|
777
|
+
person_klass.reset_column_information
|
778
|
+
assert_nil person_klass.columns_hash["money"].default
|
779
|
+
assert_equal false, person_klass.columns_hash["money"].null
|
780
|
+
assert_equal [2000], Person.connection.select_values("SELECT money FROM testings").map { |s| s.to_i }.sort
|
781
|
+
ensure
|
782
|
+
Person.connection.drop_table :testings rescue nil
|
783
|
+
end
|
784
|
+
|
665
785
|
def test_change_column_default_to_null
|
666
786
|
Person.connection.change_column_default "people", "first_name", nil
|
667
787
|
Person.reset_column_information
|
@@ -799,6 +919,21 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
799
919
|
assert !Reminder.table_exists?
|
800
920
|
end
|
801
921
|
|
922
|
+
def test_migrator_double_up
|
923
|
+
assert_equal(0, ActiveRecord::Migrator.current_version)
|
924
|
+
ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
|
925
|
+
assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) }
|
926
|
+
assert_equal(1, ActiveRecord::Migrator.current_version)
|
927
|
+
end
|
928
|
+
|
929
|
+
def test_migrator_double_down
|
930
|
+
assert_equal(0, ActiveRecord::Migrator.current_version)
|
931
|
+
ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
|
932
|
+
ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1)
|
933
|
+
assert_nothing_raised { ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) }
|
934
|
+
assert_equal(0, ActiveRecord::Migrator.current_version)
|
935
|
+
end
|
936
|
+
|
802
937
|
def test_finds_migrations
|
803
938
|
migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
|
804
939
|
[['1', 'people_have_last_names'],
|
@@ -888,16 +1023,6 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
888
1023
|
ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
|
889
1024
|
assert_equal(0, ActiveRecord::Migrator.current_version)
|
890
1025
|
end
|
891
|
-
|
892
|
-
def test_migrator_run
|
893
|
-
assert_equal(0, ActiveRecord::Migrator.current_version)
|
894
|
-
ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 3)
|
895
|
-
assert_equal(0, ActiveRecord::Migrator.current_version)
|
896
|
-
|
897
|
-
assert_equal(0, ActiveRecord::Migrator.current_version)
|
898
|
-
ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 3)
|
899
|
-
assert_equal(0, ActiveRecord::Migrator.current_version)
|
900
|
-
end
|
901
1026
|
|
902
1027
|
def test_schema_migrations_table_name
|
903
1028
|
ActiveRecord::Base.table_name_prefix = "prefix_"
|
@@ -1077,8 +1202,8 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
1077
1202
|
|
1078
1203
|
def test_timestamps_creates_updated_at_and_created_at
|
1079
1204
|
with_new_table do |t|
|
1080
|
-
t.expects(:column).with(:created_at, :datetime)
|
1081
|
-
t.expects(:column).with(:updated_at, :datetime)
|
1205
|
+
t.expects(:column).with(:created_at, :datetime, kind_of(Hash))
|
1206
|
+
t.expects(:column).with(:updated_at, :datetime, kind_of(Hash))
|
1082
1207
|
t.timestamps
|
1083
1208
|
end
|
1084
1209
|
end
|
@@ -1206,10 +1331,10 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
1206
1331
|
end
|
1207
1332
|
|
1208
1333
|
def integer_column
|
1209
|
-
if current_adapter?(:
|
1210
|
-
"integer"
|
1211
|
-
else
|
1334
|
+
if current_adapter?(:MysqlAdapter)
|
1212
1335
|
'int(11)'
|
1336
|
+
else
|
1337
|
+
'integer'
|
1213
1338
|
end
|
1214
1339
|
end
|
1215
1340
|
|