odbc-rails 1.3 → 1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|