activerecord 1.14.4 → 1.15.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.
- data/CHANGELOG +400 -1
- data/README +2 -2
- data/RUNNING_UNIT_TESTS +21 -3
- data/Rakefile +55 -10
- data/lib/active_record.rb +10 -4
- data/lib/active_record/acts/list.rb +15 -4
- data/lib/active_record/acts/nested_set.rb +11 -12
- data/lib/active_record/acts/tree.rb +13 -14
- data/lib/active_record/aggregations.rb +46 -22
- data/lib/active_record/associations.rb +213 -162
- data/lib/active_record/associations/association_collection.rb +45 -15
- data/lib/active_record/associations/association_proxy.rb +32 -13
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +18 -18
- data/lib/active_record/associations/has_many_association.rb +37 -17
- data/lib/active_record/associations/has_many_through_association.rb +120 -30
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +282 -203
- data/lib/active_record/calculations.rb +95 -54
- data/lib/active_record/callbacks.rb +13 -24
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +12 -1
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb.rej +21 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -9
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +121 -37
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -23
- data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -11
- data/lib/active_record/connection_adapters/firebird_adapter.rb +364 -50
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -33
- data/lib/active_record/connection_adapters/openbase_adapter.rb +4 -3
- data/lib/active_record/connection_adapters/oracle_adapter.rb +151 -127
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +125 -48
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +38 -10
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +183 -155
- data/lib/active_record/connection_adapters/sybase_adapter.rb +190 -212
- data/lib/active_record/deprecated_associations.rb +24 -10
- data/lib/active_record/deprecated_finders.rb +4 -1
- data/lib/active_record/fixtures.rb +37 -23
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +8 -5
- data/lib/active_record/observer.rb +73 -34
- data/lib/active_record/reflection.rb +21 -7
- data/lib/active_record/schema_dumper.rb +33 -5
- data/lib/active_record/timestamp.rb +23 -34
- data/lib/active_record/transactions.rb +37 -30
- data/lib/active_record/validations.rb +46 -30
- data/lib/active_record/vendor/mysql.rb +20 -5
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record/wrappings.rb +1 -2
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +5 -1
- data/test/abstract_unit.rb +18 -8
- data/test/{active_schema_mysql.rb → active_schema_test_mysql.rb} +2 -2
- data/test/adapter_test.rb +9 -7
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +29 -0
- data/test/{association_callbacks_test.rb → associations/callbacks_test.rb} +10 -8
- data/test/{associations_cascaded_eager_loading_test.rb → associations/cascaded_eager_loading_test.rb} +35 -3
- data/test/{associations_go_eager_test.rb → associations/eager_test.rb} +36 -2
- data/test/{associations_extensions_test.rb → associations/extension_test.rb} +5 -0
- data/test/{associations_join_model_test.rb → associations/join_model_test.rb} +118 -8
- data/test/associations_test.rb +339 -45
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +321 -67
- data/test/calculations_test.rb +48 -10
- data/test/callbacks_test.rb +13 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +18 -17
- data/test/connections/native_firebird/connection.rb +19 -17
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +18 -15
- data/test/connections/native_openbase/connection.rb +14 -15
- data/test/connections/native_oracle/connection.rb +16 -12
- data/test/connections/native_postgresql/connection.rb +16 -17
- data/test/connections/native_sqlite/connection.rb +3 -6
- data/test/connections/native_sqlite3/connection.rb +3 -6
- data/test/connections/native_sqlserver/connection.rb +16 -17
- data/test/connections/native_sqlserver_odbc/connection.rb +18 -19
- data/test/connections/native_sybase/connection.rb +16 -17
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/defaults_test.rb +52 -10
- data/test/deprecated_associations_test.rb +151 -107
- data/test/deprecated_finder_test.rb +83 -66
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +118 -11
- data/test/fixtures/accounts.yml +6 -1
- data/test/fixtures/author.rb +27 -4
- data/test/fixtures/categorizations.yml +8 -2
- data/test/fixtures/category.rb +1 -2
- data/test/fixtures/comments.yml +0 -6
- data/test/fixtures/companies.yml +6 -1
- data/test/fixtures/company.rb +23 -1
- data/test/fixtures/company_in_module.rb +8 -10
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/customers.yml +9 -0
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +9 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -0
- data/test/fixtures/db_definitions/firebird.sql +13 -1
- data/test/fixtures/db_definitions/frontbase.drop.sql +31 -0
- data/test/fixtures/db_definitions/frontbase.sql +262 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
- data/test/fixtures/db_definitions/mysql.sql +23 -14
- data/test/fixtures/db_definitions/openbase.sql +13 -1
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +29 -2
- data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
- data/test/fixtures/db_definitions/postgresql.sql +13 -3
- data/test/fixtures/db_definitions/schema.rb +29 -1
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +12 -3
- data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
- data/test/fixtures/db_definitions/sqlserver.sql +35 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
- data/test/fixtures/db_definitions/sybase.sql +13 -4
- data/test/fixtures/developer.rb +12 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/funny_jokes.yml +3 -7
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/mixin.rb +15 -0
- data/test/fixtures/mixins.yml +38 -0
- data/test/fixtures/post.rb +3 -2
- data/test/fixtures/project.rb +3 -1
- data/test/fixtures/topic.rb +6 -1
- data/test/fixtures/topics.yml +4 -4
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +45 -0
- data/test/inheritance_test.rb +67 -6
- data/test/lifecycle_test.rb +40 -19
- data/test/locking_test.rb +170 -26
- data/test/method_scoping_test.rb +2 -2
- data/test/migration_test.rb +387 -110
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +14 -2
- data/test/mixin_test.rb +56 -18
- data/test/modules_test.rb +8 -2
- data/test/multiple_db_test.rb +2 -2
- data/test/pk_test.rb +1 -0
- data/test/reflection_test.rb +8 -2
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +40 -4
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +19 -16
- data/test/transactions_test.rb +86 -72
- data/test/validations_test.rb +126 -56
- data/test/xml_serialization_test.rb +125 -0
- metadata +45 -11
- data/lib/active_record/locking.rb +0 -79
@@ -14,7 +14,11 @@ class AAACreateTablesTest < Test::Unit::TestCase
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def test_load_schema
|
17
|
-
|
17
|
+
if ActiveRecord::Base.connection.supports_migrations?
|
18
|
+
eval(File.read("#{File.dirname(__FILE__)}/fixtures/db_definitions/schema.rb"))
|
19
|
+
else
|
20
|
+
recreate ActiveRecord::Base, '3'
|
21
|
+
end
|
18
22
|
assert true
|
19
23
|
end
|
20
24
|
|
data/test/abstract_unit.rb
CHANGED
@@ -8,6 +8,10 @@ require 'active_support/binding_of_caller'
|
|
8
8
|
require 'active_support/breakpoint'
|
9
9
|
require 'connection'
|
10
10
|
|
11
|
+
# Show backtraces for deprecated behavior for quicker cleanup.
|
12
|
+
ActiveSupport::Deprecation.debug = true
|
13
|
+
|
14
|
+
|
11
15
|
QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type') unless Object.const_defined?(:QUOTED_TYPE)
|
12
16
|
|
13
17
|
class Test::Unit::TestCase #:nodoc:
|
@@ -18,9 +22,9 @@ class Test::Unit::TestCase #:nodoc:
|
|
18
22
|
def create_fixtures(*table_names, &block)
|
19
23
|
Fixtures.create_fixtures(File.dirname(__FILE__) + "/fixtures/", table_names, {}, &block)
|
20
24
|
end
|
21
|
-
|
25
|
+
|
22
26
|
def assert_date_from_db(expected, actual, message = nil)
|
23
|
-
# SQL Server doesn't have a separate column type just for dates,
|
27
|
+
# SQL Server doesn't have a separate column type just for dates,
|
24
28
|
# so the time is in the string and incorrectly formatted
|
25
29
|
if current_adapter?(:SQLServerAdapter)
|
26
30
|
assert_equal expected.strftime("%Y/%m/%d 00:00:00"), actual.strftime("%Y/%m/%d 00:00:00")
|
@@ -49,17 +53,23 @@ class Test::Unit::TestCase #:nodoc:
|
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
52
|
-
def current_adapter?(
|
53
|
-
|
54
|
-
ActiveRecord::
|
56
|
+
def current_adapter?(*types)
|
57
|
+
types.any? do |type|
|
58
|
+
ActiveRecord::ConnectionAdapters.const_defined?(type) &&
|
59
|
+
ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters.const_get(type))
|
60
|
+
end
|
55
61
|
end
|
56
62
|
|
57
63
|
ActiveRecord::Base.connection.class.class_eval do
|
58
64
|
cattr_accessor :query_count
|
65
|
+
|
66
|
+
# Array of regexes of queries that are not counted against query_count
|
67
|
+
@@ignore_list = [/^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/]
|
68
|
+
|
59
69
|
alias_method :execute_without_query_counting, :execute
|
60
|
-
def execute_with_query_counting(sql, name = nil)
|
61
|
-
self.query_count += 1
|
62
|
-
execute_without_query_counting(sql, name)
|
70
|
+
def execute_with_query_counting(sql, name = nil, &block)
|
71
|
+
self.query_count += 1 unless @@ignore_list.any? { |r| sql =~ r }
|
72
|
+
execute_without_query_counting(sql, name, &block)
|
63
73
|
end
|
64
74
|
end
|
65
75
|
|
@@ -17,11 +17,11 @@ class ActiveSchemaTest < Test::Unit::TestCase
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_add_column
|
20
|
-
assert_equal "ALTER TABLE people ADD last_name varchar(255)", add_column(:people, :last_name, :string)
|
20
|
+
assert_equal "ALTER TABLE people ADD `last_name` varchar(255)", add_column(:people, :last_name, :string)
|
21
21
|
end
|
22
22
|
|
23
23
|
def test_add_column_with_limit
|
24
|
-
assert_equal "ALTER TABLE people ADD key varchar(32)", add_column(:people, :key, :string, :limit => 32)
|
24
|
+
assert_equal "ALTER TABLE people ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32)
|
25
25
|
end
|
26
26
|
|
27
27
|
private
|
data/test/adapter_test.rb
CHANGED
@@ -66,19 +66,21 @@ class AdapterTest < Test::Unit::TestCase
|
|
66
66
|
if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
|
67
67
|
require 'fixtures/movie'
|
68
68
|
require 'fixtures/subscriber'
|
69
|
+
|
69
70
|
def test_reset_empty_table_with_custom_pk
|
70
71
|
Movie.delete_all
|
71
72
|
Movie.connection.reset_pk_sequence! 'movies'
|
72
73
|
assert_equal 1, Movie.create(:name => 'fight club').id
|
73
74
|
end
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
76
|
+
if ActiveRecord::Base.connection.adapter_name != "FrontBase"
|
77
|
+
def test_reset_table_with_non_integer_pk
|
78
|
+
Subscriber.delete_all
|
79
|
+
Subscriber.connection.reset_pk_sequence! 'subscribers'
|
80
|
+
sub = Subscriber.new(:name => 'robert drake')
|
81
|
+
sub.id = 'bob drake'
|
82
|
+
assert_nothing_raised { sub.save! }
|
83
|
+
end
|
82
84
|
end
|
83
85
|
end
|
84
86
|
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
require 'fixtures/default'
|
3
|
+
require 'fixtures/post'
|
4
|
+
require 'fixtures/task'
|
5
|
+
|
6
|
+
class SqlServerAdapterTest < Test::Unit::TestCase
|
7
|
+
fixtures :posts, :tasks
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@connection = ActiveRecord::Base.connection
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
@connection.execute("SET LANGUAGE us_english")
|
15
|
+
end
|
16
|
+
|
17
|
+
# SQL Server 2000 has a bug where some unambiguous date formats are not
|
18
|
+
# correctly identified if the session language is set to german
|
19
|
+
def test_date_insertion_when_language_is_german
|
20
|
+
@connection.execute("SET LANGUAGE deutsch")
|
21
|
+
|
22
|
+
assert_nothing_raised do
|
23
|
+
Task.create(:starting => Time.utc(2000, 1, 31, 5, 42, 0), :ending => Date.new(2006, 12, 31))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_execute_without_block_closes_statement
|
28
|
+
assert_all_statements_used_are_closed do
|
29
|
+
@connection.execute("SELECT 1")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_execute_with_block_closes_statement
|
34
|
+
assert_all_statements_used_are_closed do
|
35
|
+
@connection.execute("SELECT 1") do |sth|
|
36
|
+
assert !sth.finished?, "Statement should still be alive within block"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_insert_with_identity_closes_statement
|
42
|
+
assert_all_statements_used_are_closed do
|
43
|
+
@connection.insert("INSERT INTO accounts ([id], [firm_id],[credit_limit]) values (999, 1, 50)")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_insert_without_identity_closes_statement
|
48
|
+
assert_all_statements_used_are_closed do
|
49
|
+
@connection.insert("INSERT INTO accounts ([firm_id],[credit_limit]) values (1, 50)")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_active_closes_statement
|
54
|
+
assert_all_statements_used_are_closed do
|
55
|
+
@connection.active?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def assert_all_statements_used_are_closed(&block)
|
60
|
+
existing_handles = []
|
61
|
+
ObjectSpace.each_object(DBI::StatementHandle) {|handle| existing_handles << handle}
|
62
|
+
GC.disable
|
63
|
+
|
64
|
+
yield
|
65
|
+
|
66
|
+
used_handles = []
|
67
|
+
ObjectSpace.each_object(DBI::StatementHandle) {|handle| used_handles << handle unless existing_handles.include? handle}
|
68
|
+
|
69
|
+
assert_block "No statements were used within given block" do
|
70
|
+
used_handles.size > 0
|
71
|
+
end
|
72
|
+
|
73
|
+
ObjectSpace.each_object(DBI::StatementHandle) do |handle|
|
74
|
+
assert_block "Statement should have been closed within given block" do
|
75
|
+
handle.finished?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
ensure
|
79
|
+
GC.enable
|
80
|
+
end
|
81
|
+
end
|
data/test/aggregations_test.rb
CHANGED
@@ -63,4 +63,33 @@ class AggregationsTest < Test::Unit::TestCase
|
|
63
63
|
def test_gps_inequality
|
64
64
|
assert GpsLocation.new('39x110') != GpsLocation.new('39x111')
|
65
65
|
end
|
66
|
+
|
67
|
+
def test_allow_nil_gps_is_nil
|
68
|
+
assert_equal nil, customers(:zaphod).gps_location
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_allow_nil_gps_set_to_nil
|
72
|
+
customers(:david).gps_location = nil
|
73
|
+
customers(:david).save
|
74
|
+
customers(:david).reload
|
75
|
+
assert_equal nil, customers(:david).gps_location
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_allow_nil_set_address_attributes_to_nil
|
79
|
+
customers(:zaphod).address = nil
|
80
|
+
assert_equal nil, customers(:zaphod).attributes[:address_street]
|
81
|
+
assert_equal nil, customers(:zaphod).attributes[:address_city]
|
82
|
+
assert_equal nil, customers(:zaphod).attributes[:address_country]
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_allow_nil_address_set_to_nil
|
86
|
+
customers(:zaphod).address = nil
|
87
|
+
customers(:zaphod).save
|
88
|
+
customers(:zaphod).reload
|
89
|
+
assert_equal nil, customers(:zaphod).address
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_nil_raises_error_when_allow_nil_is_false
|
93
|
+
assert_raises(NoMethodError) { customers(:david).balance = nil }
|
94
|
+
end
|
66
95
|
end
|
@@ -111,14 +111,16 @@ class AssociationCallbacksTest < Test::Unit::TestCase
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def test_push_with_attributes
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
114
|
+
assert_deprecated 'push_with_attributes' do
|
115
|
+
david = developers(:david)
|
116
|
+
activerecord = projects(:active_record)
|
117
|
+
assert activerecord.developers_log.empty?
|
118
|
+
activerecord.developers_with_callbacks.push_with_attributes(david, {})
|
119
|
+
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], activerecord.developers_log
|
120
|
+
activerecord.developers_with_callbacks.push_with_attributes(david, {})
|
121
|
+
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
|
122
|
+
"after_adding#{david.id}"], activerecord.developers_log
|
123
|
+
end
|
122
124
|
end
|
123
125
|
end
|
124
126
|
|
@@ -11,7 +11,7 @@ require 'fixtures/topic'
|
|
11
11
|
require 'fixtures/reply'
|
12
12
|
|
13
13
|
class CascadedEagerLoadingTest < Test::Unit::TestCase
|
14
|
-
fixtures :authors, :mixins, :companies, :posts, :
|
14
|
+
fixtures :authors, :mixins, :companies, :posts, :topics
|
15
15
|
|
16
16
|
def test_eager_association_loading_with_cascaded_two_levels
|
17
17
|
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
|
@@ -28,7 +28,7 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
|
|
28
28
|
assert_equal 1, authors[1].posts.size
|
29
29
|
assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
|
30
30
|
assert_equal 1, authors[0].categorizations.size
|
31
|
-
assert_equal
|
31
|
+
assert_equal 2, authors[1].categorizations.size
|
32
32
|
end
|
33
33
|
|
34
34
|
def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
|
@@ -94,7 +94,7 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
|
|
94
94
|
author.posts.first.very_special_comment
|
95
95
|
end
|
96
96
|
end
|
97
|
-
|
97
|
+
|
98
98
|
def test_eager_association_loading_of_stis_with_multiple_references
|
99
99
|
authors = Author.find(:all, :include => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
|
100
100
|
assert_equal [authors(:david)], authors
|
@@ -103,4 +103,36 @@ class CascadedEagerLoadingTest < Test::Unit::TestCase
|
|
103
103
|
authors.first.posts.first.special_comments.first.post.very_special_comment
|
104
104
|
end
|
105
105
|
end
|
106
|
+
|
107
|
+
def test_eager_association_loading_with_recursive_cascading_three_levels_has_many
|
108
|
+
root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:children=>{:children=>:children}}, :order => 'mixins.id')
|
109
|
+
assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.children.first.children.first.children.first }
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_eager_association_loading_with_recursive_cascading_three_levels_has_one
|
113
|
+
root_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:first_child=>{:first_child=>:first_child}}, :order => 'mixins.id')
|
114
|
+
assert_equal mixins(:recursively_cascaded_tree_4), assert_no_queries { root_node.first_child.first_child.first_child }
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_eager_association_loading_with_recursive_cascading_three_levels_belongs_to
|
118
|
+
leaf_node = RecursivelyCascadedTreeMixin.find(:first, :include=>{:parent=>{:parent=>:parent}}, :order => 'mixins.id DESC')
|
119
|
+
assert_equal mixins(:recursively_cascaded_tree_1), assert_no_queries { leaf_node.parent.parent.parent }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
require 'fixtures/vertex'
|
125
|
+
require 'fixtures/edge'
|
126
|
+
class CascadedEagerLoadingTest < Test::Unit::TestCase
|
127
|
+
fixtures :edges, :vertices
|
128
|
+
|
129
|
+
def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through
|
130
|
+
source = Vertex.find(:first, :include=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id')
|
131
|
+
assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many
|
135
|
+
sink = Vertex.find(:first, :include=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC')
|
136
|
+
assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first }
|
137
|
+
end
|
106
138
|
end
|
@@ -104,7 +104,7 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def test_eager_with_has_many_through
|
107
|
-
posts_with_comments = people(:michael).posts.find(:all, :include => :comments
|
107
|
+
posts_with_comments = people(:michael).posts.find(:all, :include => :comments)
|
108
108
|
posts_with_author = people(:michael).posts.find(:all, :include => :author )
|
109
109
|
posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ])
|
110
110
|
assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size }
|
@@ -112,6 +112,22 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
112
112
|
assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
|
113
113
|
end
|
114
114
|
|
115
|
+
def test_eager_with_has_many_through_an_sti_join_model
|
116
|
+
author = Author.find(:first, :include => :special_post_comments, :order => 'authors.id')
|
117
|
+
assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both
|
121
|
+
author = Author.find(:first, :include => :special_nonexistant_post_comments, :order => 'authors.id')
|
122
|
+
assert_equal [], author.special_nonexistant_post_comments
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_eager_with_has_many_through_join_model_with_conditions
|
126
|
+
assert_equal Author.find(:first, :include => :hello_post_comments,
|
127
|
+
:order => 'authors.id').hello_post_comments.sort_by(&:id),
|
128
|
+
Author.find(:first, :order => 'authors.id').hello_post_comments.sort_by(&:id)
|
129
|
+
end
|
130
|
+
|
115
131
|
def test_eager_with_has_many_and_limit
|
116
132
|
posts = Post.find(:all, :order => 'posts.id asc', :include => [ :author, :comments ], :limit => 2)
|
117
133
|
assert_equal 2, posts.size
|
@@ -274,6 +290,16 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
274
290
|
def find_all_ordered(className, include=nil)
|
275
291
|
className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :include=>include)
|
276
292
|
end
|
293
|
+
|
294
|
+
def test_limited_eager_with_order
|
295
|
+
assert_equal [posts(:thinking), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
|
296
|
+
assert_equal [posts(:sti_post_and_comments), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_limited_eager_with_multiple_order_columns
|
300
|
+
assert_equal [posts(:thinking), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1)
|
301
|
+
assert_equal [posts(:sti_post_and_comments), posts(:sti_comments)], Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
|
302
|
+
end
|
277
303
|
|
278
304
|
def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
|
279
305
|
# Eager includes of has many and habtm associations aren't necessarily sorted in the same way
|
@@ -283,7 +309,7 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
283
309
|
end
|
284
310
|
# Test regular association, association with conditions, association with
|
285
311
|
# STI, and association with conditions assured not to be true
|
286
|
-
post_types = [:posts, :
|
312
|
+
post_types = [:posts, :other_posts, :special_posts]
|
287
313
|
# test both has_many and has_and_belongs_to_many
|
288
314
|
[Author, Category].each do |className|
|
289
315
|
d1 = find_all_ordered(className)
|
@@ -356,4 +382,12 @@ class EagerAssociationTest < Test::Unit::TestCase
|
|
356
382
|
assert_equal 2, one.comments.size
|
357
383
|
assert_equal 2, one.categories.size
|
358
384
|
end
|
385
|
+
|
386
|
+
def test_count_with_include
|
387
|
+
if current_adapter?(:SQLServerAdapter, :SybaseAdapter)
|
388
|
+
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
|
389
|
+
else
|
390
|
+
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
|
391
|
+
end
|
392
|
+
end
|
359
393
|
end
|
@@ -18,6 +18,11 @@ class AssociationsExtensionsTest < Test::Unit::TestCase
|
|
18
18
|
def test_named_extension_on_habtm
|
19
19
|
assert_equal projects(:action_controller), developers(:david).projects_extended_by_name.find_most_recent
|
20
20
|
end
|
21
|
+
|
22
|
+
def test_named_two_extensions_on_habtm
|
23
|
+
assert_equal projects(:action_controller), developers(:david).projects_extended_by_name_twice.find_most_recent
|
24
|
+
assert_equal projects(:active_record), developers(:david).projects_extended_by_name_twice.find_least_recent
|
25
|
+
end
|
21
26
|
|
22
27
|
def test_marshalling_extensions
|
23
28
|
david = developers(:david)
|
@@ -12,21 +12,26 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
|
|
12
12
|
fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites
|
13
13
|
|
14
14
|
def test_has_many
|
15
|
-
|
15
|
+
assert authors(:david).categories.include?(categories(:general))
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def test_has_many_inherited
|
19
|
-
|
19
|
+
assert authors(:mary).categories.include?(categories(:sti_test))
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_inherited_has_many
|
23
|
-
|
23
|
+
assert categories(:sti_test).authors.include?(authors(:mary))
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
|
+
def test_has_many_uniq_through_join_model
|
27
|
+
assert_equal 2, authors(:mary).categorized_posts.size
|
28
|
+
assert_equal 1, authors(:mary).unique_categorized_posts.size
|
29
|
+
end
|
30
|
+
|
26
31
|
def test_polymorphic_has_many
|
27
|
-
|
32
|
+
assert posts(:welcome).taggings.include?(taggings(:welcome_general))
|
28
33
|
end
|
29
|
-
|
34
|
+
|
30
35
|
def test_polymorphic_has_one
|
31
36
|
assert_equal taggings(:welcome_general), posts(:welcome).tagging
|
32
37
|
end
|
@@ -132,6 +137,13 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
|
|
132
137
|
assert_equal "Post", tagging.taggable_type
|
133
138
|
assert_equal old_count+1, posts(:welcome).taggings.count
|
134
139
|
end
|
140
|
+
|
141
|
+
def test_create_bang_polymorphic_with_has_many_scope
|
142
|
+
old_count = posts(:welcome).taggings.count
|
143
|
+
tagging = posts(:welcome).taggings.create!(:tag => tags(:misc))
|
144
|
+
assert_equal "Post", tagging.taggable_type
|
145
|
+
assert_equal old_count+1, posts(:welcome).taggings.count
|
146
|
+
end
|
135
147
|
|
136
148
|
def test_create_polymorphic_has_one_with_scope
|
137
149
|
old_count = Tagging.count
|
@@ -241,6 +253,10 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
|
|
241
253
|
def test_has_many_find_first
|
242
254
|
assert_equal categories(:general), authors(:david).categories.find(:first)
|
243
255
|
end
|
256
|
+
|
257
|
+
def test_has_many_with_hash_conditions
|
258
|
+
assert_equal categories(:general), authors(:david).categories_like_general.find(:first)
|
259
|
+
end
|
244
260
|
|
245
261
|
def test_has_many_find_conditions
|
246
262
|
assert_equal categories(:general), authors(:david).categories.find(:first, :conditions => "categories.name = 'General'")
|
@@ -296,7 +312,8 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
|
|
296
312
|
end
|
297
313
|
|
298
314
|
def test_has_many_through_has_many_find_conditions
|
299
|
-
|
315
|
+
options = { :conditions => "comments.#{QUOTED_TYPE}='SpecialComment'", :order => 'comments.id' }
|
316
|
+
assert_equal comments(:does_it_hurt), authors(:david).comments.find(:first, options)
|
300
317
|
end
|
301
318
|
|
302
319
|
def test_has_many_through_has_many_find_by_id
|
@@ -334,6 +351,13 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
|
|
334
351
|
assert_equal [1,2,3,5,6,7,8,9,10], author.comments.collect(&:id)
|
335
352
|
end
|
336
353
|
end
|
354
|
+
|
355
|
+
def test_eager_load_has_many_through_has_many_with_conditions
|
356
|
+
post = Post.find(:first, :include => :invalid_tags)
|
357
|
+
assert_no_queries do
|
358
|
+
post.invalid_tags
|
359
|
+
end
|
360
|
+
end
|
337
361
|
|
338
362
|
def test_eager_belongs_to_and_has_one_not_singularized
|
339
363
|
assert_nothing_raised do
|
@@ -357,6 +381,92 @@ class AssociationsJoinModelTest < Test::Unit::TestCase
|
|
357
381
|
assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"]
|
358
382
|
end
|
359
383
|
|
384
|
+
def test_raise_error_when_adding_new_record_to_has_many_through
|
385
|
+
assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags << tags(:general).clone }
|
386
|
+
assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).clone.tags << tags(:general) }
|
387
|
+
assert_raise(ActiveRecord::HasManyThroughCantAssociateNewRecords) { posts(:thinking).tags.build }
|
388
|
+
end
|
389
|
+
|
390
|
+
def test_create_associate_when_adding_to_has_many_through
|
391
|
+
count = posts(:thinking).tags.count
|
392
|
+
push = Tag.create!(:name => 'pushme')
|
393
|
+
post_thinking = posts(:thinking)
|
394
|
+
assert_nothing_raised { post_thinking.tags << push }
|
395
|
+
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
|
396
|
+
message = "Expected a Tag in tags collection, got #{wrong.class}.")
|
397
|
+
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
|
398
|
+
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
|
399
|
+
assert_equal(count + 1, post_thinking.tags.size)
|
400
|
+
assert_equal(count + 1, post_thinking.tags(true).size)
|
401
|
+
|
402
|
+
assert_nothing_raised { post_thinking.tags.create!(:name => 'foo') }
|
403
|
+
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
|
404
|
+
message = "Expected a Tag in tags collection, got #{wrong.class}.")
|
405
|
+
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
|
406
|
+
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
|
407
|
+
assert_equal(count + 2, post_thinking.tags.size)
|
408
|
+
assert_equal(count + 2, post_thinking.tags(true).size)
|
409
|
+
|
410
|
+
assert_nothing_raised { post_thinking.tags.concat(Tag.create!(:name => 'abc'), Tag.create!(:name => 'def')) }
|
411
|
+
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
|
412
|
+
message = "Expected a Tag in tags collection, got #{wrong.class}.")
|
413
|
+
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
|
414
|
+
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
|
415
|
+
assert_equal(count + 4, post_thinking.tags.size)
|
416
|
+
assert_equal(count + 4, post_thinking.tags(true).size)
|
417
|
+
end
|
418
|
+
|
419
|
+
def test_adding_junk_to_has_many_through_should_raise_type_mismatch
|
420
|
+
assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags << "Uhh what now?" }
|
421
|
+
end
|
422
|
+
|
423
|
+
def test_adding_to_has_many_through_should_return_self
|
424
|
+
tags = posts(:thinking).tags
|
425
|
+
assert_equal tags, posts(:thinking).tags.push(tags(:general))
|
426
|
+
end
|
427
|
+
|
428
|
+
def test_delete_associate_when_deleting_from_has_many_through
|
429
|
+
count = posts(:thinking).tags.count
|
430
|
+
tags_before = posts(:thinking).tags
|
431
|
+
tag = Tag.create!(:name => 'doomed')
|
432
|
+
post_thinking = posts(:thinking)
|
433
|
+
post_thinking.tags << tag
|
434
|
+
assert_equal(count + 1, post_thinking.tags(true).size)
|
435
|
+
|
436
|
+
assert_nothing_raised { post_thinking.tags.delete(tag) }
|
437
|
+
assert_equal(count, post_thinking.tags.size)
|
438
|
+
assert_equal(count, post_thinking.tags(true).size)
|
439
|
+
assert_equal(tags_before.sort, post_thinking.tags.sort)
|
440
|
+
end
|
441
|
+
|
442
|
+
def test_delete_associate_when_deleting_from_has_many_through_with_multiple_tags
|
443
|
+
count = posts(:thinking).tags.count
|
444
|
+
tags_before = posts(:thinking).tags
|
445
|
+
doomed = Tag.create!(:name => 'doomed')
|
446
|
+
doomed2 = Tag.create!(:name => 'doomed2')
|
447
|
+
quaked = Tag.create!(:name => 'quaked')
|
448
|
+
post_thinking = posts(:thinking)
|
449
|
+
post_thinking.tags << doomed << doomed2
|
450
|
+
assert_equal(count + 2, post_thinking.tags(true).size)
|
451
|
+
|
452
|
+
assert_nothing_raised { post_thinking.tags.delete(doomed, doomed2, quaked) }
|
453
|
+
assert_equal(count, post_thinking.tags.size)
|
454
|
+
assert_equal(count, post_thinking.tags(true).size)
|
455
|
+
assert_equal(tags_before.sort, post_thinking.tags.sort)
|
456
|
+
end
|
457
|
+
|
458
|
+
def test_deleting_junk_from_has_many_through_should_raise_type_mismatch
|
459
|
+
assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags.delete("Uhh what now?") }
|
460
|
+
end
|
461
|
+
|
462
|
+
def test_has_many_through_sum_uses_calculations
|
463
|
+
assert_nothing_raised { authors(:david).comments.sum(:post_id) }
|
464
|
+
end
|
465
|
+
|
466
|
+
def test_has_many_through_has_many_with_sti
|
467
|
+
assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments
|
468
|
+
end
|
469
|
+
|
360
470
|
private
|
361
471
|
# create dynamic Post models to allow different dependency options
|
362
472
|
def find_post_with_dependency(post_id, association, association_name, dependency)
|