odbc-rails 1.3 → 1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +18 -0
- data/NEWS +5 -0
- data/README +20 -7
- data/lib/active_record/connection_adapters/odbc_adapter.rb +254 -211
- data/lib/active_record/vendor/odbcext_informix.rb +17 -4
- data/lib/active_record/vendor/odbcext_ingres.rb +5 -5
- data/lib/active_record/vendor/odbcext_microsoftsqlserver.rb +35 -4
- data/lib/active_record/vendor/odbcext_mysql.rb +36 -8
- data/lib/active_record/vendor/odbcext_oracle.rb +4 -4
- data/lib/active_record/vendor/odbcext_postgresql.rb +8 -12
- data/lib/active_record/vendor/odbcext_progress.rb +3 -3
- data/lib/active_record/vendor/odbcext_progress89.rb +5 -4
- data/lib/active_record/vendor/odbcext_sybase.rb +6 -5
- data/lib/active_record/vendor/odbcext_virtuoso.rb +16 -3
- data/support/odbc_rails.diff +335 -266
- data/support/rake_fixes/README +6 -0
- data/support/rake_fixes/databases.dif +13 -0
- data/support/test/base_test.rb +333 -75
- data/support/test/migration_test.rb +430 -149
- data/test/connections/native_odbc/connection.rb +63 -44
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +9 -0
- data/test/fixtures/db_definitions/informix.drop.sql +1 -0
- data/test/fixtures/db_definitions/informix.sql +10 -0
- data/test/fixtures/db_definitions/ingres.drop.sql +2 -0
- data/test/fixtures/db_definitions/ingres.sql +9 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
- data/test/fixtures/db_definitions/mysql.sql +21 -12
- data/test/fixtures/db_definitions/oracle_odbc.drop.sql +4 -0
- data/test/fixtures/db_definitions/oracle_odbc.sql +29 -1
- data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
- data/test/fixtures/db_definitions/postgresql.sql +13 -3
- data/test/fixtures/db_definitions/progress.drop.sql +2 -0
- data/test/fixtures/db_definitions/progress.sql +11 -0
- 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 +16 -7
- data/test/fixtures/db_definitions/virtuoso.drop.sql +1 -0
- data/test/fixtures/db_definitions/virtuoso.sql +10 -0
- metadata +6 -3
@@ -0,0 +1,13 @@
|
|
1
|
+
--- databases.rake.orig 2007-01-22 12:09:02.230758200 +0000
|
2
|
+
+++ databases.rake 2007-02-01 17:08:12.350537700 +0000
|
3
|
+
@@ -142,6 +142,10 @@
|
4
|
+
when "firebird"
|
5
|
+
ActiveRecord::Base.establish_connection(:test)
|
6
|
+
ActiveRecord::Base.connection.recreate_database!
|
7
|
+
+ when "odbc"
|
8
|
+
+ ActiveRecord::Base.establish_connection(:test)
|
9
|
+
+ test_db = ActiveRecord::Base.connection.current_database
|
10
|
+
+ ActiveRecord::Base.connection.recreate_database(test_db, false) unless test_db.empty?
|
11
|
+
else
|
12
|
+
raise "Task not supported by '#{abcs["test"]["adapter"]}'"
|
13
|
+
end
|
data/support/test/base_test.rb
CHANGED
@@ -10,10 +10,18 @@ require 'fixtures/auto_id'
|
|
10
10
|
require 'fixtures/column_name'
|
11
11
|
require 'fixtures/subscriber'
|
12
12
|
require 'fixtures/keyboard'
|
13
|
+
require 'fixtures/post'
|
13
14
|
|
14
15
|
class Category < ActiveRecord::Base; end
|
15
16
|
class Smarts < ActiveRecord::Base; end
|
16
|
-
class CreditCard < ActiveRecord::Base
|
17
|
+
class CreditCard < ActiveRecord::Base
|
18
|
+
class PinNumber < ActiveRecord::Base
|
19
|
+
class CvvCode < ActiveRecord::Base; end
|
20
|
+
class SubCvvCode < CvvCode; end
|
21
|
+
end
|
22
|
+
class SubPinNumber < PinNumber; end
|
23
|
+
class Brand < Category; end
|
24
|
+
end
|
17
25
|
class MasterCreditCard < ActiveRecord::Base; end
|
18
26
|
class Post < ActiveRecord::Base; end
|
19
27
|
class Computer < ActiveRecord::Base; end
|
@@ -21,8 +29,9 @@ class NonExistentTable < ActiveRecord::Base; end
|
|
21
29
|
class TestOracleDefault < ActiveRecord::Base; end
|
22
30
|
|
23
31
|
class LoosePerson < ActiveRecord::Base
|
24
|
-
|
32
|
+
self.table_name = 'people'
|
25
33
|
self.abstract_class = true
|
34
|
+
attr_protected :credit_rating, :administrator
|
26
35
|
end
|
27
36
|
|
28
37
|
class LooseDescendant < LoosePerson
|
@@ -30,6 +39,7 @@ class LooseDescendant < LoosePerson
|
|
30
39
|
end
|
31
40
|
|
32
41
|
class TightPerson < ActiveRecord::Base
|
42
|
+
self.table_name = 'people'
|
33
43
|
attr_accessible :name, :address
|
34
44
|
end
|
35
45
|
|
@@ -59,7 +69,7 @@ class BasicsTest < Test::Unit::TestCase
|
|
59
69
|
assert_equal("Jason", topic.author_name)
|
60
70
|
assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
|
61
71
|
end
|
62
|
-
|
72
|
+
|
63
73
|
def test_integers_as_nil
|
64
74
|
test = AutoId.create('value' => '')
|
65
75
|
assert_nil AutoId.find(test.id).value
|
@@ -142,8 +152,20 @@ class BasicsTest < Test::Unit::TestCase
|
|
142
152
|
def test_save!
|
143
153
|
topic = Topic.new(:title => "New Topic")
|
144
154
|
assert topic.save!
|
145
|
-
end
|
146
155
|
|
156
|
+
reply = Reply.new
|
157
|
+
assert_raise(ActiveRecord::RecordInvalid) { reply.save! }
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_save_null_string_attributes
|
161
|
+
topic = Topic.find(1)
|
162
|
+
topic.attributes = { "title" => "null", "author_name" => "null" }
|
163
|
+
topic.save!
|
164
|
+
topic.reload
|
165
|
+
assert_equal("null", topic.title)
|
166
|
+
assert_equal("null", topic.author_name)
|
167
|
+
end
|
168
|
+
|
147
169
|
def test_hashes_not_mangled
|
148
170
|
new_topic = { :title => "New Topic" }
|
149
171
|
new_topic_values = { :title => "AnotherTopic" }
|
@@ -304,8 +326,6 @@ class BasicsTest < Test::Unit::TestCase
|
|
304
326
|
"The last_read attribute should be of the Time class"
|
305
327
|
)
|
306
328
|
else
|
307
|
-
# ODBCAdapter fails against SQL Server because topics.last_read is
|
308
|
-
# defined as a datetime column, which is returned as a Ruby Time object.
|
309
329
|
assert_kind_of(
|
310
330
|
Date, Topic.find(1).last_read,
|
311
331
|
"The last_read attribute should be of the Date class"
|
@@ -323,23 +343,22 @@ class BasicsTest < Test::Unit::TestCase
|
|
323
343
|
Time, Topic.find(1).written_on,
|
324
344
|
"The written_on attribute should be of the Time class"
|
325
345
|
)
|
346
|
+
|
347
|
+
# For adapters which support microsecond resolution.
|
348
|
+
if current_adapter?(:PostgreSQLAdapter)
|
349
|
+
assert_equal 11, Topic.find(1).written_on.sec
|
350
|
+
assert_equal 223300, Topic.find(1).written_on.usec
|
351
|
+
assert_equal 9900, Topic.find(2).written_on.usec
|
352
|
+
end
|
326
353
|
end
|
327
|
-
|
354
|
+
|
328
355
|
def test_destroy
|
329
|
-
topic = Topic.
|
330
|
-
topic.
|
331
|
-
topic.
|
332
|
-
topic.save
|
333
|
-
topic.destroy
|
356
|
+
topic = Topic.find(1)
|
357
|
+
assert_equal topic, topic.destroy, 'topic.destroy did not return self'
|
358
|
+
assert topic.frozen?, 'topic not frozen after destroy'
|
334
359
|
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
|
335
360
|
end
|
336
|
-
|
337
|
-
def test_destroy_returns_self
|
338
|
-
topic = Topic.new("title" => "Yet Another Title")
|
339
|
-
assert topic.save
|
340
|
-
assert_equal topic, topic.destroy, "destroy did not return destroyed object"
|
341
|
-
end
|
342
|
-
|
361
|
+
|
343
362
|
def test_record_not_found_exception
|
344
363
|
assert_raises(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
|
345
364
|
end
|
@@ -376,21 +395,33 @@ class BasicsTest < Test::Unit::TestCase
|
|
376
395
|
end
|
377
396
|
|
378
397
|
def test_table_name_guesses
|
398
|
+
classes = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
|
399
|
+
|
379
400
|
assert_equal "topics", Topic.table_name
|
380
|
-
|
401
|
+
|
381
402
|
assert_equal "categories", Category.table_name
|
382
403
|
assert_equal "smarts", Smarts.table_name
|
383
404
|
assert_equal "credit_cards", CreditCard.table_name
|
405
|
+
assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
|
406
|
+
assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
|
407
|
+
assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
|
408
|
+
assert_equal "categories", CreditCard::Brand.table_name
|
384
409
|
assert_equal "master_credit_cards", MasterCreditCard.table_name
|
385
410
|
|
386
411
|
ActiveRecord::Base.pluralize_table_names = false
|
387
|
-
|
412
|
+
classes.each(&:reset_table_name)
|
413
|
+
|
388
414
|
assert_equal "category", Category.table_name
|
389
415
|
assert_equal "smarts", Smarts.table_name
|
390
416
|
assert_equal "credit_card", CreditCard.table_name
|
417
|
+
assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
|
418
|
+
assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
|
419
|
+
assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
|
420
|
+
assert_equal "category", CreditCard::Brand.table_name
|
391
421
|
assert_equal "master_credit_card", MasterCreditCard.table_name
|
422
|
+
|
392
423
|
ActiveRecord::Base.pluralize_table_names = true
|
393
|
-
|
424
|
+
classes.each(&:reset_table_name)
|
394
425
|
|
395
426
|
ActiveRecord::Base.table_name_prefix = "test_"
|
396
427
|
Category.reset_table_name
|
@@ -418,8 +449,9 @@ class BasicsTest < Test::Unit::TestCase
|
|
418
449
|
ActiveRecord::Base.table_name_suffix = ""
|
419
450
|
Category.reset_table_name
|
420
451
|
assert_equal "category", Category.table_name
|
452
|
+
|
421
453
|
ActiveRecord::Base.pluralize_table_names = true
|
422
|
-
|
454
|
+
classes.each(&:reset_table_name)
|
423
455
|
end
|
424
456
|
|
425
457
|
def test_destroy_all
|
@@ -447,18 +479,18 @@ class BasicsTest < Test::Unit::TestCase
|
|
447
479
|
|
448
480
|
def test_increment_counter
|
449
481
|
Topic.increment_counter("replies_count", 1)
|
450
|
-
assert_equal
|
482
|
+
assert_equal 2, Topic.find(1).replies_count
|
451
483
|
|
452
484
|
Topic.increment_counter("replies_count", 1)
|
453
|
-
assert_equal
|
485
|
+
assert_equal 3, Topic.find(1).replies_count
|
454
486
|
end
|
455
487
|
|
456
488
|
def test_decrement_counter
|
457
489
|
Topic.decrement_counter("replies_count", 2)
|
458
|
-
assert_equal 1, Topic.find(2).replies_count
|
490
|
+
assert_equal -1, Topic.find(2).replies_count
|
459
491
|
|
460
492
|
Topic.decrement_counter("replies_count", 2)
|
461
|
-
assert_equal
|
493
|
+
assert_equal -2, Topic.find(2).replies_count
|
462
494
|
end
|
463
495
|
|
464
496
|
def test_update_all
|
@@ -557,16 +589,29 @@ class BasicsTest < Test::Unit::TestCase
|
|
557
589
|
end
|
558
590
|
end
|
559
591
|
|
560
|
-
|
561
|
-
|
562
|
-
|
592
|
+
# Oracle, SQLServer, and Sybase do not have a TIME datatype.
|
593
|
+
unless current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
|
594
|
+
def test_utc_as_time_zone
|
595
|
+
Topic.default_timezone = :utc
|
596
|
+
attributes = { "bonus_time" => "5:42:00AM" }
|
597
|
+
topic = Topic.find(1)
|
598
|
+
topic.attributes = attributes
|
599
|
+
assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
|
600
|
+
Topic.default_timezone = :local
|
601
|
+
end
|
563
602
|
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
603
|
+
def test_utc_as_time_zone_and_new
|
604
|
+
Topic.default_timezone = :utc
|
605
|
+
attributes = { "bonus_time(1i)"=>"2000",
|
606
|
+
"bonus_time(2i)"=>"1",
|
607
|
+
"bonus_time(3i)"=>"1",
|
608
|
+
"bonus_time(4i)"=>"10",
|
609
|
+
"bonus_time(5i)"=>"35",
|
610
|
+
"bonus_time(6i)"=>"50" }
|
611
|
+
topic = Topic.new(attributes)
|
612
|
+
assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
|
613
|
+
Topic.default_timezone = :local
|
614
|
+
end
|
570
615
|
end
|
571
616
|
|
572
617
|
def test_default_values_on_empty_strings
|
@@ -628,6 +673,40 @@ class BasicsTest < Test::Unit::TestCase
|
|
628
673
|
assert !Topic.find(1).approved?
|
629
674
|
end
|
630
675
|
|
676
|
+
def test_update_attributes
|
677
|
+
topic = Topic.find(1)
|
678
|
+
assert !topic.approved?
|
679
|
+
assert_equal "The First Topic", topic.title
|
680
|
+
|
681
|
+
topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
|
682
|
+
topic.reload
|
683
|
+
assert topic.approved?
|
684
|
+
assert_equal "The First Topic Updated", topic.title
|
685
|
+
|
686
|
+
topic.update_attributes(:approved => false, :title => "The First Topic")
|
687
|
+
topic.reload
|
688
|
+
assert !topic.approved?
|
689
|
+
assert_equal "The First Topic", topic.title
|
690
|
+
end
|
691
|
+
|
692
|
+
def test_update_attributes!
|
693
|
+
reply = Reply.find(2)
|
694
|
+
assert_equal "The Second Topic's of the day", reply.title
|
695
|
+
assert_equal "Have a nice day", reply.content
|
696
|
+
|
697
|
+
reply.update_attributes!("title" => "The Second Topic's of the day updated", "content" => "Have a nice evening")
|
698
|
+
reply.reload
|
699
|
+
assert_equal "The Second Topic's of the day updated", reply.title
|
700
|
+
assert_equal "Have a nice evening", reply.content
|
701
|
+
|
702
|
+
reply.update_attributes!(:title => "The Second Topic's of the day", :content => "Have a nice day")
|
703
|
+
reply.reload
|
704
|
+
assert_equal "The Second Topic's of the day", reply.title
|
705
|
+
assert_equal "Have a nice day", reply.content
|
706
|
+
|
707
|
+
assert_raise(ActiveRecord::RecordInvalid) { reply.update_attributes!(:title => nil, :content => "Have a nice evening") }
|
708
|
+
end
|
709
|
+
|
631
710
|
def test_mass_assignment_protection
|
632
711
|
firm = Firm.new
|
633
712
|
firm.attributes = { "name" => "Next Angle", "rating" => 5 }
|
@@ -746,12 +825,11 @@ class BasicsTest < Test::Unit::TestCase
|
|
746
825
|
end
|
747
826
|
|
748
827
|
def test_attributes_on_dummy_time
|
749
|
-
# Oracle
|
750
|
-
return true if current_adapter?(:SQLServerAdapter
|
751
|
-
current_adapter?(:OracleAdapter)
|
828
|
+
# Oracle, SQL Server, and Sybase do not have a TIME datatype.
|
829
|
+
return true if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
|
752
830
|
if current_adapter?(:ODBCAdapter)
|
753
|
-
|
754
|
-
|
831
|
+
# Check for databases which don't have a true TIME datatype
|
832
|
+
return true if [:ingres, :oracle].include?(ActiveRecord::Base.connection.dbmsName)
|
755
833
|
end
|
756
834
|
|
757
835
|
attributes = {
|
@@ -922,12 +1000,45 @@ class BasicsTest < Test::Unit::TestCase
|
|
922
1000
|
end
|
923
1001
|
end
|
924
1002
|
|
1003
|
+
class NumericData < ActiveRecord::Base
|
1004
|
+
self.table_name = 'numeric_data'
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
def test_numeric_fields
|
1008
|
+
m = NumericData.new(
|
1009
|
+
:bank_balance => 1586.43,
|
1010
|
+
:big_bank_balance => BigDecimal("1000234000567.95"),
|
1011
|
+
:world_population => 6000000000,
|
1012
|
+
:my_house_population => 3
|
1013
|
+
)
|
1014
|
+
assert m.save
|
1015
|
+
|
1016
|
+
m1 = NumericData.find(m.id)
|
1017
|
+
assert_not_nil m1
|
1018
|
+
|
1019
|
+
# As with migration_test.rb, we should make world_population >= 2**62
|
1020
|
+
# to cover 64-bit platforms and test it is a Bignum, but the main thing
|
1021
|
+
# is that it's an Integer.
|
1022
|
+
assert_kind_of Integer, m1.world_population
|
1023
|
+
assert_equal 6000000000, m1.world_population
|
1024
|
+
|
1025
|
+
assert_kind_of Fixnum, m1.my_house_population
|
1026
|
+
assert_equal 3, m1.my_house_population
|
1027
|
+
|
1028
|
+
assert_kind_of BigDecimal, m1.bank_balance
|
1029
|
+
assert_equal BigDecimal("1586.43"), m1.bank_balance
|
1030
|
+
|
1031
|
+
assert_kind_of BigDecimal, m1.big_bank_balance
|
1032
|
+
assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
|
1033
|
+
|
1034
|
+
end
|
1035
|
+
|
925
1036
|
def test_auto_id
|
926
1037
|
auto = AutoId.new
|
927
1038
|
auto.save
|
928
1039
|
assert (auto.id > 0)
|
929
1040
|
end
|
930
|
-
|
1041
|
+
|
931
1042
|
def quote_column_name(name)
|
932
1043
|
"<#{name}>"
|
933
1044
|
end
|
@@ -942,12 +1053,8 @@ class BasicsTest < Test::Unit::TestCase
|
|
942
1053
|
end
|
943
1054
|
|
944
1055
|
def test_sql_injection_via_find
|
945
|
-
assert_raises(ActiveRecord::RecordNotFound) do
|
946
|
-
|
947
|
-
end
|
948
|
-
|
949
|
-
assert_raises(ActiveRecord::RecordNotFound) do
|
950
|
-
Topic.find(";;; this should raise an RecordNotFound error")
|
1056
|
+
assert_raises(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
|
1057
|
+
Topic.find("123456 OR id > 0")
|
951
1058
|
end
|
952
1059
|
end
|
953
1060
|
|
@@ -961,6 +1068,17 @@ class BasicsTest < Test::Unit::TestCase
|
|
961
1068
|
assert_equal(41, c2.references)
|
962
1069
|
end
|
963
1070
|
|
1071
|
+
def test_quoting_arrays
|
1072
|
+
replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
|
1073
|
+
assert_equal topics(:first).replies.size, replies.size
|
1074
|
+
|
1075
|
+
# DB2 doesn't support "WHERE (id IN (NULL))" clause
|
1076
|
+
unless current_adapter?(:ODBCAdapter) && [:db2].include?(ActiveRecord::Base.connection.dbmsName)
|
1077
|
+
replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
|
1078
|
+
assert_equal 0, replies.size
|
1079
|
+
end
|
1080
|
+
end
|
1081
|
+
|
964
1082
|
MyObject = Struct.new :attribute1, :attribute2
|
965
1083
|
|
966
1084
|
def test_serialized_attribute
|
@@ -994,6 +1112,18 @@ class BasicsTest < Test::Unit::TestCase
|
|
994
1112
|
assert_equal author_name, Topic.find(topic.id).author_name
|
995
1113
|
end
|
996
1114
|
|
1115
|
+
def test_quote_chars
|
1116
|
+
str = 'The Narrator'
|
1117
|
+
topic = Topic.create(:author_name => str)
|
1118
|
+
assert_equal str, topic.author_name
|
1119
|
+
|
1120
|
+
assert_kind_of ActiveSupport::Multibyte::Chars, str.chars
|
1121
|
+
topic = Topic.find_by_author_name(str.chars)
|
1122
|
+
|
1123
|
+
assert_kind_of Topic, topic
|
1124
|
+
assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
|
1125
|
+
end
|
1126
|
+
|
997
1127
|
def test_class_level_destroy
|
998
1128
|
should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
|
999
1129
|
Topic.find(1).replies << should_be_destroyed_reply
|
@@ -1013,12 +1143,12 @@ class BasicsTest < Test::Unit::TestCase
|
|
1013
1143
|
end
|
1014
1144
|
|
1015
1145
|
def test_increment_attribute
|
1016
|
-
assert_equal
|
1146
|
+
assert_equal 1, topics(:first).replies_count
|
1017
1147
|
topics(:first).increment! :replies_count
|
1018
|
-
assert_equal
|
1148
|
+
assert_equal 2, topics(:first, :reload).replies_count
|
1019
1149
|
|
1020
1150
|
topics(:first).increment(:replies_count).increment!(:replies_count)
|
1021
|
-
assert_equal
|
1151
|
+
assert_equal 4, topics(:first, :reload).replies_count
|
1022
1152
|
end
|
1023
1153
|
|
1024
1154
|
def test_increment_nil_attribute
|
@@ -1029,13 +1159,13 @@ class BasicsTest < Test::Unit::TestCase
|
|
1029
1159
|
|
1030
1160
|
def test_decrement_attribute
|
1031
1161
|
topics(:first).increment(:replies_count).increment!(:replies_count)
|
1032
|
-
assert_equal
|
1162
|
+
assert_equal 3, topics(:first).replies_count
|
1033
1163
|
|
1034
1164
|
topics(:first).decrement!(:replies_count)
|
1035
|
-
assert_equal
|
1165
|
+
assert_equal 2, topics(:first, :reload).replies_count
|
1036
1166
|
|
1037
1167
|
topics(:first).decrement(:replies_count).decrement!(:replies_count)
|
1038
|
-
assert_equal
|
1168
|
+
assert_equal 0, topics(:first, :reload).replies_count
|
1039
1169
|
end
|
1040
1170
|
|
1041
1171
|
def test_toggle_attribute
|
@@ -1115,7 +1245,7 @@ class BasicsTest < Test::Unit::TestCase
|
|
1115
1245
|
def test_count_with_join
|
1116
1246
|
res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
|
1117
1247
|
res2 = nil
|
1118
|
-
|
1248
|
+
assert_deprecated 'count' do
|
1119
1249
|
res2 = Post.count("posts.#{QUOTED_TYPE} = 'Post'",
|
1120
1250
|
"LEFT JOIN comments ON posts.id=comments.post_id")
|
1121
1251
|
end
|
@@ -1128,21 +1258,21 @@ class BasicsTest < Test::Unit::TestCase
|
|
1128
1258
|
end
|
1129
1259
|
assert_equal res, res3
|
1130
1260
|
|
1131
|
-
res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments
|
1261
|
+
res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
|
1132
1262
|
res5 = nil
|
1133
1263
|
assert_nothing_raised do
|
1134
|
-
res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=
|
1135
|
-
:joins => "p, comments
|
1264
|
+
res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
|
1265
|
+
:joins => "p, comments co",
|
1136
1266
|
:select => "p.id")
|
1137
1267
|
end
|
1138
1268
|
|
1139
1269
|
assert_equal res4, res5
|
1140
1270
|
|
1141
|
-
res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments
|
1271
|
+
res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
|
1142
1272
|
res7 = nil
|
1143
1273
|
assert_nothing_raised do
|
1144
|
-
res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=
|
1145
|
-
:joins => "p, comments
|
1274
|
+
res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
|
1275
|
+
:joins => "p, comments co",
|
1146
1276
|
:select => "p.id",
|
1147
1277
|
:distinct => true)
|
1148
1278
|
end
|
@@ -1198,13 +1328,104 @@ class BasicsTest < Test::Unit::TestCase
|
|
1198
1328
|
assert_equal Developer.count, developers.size
|
1199
1329
|
end
|
1200
1330
|
|
1201
|
-
def
|
1331
|
+
def test_scoped_find_order
|
1332
|
+
# Test order in scope
|
1333
|
+
scoped_developers = Developer.with_scope(:find => { :limit => 1, :order => 'salary DESC' }) do
|
1334
|
+
Developer.find(:all)
|
1335
|
+
end
|
1336
|
+
assert_equal 'Jamis', scoped_developers.first.name
|
1337
|
+
assert scoped_developers.include?(developers(:jamis))
|
1338
|
+
# Test scope without order and order in find
|
1339
|
+
scoped_developers = Developer.with_scope(:find => { :limit => 1 }) do
|
1340
|
+
Developer.find(:all, :order => 'salary DESC')
|
1341
|
+
end
|
1342
|
+
# Test scope order + find order, find has priority
|
1343
|
+
scoped_developers = Developer.with_scope(:find => { :limit => 3, :order => 'id DESC' }) do
|
1344
|
+
Developer.find(:all, :order => 'salary ASC')
|
1345
|
+
end
|
1346
|
+
assert scoped_developers.include?(developers(:poor_jamis))
|
1347
|
+
assert scoped_developers.include?(developers(:david))
|
1348
|
+
assert scoped_developers.include?(developers(:dev_10))
|
1349
|
+
# Test without scoped find conditions to ensure we get the right thing
|
1350
|
+
developers = Developer.find(:all, :order => 'id', :limit => 1)
|
1351
|
+
assert scoped_developers.include?(developers(:david))
|
1352
|
+
end
|
1353
|
+
|
1354
|
+
def test_scoped_find_limit_offset_including_has_many_association
|
1355
|
+
topics = Topic.with_scope(:find => {:limit => 1, :offset => 1, :include => :replies}) do
|
1356
|
+
Topic.find(:all, :order => "topics.id")
|
1357
|
+
end
|
1358
|
+
assert_equal 1, topics.size
|
1359
|
+
assert_equal 2, topics.first.id
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
def test_scoped_find_order_including_has_many_association
|
1363
|
+
developers = Developer.with_scope(:find => { :order => 'developers.salary DESC', :include => :projects }) do
|
1364
|
+
Developer.find(:all)
|
1365
|
+
end
|
1366
|
+
assert developers.size >= 2
|
1367
|
+
for i in 1...developers.size
|
1368
|
+
assert developers[i-1].salary >= developers[i].salary
|
1369
|
+
end
|
1370
|
+
end
|
1371
|
+
|
1372
|
+
def test_abstract_class
|
1373
|
+
assert !ActiveRecord::Base.abstract_class?
|
1202
1374
|
assert LoosePerson.abstract_class?
|
1203
1375
|
assert !LooseDescendant.abstract_class?
|
1376
|
+
end
|
1377
|
+
|
1378
|
+
def test_base_class
|
1204
1379
|
assert_equal LoosePerson, LoosePerson.base_class
|
1205
1380
|
assert_equal LooseDescendant, LooseDescendant.base_class
|
1206
1381
|
assert_equal TightPerson, TightPerson.base_class
|
1207
1382
|
assert_equal TightPerson, TightDescendant.base_class
|
1383
|
+
|
1384
|
+
assert_equal Post, Post.base_class
|
1385
|
+
assert_equal Post, SpecialPost.base_class
|
1386
|
+
assert_equal Post, StiPost.base_class
|
1387
|
+
assert_equal SubStiPost, SubStiPost.base_class
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
def test_descends_from_active_record
|
1391
|
+
# Tries to call Object.abstract_class?
|
1392
|
+
assert_raise(NoMethodError) do
|
1393
|
+
ActiveRecord::Base.descends_from_active_record?
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
# Abstract subclass of AR::Base.
|
1397
|
+
assert LoosePerson.descends_from_active_record?
|
1398
|
+
|
1399
|
+
# Concrete subclass of an abstract class.
|
1400
|
+
assert LooseDescendant.descends_from_active_record?
|
1401
|
+
|
1402
|
+
# Concrete subclass of AR::Base.
|
1403
|
+
assert TightPerson.descends_from_active_record?
|
1404
|
+
|
1405
|
+
# Concrete subclass of a concrete class but has no type column.
|
1406
|
+
assert TightDescendant.descends_from_active_record?
|
1407
|
+
|
1408
|
+
# Concrete subclass of AR::Base.
|
1409
|
+
assert Post.descends_from_active_record?
|
1410
|
+
|
1411
|
+
# Abstract subclass of a concrete class which has a type column.
|
1412
|
+
# This is pathological, as you'll never have Sub < Abstract < Concrete.
|
1413
|
+
assert !StiPost.descends_from_active_record?
|
1414
|
+
|
1415
|
+
# Concrete subclasses an abstract class which has a type column.
|
1416
|
+
assert !SubStiPost.descends_from_active_record?
|
1417
|
+
end
|
1418
|
+
|
1419
|
+
def test_find_on_abstract_base_class_doesnt_use_type_condition
|
1420
|
+
old_class = LooseDescendant
|
1421
|
+
Object.send :remove_const, :LooseDescendant
|
1422
|
+
|
1423
|
+
descendant = old_class.create!
|
1424
|
+
assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
|
1425
|
+
ensure
|
1426
|
+
unless Object.const_defined?(:LooseDescendant)
|
1427
|
+
Object.const_set :LooseDescendant, old_class
|
1428
|
+
end
|
1208
1429
|
end
|
1209
1430
|
|
1210
1431
|
def test_assert_queries
|
@@ -1218,26 +1439,29 @@ class BasicsTest < Test::Unit::TestCase
|
|
1218
1439
|
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true)
|
1219
1440
|
bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
|
1220
1441
|
written_on_in_current_timezone = topics(:first).written_on.xmlschema
|
1442
|
+
#cb+- Follwoing works
|
1443
|
+
#written_on = topics(:first).written_on
|
1221
1444
|
last_read_in_current_timezone = topics(:first).last_read.xmlschema
|
1222
1445
|
assert_equal "<topic>", xml.first(7)
|
1223
1446
|
assert xml.include?(%(<title>The First Topic</title>))
|
1224
1447
|
assert xml.include?(%(<author-name>David</author-name>))
|
1225
1448
|
assert xml.include?(%(<id type="integer">1</id>))
|
1226
|
-
assert xml.include?(%(<replies-count type="integer">
|
1449
|
+
assert xml.include?(%(<replies-count type="integer">1</replies-count>))
|
1227
1450
|
assert xml.include?(%(<written-on type="datetime">#{written_on_in_current_timezone}</written-on>))
|
1451
|
+
#cb+- Following works
|
1452
|
+
#assert xml.include?(%(<written-on type="timestamp">#{written_on}</written-on>))
|
1228
1453
|
assert xml.include?(%(<content>Have a nice day</content>))
|
1229
1454
|
assert xml.include?(%(<author-email-address>david@loudthinking.com</author-email-address>))
|
1230
|
-
assert xml.
|
1455
|
+
assert xml.match(%(<parent-id type="integer"></parent-id>))
|
1231
1456
|
# Following databases don't have a true date type, only a composite datetime type
|
1232
|
-
if current_adapter?(:SybaseAdapter
|
1233
|
-
|
1234
|
-
[:ingres,:oracle,:microsoftsqlserver].include?(ActiveRecord::Base.connection.dbmsName)
|
1457
|
+
if current_adapter?(:SybaseAdapter, :SQLServerAdapter, :OracleAdapter) or
|
1458
|
+
current_adapter?(:ODBCAdapter) && [:ingres,:oracle,:microsoftsqlserver].include?(ActiveRecord::Base.connection.dbmsName)
|
1235
1459
|
assert xml.include?(%(<last-read type="datetime">#{last_read_in_current_timezone}</last-read>))
|
1236
1460
|
else
|
1237
1461
|
assert xml.include?(%(<last-read type="date">2004-04-15</last-read>))
|
1238
1462
|
end
|
1239
1463
|
# Following databases don't have a true boolean type
|
1240
|
-
unless current_adapter?(:OracleAdapter
|
1464
|
+
unless current_adapter?(:OracleAdapter, :DB2Adapter)
|
1241
1465
|
if current_adapter?(:ODBCAdapter) &&
|
1242
1466
|
[:ingres,:virtuoso,:oracle,:mysql,:db2,:progress].include?(ActiveRecord::Base.connection.dbmsName)
|
1243
1467
|
assert xml.include?(%(<approved type="integer">0</approved>)), "Approved should be an integer"
|
@@ -1246,29 +1470,53 @@ class BasicsTest < Test::Unit::TestCase
|
|
1246
1470
|
end
|
1247
1471
|
end
|
1248
1472
|
# Oracle and DB2 don't have a true time-only field
|
1249
|
-
unless current_adapter?(:OracleAdapter
|
1473
|
+
unless current_adapter?(:OracleAdapter, :DB2Adapter)
|
1250
1474
|
assert xml.include?(%(<bonus-time type="datetime">#{bonus_time_in_current_timezone}</bonus-time>))
|
1251
1475
|
end
|
1252
1476
|
end
|
1253
|
-
|
1477
|
+
|
1254
1478
|
def test_to_xml_skipping_attributes
|
1255
|
-
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => :title)
|
1479
|
+
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
|
1256
1480
|
assert_equal "<topic>", xml.first(7)
|
1257
1481
|
assert !xml.include?(%(<title>The First Topic</title>))
|
1258
1482
|
assert xml.include?(%(<author-name>David</author-name>))
|
1259
1483
|
|
1260
|
-
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [
|
1484
|
+
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
|
1261
1485
|
assert !xml.include?(%(<title>The First Topic</title>))
|
1262
1486
|
assert !xml.include?(%(<author-name>David</author-name>))
|
1263
1487
|
end
|
1264
|
-
|
1488
|
+
|
1265
1489
|
def test_to_xml_including_has_many_association
|
1266
|
-
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
|
1490
|
+
xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
|
1267
1491
|
assert_equal "<topic>", xml.first(7)
|
1268
1492
|
assert xml.include?(%(<replies><reply>))
|
1269
1493
|
assert xml.include?(%(<title>The Second Topic's of the day</title>))
|
1270
1494
|
end
|
1271
1495
|
|
1496
|
+
def test_array_to_xml_including_has_many_association
|
1497
|
+
xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
|
1498
|
+
assert xml.include?(%(<replies><reply>))
|
1499
|
+
end
|
1500
|
+
|
1501
|
+
def test_array_to_xml_including_methods
|
1502
|
+
xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
|
1503
|
+
assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
|
1504
|
+
assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
|
1505
|
+
end
|
1506
|
+
|
1507
|
+
def test_array_to_xml_including_has_one_association
|
1508
|
+
xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
|
1509
|
+
assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
|
1510
|
+
assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
|
1511
|
+
end
|
1512
|
+
|
1513
|
+
def test_array_to_xml_including_belongs_to_association
|
1514
|
+
xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
|
1515
|
+
assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
|
1516
|
+
assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
|
1517
|
+
assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
|
1518
|
+
end
|
1519
|
+
|
1272
1520
|
def test_to_xml_including_belongs_to_association
|
1273
1521
|
xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
|
1274
1522
|
assert !xml.include?("<firm>")
|
@@ -1295,6 +1543,12 @@ class BasicsTest < Test::Unit::TestCase
|
|
1295
1543
|
assert xml.include?(%(<clients><client>))
|
1296
1544
|
end
|
1297
1545
|
|
1546
|
+
def test_to_xml_including_methods
|
1547
|
+
xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
|
1548
|
+
assert_equal "<company>", xml.first(9)
|
1549
|
+
assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
|
1550
|
+
end
|
1551
|
+
|
1298
1552
|
def test_except_attributes
|
1299
1553
|
assert_equal(
|
1300
1554
|
%w( author_name type id approved replies_count bonus_time written_on content author_email_address parent_id last_read),
|
@@ -1316,6 +1570,10 @@ class BasicsTest < Test::Unit::TestCase
|
|
1316
1570
|
assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
|
1317
1571
|
assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
|
1318
1572
|
end
|
1573
|
+
|
1574
|
+
def test_to_param_should_return_string
|
1575
|
+
assert_kind_of String, Client.find(:first).to_param
|
1576
|
+
end
|
1319
1577
|
|
1320
1578
|
# FIXME: this test ought to run, but it needs to run sandboxed so that it
|
1321
1579
|
# doesn't b0rk the current test environment by undefing everything.
|
@@ -1341,7 +1599,7 @@ class BasicsTest < Test::Unit::TestCase
|
|
1341
1599
|
|
1342
1600
|
private
|
1343
1601
|
def assert_readers(model, exceptions)
|
1344
|
-
expected_readers = Set.new(model.column_names -
|
1602
|
+
expected_readers = Set.new(model.column_names - ['id'])
|
1345
1603
|
expected_readers += expected_readers.map { |col| "#{col}?" }
|
1346
1604
|
expected_readers -= exceptions
|
1347
1605
|
assert_equal expected_readers, model.read_methods
|