activerecord 2.3.5 → 2.3.6
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 +33 -0
- data/Rakefile +1 -1
- data/examples/performance.sql +85 -0
- data/lib/active_record.rb +1 -2
- data/lib/active_record/association_preload.rb +9 -2
- data/lib/active_record/associations.rb +48 -38
- data/lib/active_record/associations/association_collection.rb +15 -11
- data/lib/active_record/associations/association_proxy.rb +16 -6
- data/lib/active_record/associations/belongs_to_association.rb +11 -1
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -10
- data/lib/active_record/associations/has_many_association.rb +5 -0
- data/lib/active_record/associations/has_many_through_association.rb +5 -5
- data/lib/active_record/associations/has_one_association.rb +10 -1
- data/lib/active_record/attribute_methods.rb +5 -1
- data/lib/active_record/autosave_association.rb +66 -35
- data/lib/active_record/base.rb +77 -36
- data/lib/active_record/batches.rb +13 -9
- data/lib/active_record/calculations.rb +6 -3
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +64 -10
- data/lib/active_record/connection_adapters/abstract_adapter.rb +2 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +31 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +31 -66
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -2
- data/lib/active_record/dirty.rb +2 -2
- data/lib/active_record/fixtures.rb +1 -0
- data/lib/active_record/locking/optimistic.rb +34 -1
- data/lib/active_record/migration.rb +5 -0
- data/lib/active_record/nested_attributes.rb +64 -52
- data/lib/active_record/reflection.rb +66 -1
- data/lib/active_record/schema.rb +5 -1
- data/lib/active_record/schema_dumper.rb +3 -0
- data/lib/active_record/serializers/json_serializer.rb +1 -1
- data/lib/active_record/validations.rb +13 -1
- data/lib/active_record/version.rb +1 -1
- data/test/cases/active_schema_test_mysql.rb +22 -0
- data/test/cases/associations/belongs_to_associations_test.rb +13 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +8 -7
- data/test/cases/associations/eager_test.rb +7 -1
- data/test/cases/associations/has_many_associations_test.rb +26 -0
- data/test/cases/associations/inverse_associations_test.rb +566 -0
- data/test/cases/associations_test.rb +10 -0
- data/test/cases/autosave_association_test.rb +86 -10
- data/test/cases/base_test.rb +29 -0
- data/test/cases/batches_test.rb +20 -0
- data/test/cases/calculations_test.rb +2 -3
- data/test/cases/encoding_test.rb +6 -0
- data/test/cases/finder_test.rb +19 -3
- data/test/cases/fixtures_test.rb +5 -0
- data/test/cases/json_serialization_test.rb +14 -0
- data/test/cases/locking_test.rb +48 -3
- data/test/cases/migration_test.rb +115 -0
- data/test/cases/modules_test.rb +28 -0
- data/test/cases/named_scope_test.rb +1 -1
- data/test/cases/nested_attributes_test.rb +239 -7
- data/test/cases/query_cache_test.rb +7 -1
- data/test/cases/reflection_test.rb +47 -7
- data/test/cases/schema_test_postgresql.rb +2 -2
- data/test/cases/validations_i18n_test.rb +6 -36
- data/test/cases/validations_test.rb +33 -1
- data/test/cases/yaml_serialization_test.rb +11 -0
- data/test/fixtures/faces.yml +11 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/interests.yml +33 -0
- data/test/fixtures/men.yml +5 -0
- data/test/fixtures/zines.yml +5 -0
- data/test/models/author.rb +3 -0
- data/test/models/bird.rb +6 -0
- data/test/models/company_in_module.rb +17 -0
- data/test/models/event_author.rb +5 -0
- data/test/models/face.rb +7 -0
- data/test/models/interest.rb +5 -0
- data/test/models/invoice.rb +4 -0
- data/test/models/line_item.rb +3 -0
- data/test/models/man.rb +9 -0
- data/test/models/parrot.rb +6 -0
- data/test/models/pirate.rb +10 -0
- data/test/models/ship.rb +10 -1
- data/test/models/ship_part.rb +3 -1
- data/test/models/zine.rb +3 -0
- data/test/schema/schema.rb +41 -0
- metadata +37 -11
- data/lib/active_record/i18n_interpolation_deprecation.rb +0 -26
@@ -64,6 +64,16 @@ class AssociationsTest < ActiveRecord::TestCase
|
|
64
64
|
assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
|
65
65
|
assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count"
|
66
66
|
end
|
67
|
+
|
68
|
+
def test_force_reload_is_uncached
|
69
|
+
firm = Firm.create!("name" => "A New Firm, Inc")
|
70
|
+
client = Client.create!("name" => "TheClient.com", :firm => firm)
|
71
|
+
ActiveRecord::Base.cache do
|
72
|
+
firm.clients.each {}
|
73
|
+
assert_queries(0) { assert_not_nil firm.clients.each {} }
|
74
|
+
assert_queries(1) { assert_not_nil firm.clients(true).each {} }
|
75
|
+
end
|
76
|
+
end
|
67
77
|
|
68
78
|
def test_storing_in_pstore
|
69
79
|
require "tmpdir"
|
@@ -3,6 +3,8 @@ require 'models/bird'
|
|
3
3
|
require 'models/company'
|
4
4
|
require 'models/customer'
|
5
5
|
require 'models/developer'
|
6
|
+
require 'models/invoice'
|
7
|
+
require 'models/line_item'
|
6
8
|
require 'models/order'
|
7
9
|
require 'models/parrot'
|
8
10
|
require 'models/person'
|
@@ -30,11 +32,40 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
|
|
30
32
|
assert base.valid_keys_for_has_and_belongs_to_many_association.include?(:autosave)
|
31
33
|
end
|
32
34
|
|
35
|
+
def test_should_not_add_the_same_callbacks_multiple_times_for_has_one
|
36
|
+
assert_no_difference_when_adding_callbacks_twice_for Pirate, :ship
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_should_not_add_the_same_callbacks_multiple_times_for_belongs_to
|
40
|
+
assert_no_difference_when_adding_callbacks_twice_for Ship, :pirate
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_should_not_add_the_same_callbacks_multiple_times_for_has_many
|
44
|
+
assert_no_difference_when_adding_callbacks_twice_for Pirate, :birds
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_should_not_add_the_same_callbacks_multiple_times_for_has_and_belongs_to_many
|
48
|
+
assert_no_difference_when_adding_callbacks_twice_for Pirate, :parrots
|
49
|
+
end
|
50
|
+
|
33
51
|
private
|
34
52
|
|
35
53
|
def base
|
36
54
|
ActiveRecord::Base
|
37
55
|
end
|
56
|
+
|
57
|
+
def assert_no_difference_when_adding_callbacks_twice_for(model, association_name)
|
58
|
+
reflection = model.reflect_on_association(association_name)
|
59
|
+
assert_no_difference "callbacks_for_model(#{model.name}).length" do
|
60
|
+
model.send(:add_autosave_association_callbacks, reflection)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def callbacks_for_model(model)
|
65
|
+
model.instance_variables.grep(/_callbacks$/).map do |ivar|
|
66
|
+
model.instance_variable_get(ivar)
|
67
|
+
end.flatten
|
68
|
+
end
|
38
69
|
end
|
39
70
|
|
40
71
|
class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
|
@@ -663,23 +694,18 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
|
|
663
694
|
|
664
695
|
define_method("test_should_rollback_destructions_if_an_exception_occurred_while_saving_#{association_name}") do
|
665
696
|
2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
|
666
|
-
before = @pirate.send(association_name).map { |c| c }
|
697
|
+
before = @pirate.send(association_name).map { |c| c.mark_for_destruction ; c }
|
667
698
|
|
668
|
-
# Stub the
|
669
|
-
class << before.first
|
670
|
-
def save(*args)
|
671
|
-
super
|
672
|
-
destroy
|
673
|
-
end
|
674
|
-
end
|
699
|
+
# Stub the destroy method of the the second child to raise an exception
|
675
700
|
class << before.last
|
676
|
-
def
|
701
|
+
def destroy(*args)
|
677
702
|
super
|
678
703
|
raise 'Oh noes!'
|
679
704
|
end
|
680
705
|
end
|
681
706
|
|
682
707
|
assert_raise(RuntimeError) { assert !@pirate.save }
|
708
|
+
assert before.first.frozen? # the first child was indeed destroyed
|
683
709
|
assert_equal before, @pirate.reload.send(association_name)
|
684
710
|
end
|
685
711
|
|
@@ -787,6 +813,18 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
|
|
787
813
|
end
|
788
814
|
end
|
789
815
|
|
816
|
+
def test_should_not_save_and_return_false_if_a_callback_cancelled_saving
|
817
|
+
pirate = Pirate.new(:catchphrase => 'Arr')
|
818
|
+
ship = pirate.build_ship(:name => 'The Vile Insanity')
|
819
|
+
ship.cancel_save_from_callback = true
|
820
|
+
|
821
|
+
assert_no_difference 'Pirate.count' do
|
822
|
+
assert_no_difference 'Ship.count' do
|
823
|
+
assert !pirate.save
|
824
|
+
end
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
790
828
|
def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
|
791
829
|
before = [@pirate.catchphrase, @pirate.ship.name]
|
792
830
|
|
@@ -865,6 +903,18 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
|
|
865
903
|
end
|
866
904
|
end
|
867
905
|
|
906
|
+
def test_should_not_save_and_return_false_if_a_callback_cancelled_saving
|
907
|
+
ship = Ship.new(:name => 'The Vile Insanity')
|
908
|
+
pirate = ship.build_pirate(:catchphrase => 'Arr')
|
909
|
+
pirate.cancel_save_from_callback = true
|
910
|
+
|
911
|
+
assert_no_difference 'Ship.count' do
|
912
|
+
assert_no_difference 'Pirate.count' do
|
913
|
+
assert !ship.save
|
914
|
+
end
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
868
918
|
def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
|
869
919
|
before = [@ship.pirate.catchphrase, @ship.name]
|
870
920
|
|
@@ -880,7 +930,6 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
|
|
880
930
|
end
|
881
931
|
|
882
932
|
assert_raise(RuntimeError) { assert !@ship.save }
|
883
|
-
# TODO: Why does using reload on @ship looses the associated pirate?
|
884
933
|
assert_equal before, [@ship.pirate.reload.catchphrase, @ship.reload.name]
|
885
934
|
end
|
886
935
|
|
@@ -957,6 +1006,26 @@ module AutosaveAssociationOnACollectionAssociationTests
|
|
957
1006
|
end
|
958
1007
|
end
|
959
1008
|
|
1009
|
+
def test_should_not_save_and_return_false_if_a_callback_cancelled_saving_in_either_create_or_update
|
1010
|
+
@pirate.catchphrase = 'Changed'
|
1011
|
+
@child_1.name = 'Changed'
|
1012
|
+
@child_1.cancel_save_from_callback = true
|
1013
|
+
|
1014
|
+
assert !@pirate.save
|
1015
|
+
assert_equal "Don' botharrr talkin' like one, savvy?", @pirate.reload.catchphrase
|
1016
|
+
assert_equal "Posideons Killer", @child_1.reload.name
|
1017
|
+
|
1018
|
+
new_pirate = Pirate.new(:catchphrase => 'Arr')
|
1019
|
+
new_child = new_pirate.send(@association_name).build(:name => 'Grace OMalley')
|
1020
|
+
new_child.cancel_save_from_callback = true
|
1021
|
+
|
1022
|
+
assert_no_difference 'Pirate.count' do
|
1023
|
+
assert_no_difference "#{new_child.class.name}.count" do
|
1024
|
+
assert !new_pirate.save
|
1025
|
+
end
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
|
960
1029
|
def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
|
961
1030
|
before = [@pirate.catchphrase, *@pirate.send(@association_name).map(&:name)]
|
962
1031
|
new_names = ['Grace OMalley', 'Privateers Greed']
|
@@ -1140,3 +1209,10 @@ class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCas
|
|
1140
1209
|
assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrots)
|
1141
1210
|
end
|
1142
1211
|
end
|
1212
|
+
|
1213
|
+
class TestAutosaveAssociationWithTouch < ActiveRecord::TestCase
|
1214
|
+
def test_autosave_with_touch_should_not_raise_system_stack_error
|
1215
|
+
invoice = Invoice.create
|
1216
|
+
assert_nothing_raised { invoice.line_items.create(:amount => 10) }
|
1217
|
+
end
|
1218
|
+
end
|
data/test/cases/base_test.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "cases/helper"
|
2
2
|
require 'models/post'
|
3
3
|
require 'models/author'
|
4
|
+
require 'models/event_author'
|
4
5
|
require 'models/topic'
|
5
6
|
require 'models/reply'
|
6
7
|
require 'models/category'
|
@@ -627,6 +628,16 @@ class BasicsTest < ActiveRecord::TestCase
|
|
627
628
|
assert_equal -2, Topic.find(2).replies_count
|
628
629
|
end
|
629
630
|
|
631
|
+
def test_reset_counters
|
632
|
+
assert_equal 1, Topic.find(1).replies_count
|
633
|
+
|
634
|
+
Topic.increment_counter("replies_count", 1)
|
635
|
+
assert_equal 2, Topic.find(1).replies_count
|
636
|
+
|
637
|
+
Topic.reset_counters(1, :replies)
|
638
|
+
assert_equal 1, Topic.find(1).replies_count
|
639
|
+
end
|
640
|
+
|
630
641
|
def test_update_counter
|
631
642
|
category = categories(:general)
|
632
643
|
assert_nil category.categorizations_count
|
@@ -1005,6 +1016,18 @@ class BasicsTest < ActiveRecord::TestCase
|
|
1005
1016
|
assert_equal "changed", post.body
|
1006
1017
|
end
|
1007
1018
|
|
1019
|
+
def test_multiparameter_attribute_assignment_via_association_proxy
|
1020
|
+
multiparameter_date_attribute = {
|
1021
|
+
"ends_on(1i)" => "2004", "ends_on(2i)" => "6", "ends_on(3i)" => "24",
|
1022
|
+
"ends_on(4i)" => "16", "ends_on(5i)" => "24", "ends_on(6i)" => "00"
|
1023
|
+
}
|
1024
|
+
|
1025
|
+
author = Author.create(:name => "dhh")
|
1026
|
+
event = author.events.create(multiparameter_date_attribute)
|
1027
|
+
|
1028
|
+
assert_equal Time.local(2004,6,24,16,24,0),event.ends_on
|
1029
|
+
end
|
1030
|
+
|
1008
1031
|
def test_multiparameter_attributes_on_date
|
1009
1032
|
attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
|
1010
1033
|
topic = Topic.find(1)
|
@@ -1622,6 +1645,12 @@ class BasicsTest < ActiveRecord::TestCase
|
|
1622
1645
|
assert_equal t1.title, t2.title
|
1623
1646
|
end
|
1624
1647
|
|
1648
|
+
def test_reload_with_exclusive_scope
|
1649
|
+
dev = DeveloperCalledDavid.first
|
1650
|
+
dev.update_attributes!( :name => "NotDavid" )
|
1651
|
+
assert_equal dev, dev.reload
|
1652
|
+
end
|
1653
|
+
|
1625
1654
|
def test_define_attr_method_with_value
|
1626
1655
|
k = Class.new( ActiveRecord::Base )
|
1627
1656
|
k.send(:define_attr_method, :table_name, "foo")
|
data/test/cases/batches_test.rb
CHANGED
@@ -58,4 +58,24 @@ class EachTest < ActiveRecord::TestCase
|
|
58
58
|
Post.find_in_batches(:batch_size => post_count + 1) {|batch| assert_kind_of Array, batch }
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
62
|
+
def test_find_in_batches_doesnt_clog_conditions
|
63
|
+
Post.find_in_batches(:conditions => {:id => posts(:welcome).id}) do
|
64
|
+
assert_nothing_raised { Post.find(posts(:thinking).id) }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_each_should_raise_if_select_is_set_without_id
|
69
|
+
assert_raise(RuntimeError) do
|
70
|
+
Post.find_each(:select => :title, :batch_size => 1) { |post| post }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_each_should_execute_if_id_is_in_select
|
75
|
+
assert_queries(4) do
|
76
|
+
Post.find_each(:select => "id, title, type", :batch_size => 2) do |post|
|
77
|
+
assert_kind_of Post, post
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
61
81
|
end
|
@@ -23,8 +23,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
23
23
|
|
24
24
|
def test_should_average_field
|
25
25
|
value = Account.average(:credit_limit)
|
26
|
-
|
27
|
-
assert_equal BigDecimal.new('53.0'), value
|
26
|
+
assert_equal 53.0, value
|
28
27
|
end
|
29
28
|
|
30
29
|
def test_should_return_nil_as_average
|
@@ -298,7 +297,7 @@ class CalculationsTest < ActiveRecord::TestCase
|
|
298
297
|
end
|
299
298
|
|
300
299
|
def test_should_sum_expression
|
301
|
-
assert_equal
|
300
|
+
assert_equal 636, Account.sum("2 * credit_limit").to_i
|
302
301
|
end
|
303
302
|
|
304
303
|
def test_count_with_from_option
|
data/test/cases/finder_test.rb
CHANGED
@@ -518,8 +518,8 @@ class FinderTest < ActiveRecord::TestCase
|
|
518
518
|
end
|
519
519
|
|
520
520
|
def test_string_sanitation
|
521
|
-
assert_not_equal "
|
522
|
-
assert_equal "
|
521
|
+
assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
|
522
|
+
assert_equal "'something; select table'", ActiveRecord::Base.sanitize("something; select table")
|
523
523
|
end
|
524
524
|
|
525
525
|
def test_count
|
@@ -838,7 +838,7 @@ class FinderTest < ActiveRecord::TestCase
|
|
838
838
|
assert c.new_record?
|
839
839
|
end
|
840
840
|
|
841
|
-
def
|
841
|
+
def test_find_or_create_from_one_attribute_should_not_set_attribute_even_when_protected
|
842
842
|
c = Company.find_or_create_by_name({:name => "Fortune 1000", :rating => 1000})
|
843
843
|
assert_equal "Fortune 1000", c.name
|
844
844
|
assert_not_equal 1000, c.rating
|
@@ -846,6 +846,22 @@ class FinderTest < ActiveRecord::TestCase
|
|
846
846
|
assert !c.new_record?
|
847
847
|
end
|
848
848
|
|
849
|
+
def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected_and_also_set_the_hash
|
850
|
+
c = Company.find_or_initialize_by_rating(1000, {:name => "Fortune 1000"})
|
851
|
+
assert_equal "Fortune 1000", c.name
|
852
|
+
assert_equal 1000, c.rating
|
853
|
+
assert c.valid?
|
854
|
+
assert c.new_record?
|
855
|
+
end
|
856
|
+
|
857
|
+
def test_find_or_create_from_one_attribute_should_set_attribute_even_when_protected_and_also_set_the_hash
|
858
|
+
c = Company.find_or_create_by_rating(1000, {:name => "Fortune 1000"})
|
859
|
+
assert_equal "Fortune 1000", c.name
|
860
|
+
assert_equal 1000, c.rating
|
861
|
+
assert c.valid?
|
862
|
+
assert !c.new_record?
|
863
|
+
end
|
864
|
+
|
849
865
|
def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected
|
850
866
|
c = Company.find_or_initialize_by_name_and_rating("Fortune 1000", 1000)
|
851
867
|
assert_equal "Fortune 1000", c.name
|
data/test/cases/fixtures_test.rb
CHANGED
@@ -254,6 +254,11 @@ class FixturesWithoutInstantiationTest < ActiveRecord::TestCase
|
|
254
254
|
assert_nil @unknown
|
255
255
|
end
|
256
256
|
|
257
|
+
def test_visibility_of_accessor_method
|
258
|
+
assert_equal false, respond_to?(:topics, false), "should be private method"
|
259
|
+
assert_equal true, respond_to?(:topics, true), "confirm to respond surely"
|
260
|
+
end
|
261
|
+
|
257
262
|
def test_accessor_methods
|
258
263
|
assert_equal "The First Topic", topics(:first).title
|
259
264
|
assert_equal "Jamis", developers(:jamis).name
|
@@ -43,6 +43,20 @@ class JsonSerializationTest < ActiveRecord::TestCase
|
|
43
43
|
Contact.include_root_in_json = false
|
44
44
|
end
|
45
45
|
|
46
|
+
def test_should_include_root_in_json
|
47
|
+
Contact.include_root_in_json = true
|
48
|
+
json = @contact.to_json(:root => 'json_contact')
|
49
|
+
|
50
|
+
assert_match %r{^\{"json_contact":\{}, json
|
51
|
+
assert_match %r{"name":"Konata Izumi"}, json
|
52
|
+
assert_match %r{"age":16}, json
|
53
|
+
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
54
|
+
assert_match %r{"awesome":true}, json
|
55
|
+
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
56
|
+
ensure
|
57
|
+
Contact.include_root_in_json = false
|
58
|
+
end
|
59
|
+
|
46
60
|
def test_should_encode_all_encodable_attributes
|
47
61
|
json = @contact.to_json
|
48
62
|
|
data/test/cases/locking_test.rb
CHANGED
@@ -38,6 +38,25 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|
38
38
|
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
39
39
|
end
|
40
40
|
|
41
|
+
# See Lighthouse ticket #1966
|
42
|
+
def test_lock_destroy
|
43
|
+
p1 = Person.find(1)
|
44
|
+
p2 = Person.find(1)
|
45
|
+
assert_equal 0, p1.lock_version
|
46
|
+
assert_equal 0, p2.lock_version
|
47
|
+
|
48
|
+
p1.first_name = 'stu'
|
49
|
+
p1.save!
|
50
|
+
assert_equal 1, p1.lock_version
|
51
|
+
assert_equal 0, p2.lock_version
|
52
|
+
|
53
|
+
assert_raises(ActiveRecord::StaleObjectError) { p2.destroy }
|
54
|
+
|
55
|
+
assert p1.destroy
|
56
|
+
assert_equal true, p1.frozen?
|
57
|
+
assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) }
|
58
|
+
end
|
59
|
+
|
41
60
|
def test_lock_repeating
|
42
61
|
p1 = Person.find(1)
|
43
62
|
p2 = Person.find(1)
|
@@ -150,6 +169,32 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|
150
169
|
end
|
151
170
|
end
|
152
171
|
end
|
172
|
+
|
173
|
+
# See Lighthouse ticket #1966
|
174
|
+
def test_destroy_dependents
|
175
|
+
# Establish dependent relationship between People and LegacyThing
|
176
|
+
add_counter_column_to(Person, 'legacy_things_count')
|
177
|
+
LegacyThing.connection.add_column LegacyThing.table_name, 'person_id', :integer
|
178
|
+
LegacyThing.reset_column_information
|
179
|
+
LegacyThing.class_eval do
|
180
|
+
belongs_to :person, :counter_cache => true
|
181
|
+
end
|
182
|
+
Person.class_eval do
|
183
|
+
has_many :legacy_things, :dependent => :destroy
|
184
|
+
end
|
185
|
+
|
186
|
+
# Make sure that counter incrementing doesn't cause problems
|
187
|
+
p1 = Person.new(:first_name => 'fjord')
|
188
|
+
p1.save!
|
189
|
+
t = LegacyThing.new(:person => p1)
|
190
|
+
t.save!
|
191
|
+
p1.reload
|
192
|
+
assert_equal 1, p1.legacy_things_count
|
193
|
+
assert p1.destroy
|
194
|
+
assert_equal true, p1.frozen?
|
195
|
+
assert_raises(ActiveRecord::RecordNotFound) { Person.find(p1.id) }
|
196
|
+
assert_raises(ActiveRecord::RecordNotFound) { LegacyThing.find(t.id) }
|
197
|
+
end
|
153
198
|
|
154
199
|
def test_quote_table_name
|
155
200
|
ref = references(:michael_magician)
|
@@ -168,11 +213,11 @@ class OptimisticLockingTest < ActiveRecord::TestCase
|
|
168
213
|
|
169
214
|
private
|
170
215
|
|
171
|
-
def add_counter_column_to(model)
|
172
|
-
model.connection.add_column model.table_name,
|
216
|
+
def add_counter_column_to(model, col='test_count')
|
217
|
+
model.connection.add_column model.table_name, col, :integer, :null => false, :default => 0
|
173
218
|
model.reset_column_information
|
174
219
|
# OpenBase does not set a value to existing rows when adding a not null default column
|
175
|
-
model.update_all(
|
220
|
+
model.update_all(col => 0) if current_adapter?(:OpenBaseAdapter)
|
176
221
|
end
|
177
222
|
|
178
223
|
def remove_counter_column_from(model)
|
@@ -92,6 +92,14 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
92
92
|
assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
|
93
93
|
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
|
94
94
|
assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
|
95
|
+
assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) }
|
96
|
+
assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
|
97
|
+
assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => {:last_name => 10}) }
|
98
|
+
assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) }
|
99
|
+
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => 10) }
|
100
|
+
assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
|
101
|
+
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20}) }
|
102
|
+
assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
|
95
103
|
end
|
96
104
|
|
97
105
|
# quoting
|
@@ -111,6 +119,40 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
111
119
|
end
|
112
120
|
end
|
113
121
|
|
122
|
+
def test_add_index_length_limit
|
123
|
+
good_index_name = 'x' * Person.connection.index_name_length
|
124
|
+
too_long_index_name = good_index_name + 'x'
|
125
|
+
assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => too_long_index_name) }
|
126
|
+
assert !Person.connection.index_exists?("people", too_long_index_name, false)
|
127
|
+
assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => good_index_name) }
|
128
|
+
assert Person.connection.index_exists?("people", good_index_name, false)
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_remove_nonexistent_index
|
132
|
+
# we do this by name, so OpenBase is a wash as noted above
|
133
|
+
unless current_adapter?(:OpenBaseAdapter)
|
134
|
+
assert_nothing_raised { Person.connection.remove_index("people", "no_such_index") }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_rename_index
|
139
|
+
unless current_adapter?(:OpenBaseAdapter)
|
140
|
+
# keep the names short to make Oracle and similar behave
|
141
|
+
Person.connection.add_index('people', [:first_name], :name => 'old_idx')
|
142
|
+
assert_nothing_raised { Person.connection.rename_index('people', 'old_idx', 'new_idx') }
|
143
|
+
# if the adapter doesn't support the indexes call, pick defaults that let the test pass
|
144
|
+
assert !Person.connection.index_exists?('people', 'old_idx', false)
|
145
|
+
assert Person.connection.index_exists?('people', 'new_idx', true)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_double_add_index
|
150
|
+
unless current_adapter?(:OpenBaseAdapter)
|
151
|
+
Person.connection.add_index('people', [:first_name], :name => 'some_idx')
|
152
|
+
assert_nothing_raised { Person.connection.add_index('people', [:first_name], :name => 'some_idx') }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
114
156
|
def testing_table_with_only_foo_attribute
|
115
157
|
Person.connection.create_table :testings, :id => false do |t|
|
116
158
|
t.column :foo, :string
|
@@ -311,6 +353,13 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
311
353
|
Person.connection.drop_table table_name rescue nil
|
312
354
|
end
|
313
355
|
|
356
|
+
def test_create_table_without_a_block
|
357
|
+
table_name = :testings
|
358
|
+
Person.connection.create_table table_name
|
359
|
+
ensure
|
360
|
+
Person.connection.drop_table table_name rescue nil
|
361
|
+
end
|
362
|
+
|
314
363
|
# Sybase, and SQLite3 will not allow you to add a NOT NULL
|
315
364
|
# column to a table without a default value.
|
316
365
|
unless current_adapter?(:SybaseAdapter, :SQLiteAdapter)
|
@@ -516,6 +565,53 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
516
565
|
assert !Person.column_methods_hash.include?(:last_name)
|
517
566
|
end
|
518
567
|
|
568
|
+
if current_adapter?(:MysqlAdapter)
|
569
|
+
def testing_table_for_positioning
|
570
|
+
Person.connection.create_table :testings, :id => false do |t|
|
571
|
+
t.column :first, :integer
|
572
|
+
t.column :second, :integer
|
573
|
+
t.column :third, :integer
|
574
|
+
end
|
575
|
+
|
576
|
+
yield Person.connection
|
577
|
+
ensure
|
578
|
+
Person.connection.drop_table :testings rescue nil
|
579
|
+
end
|
580
|
+
protected :testing_table_for_positioning
|
581
|
+
|
582
|
+
def test_column_positioning
|
583
|
+
testing_table_for_positioning do |conn|
|
584
|
+
assert_equal %w(first second third), conn.columns(:testings).map {|c| c.name }
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
def test_add_column_with_positioning
|
589
|
+
testing_table_for_positioning do |conn|
|
590
|
+
conn.add_column :testings, :new_col, :integer
|
591
|
+
assert_equal %w(first second third new_col), conn.columns(:testings).map {|c| c.name }
|
592
|
+
end
|
593
|
+
testing_table_for_positioning do |conn|
|
594
|
+
conn.add_column :testings, :new_col, :integer, :first => true
|
595
|
+
assert_equal %w(new_col first second third), conn.columns(:testings).map {|c| c.name }
|
596
|
+
end
|
597
|
+
testing_table_for_positioning do |conn|
|
598
|
+
conn.add_column :testings, :new_col, :integer, :after => :first
|
599
|
+
assert_equal %w(first new_col second third), conn.columns(:testings).map {|c| c.name }
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
def test_change_column_with_positioning
|
604
|
+
testing_table_for_positioning do |conn|
|
605
|
+
conn.change_column :testings, :second, :integer, :first => true
|
606
|
+
assert_equal %w(second first third), conn.columns(:testings).map {|c| c.name }
|
607
|
+
end
|
608
|
+
testing_table_for_positioning do |conn|
|
609
|
+
conn.change_column :testings, :second, :integer, :after => :third
|
610
|
+
assert_equal %w(first third second), conn.columns(:testings).map {|c| c.name }
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
519
615
|
def test_add_rename
|
520
616
|
Person.delete_all
|
521
617
|
|
@@ -1023,6 +1119,25 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
1023
1119
|
load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
|
1024
1120
|
end
|
1025
1121
|
|
1122
|
+
def test_target_version_zero_should_run_only_once
|
1123
|
+
# migrate up to 1
|
1124
|
+
ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
|
1125
|
+
|
1126
|
+
# migrate down to 0
|
1127
|
+
ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
|
1128
|
+
|
1129
|
+
# now unload the migrations that have been defined
|
1130
|
+
PeopleHaveLastNames.unloadable
|
1131
|
+
ActiveSupport::Dependencies.remove_unloadable_constants!
|
1132
|
+
|
1133
|
+
# migrate down to 0 again
|
1134
|
+
ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
|
1135
|
+
|
1136
|
+
assert !defined? PeopleHaveLastNames
|
1137
|
+
ensure
|
1138
|
+
load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
|
1139
|
+
end
|
1140
|
+
|
1026
1141
|
def test_migrator_interleaved_migrations
|
1027
1142
|
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
|
1028
1143
|
|