ibm_db 2.5.6-x86-mswin32-60 → 2.5.7-x86-mswin32-60

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.
Files changed (38) hide show
  1. data/CHANGES +6 -0
  2. data/README +1 -1
  3. data/ext/Makefile.nt32 +3 -3
  4. data/ext/Makefile.nt32.191 +212 -0
  5. data/ext/ibm_db.c +30 -5
  6. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +300 -108
  7. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1 -1
  8. data/lib/mswin32/rb18x/ibm_db.so +0 -0
  9. data/test/cases/adapter_test.rb +25 -22
  10. data/test/cases/associations/belongs_to_associations_test.rb +245 -43
  11. data/test/cases/associations/cascaded_eager_loading_test.rb +28 -26
  12. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +60 -156
  13. data/test/cases/associations/join_model_test.rb +96 -146
  14. data/test/cases/attribute_methods_test.rb +98 -33
  15. data/test/cases/base_test.rb +525 -103
  16. data/test/cases/calculations_test.rb +92 -8
  17. data/test/cases/migration_test.rb +533 -207
  18. data/test/cases/persistence_test.rb +636 -0
  19. data/test/cases/query_cache_test.rb +242 -0
  20. data/test/cases/relations_test.rb +1019 -0
  21. data/test/cases/schema_dumper_test.rb +37 -17
  22. data/test/cases/transaction_callbacks_test.rb +300 -0
  23. data/test/cases/validations/uniqueness_validation_test.rb +38 -22
  24. data/test/cases/xml_serialization_test.rb +276 -0
  25. data/test/config.yml +154 -0
  26. data/test/connections/native_ibm_db/connection.rb +2 -0
  27. data/test/models/warehouse_thing.rb +4 -4
  28. data/test/schema/i5/ibm_db_specific_schema.rb +3 -1
  29. data/test/schema/ids/ibm_db_specific_schema.rb +3 -1
  30. data/test/schema/luw/ibm_db_specific_schema.rb +2 -0
  31. data/test/schema/schema.rb +174 -89
  32. data/test/schema/zOS/ibm_db_specific_schema.rb +3 -1
  33. metadata +9 -7
  34. data/lib/mswin32/rb19x/ibm_db.so +0 -0
  35. data/test/cases/associations/eager_test.rb +0 -862
  36. data/test/cases/associations/has_many_through_associations_test.rb +0 -461
  37. data/test/cases/finder_test.rb +0 -1088
  38. data/test/cases/fixtures_test.rb +0 -684
@@ -18,7 +18,9 @@ require 'models/comment'
18
18
  require 'models/minimalistic'
19
19
  require 'models/warehouse_thing'
20
20
  require 'models/parrot'
21
- require 'models/loose_person'
21
+ require 'models/person'
22
+ require 'models/edge'
23
+ require 'models/joke'
22
24
  require 'rexml/document'
23
25
  require 'active_support/core_ext/exception'
24
26
 
@@ -43,10 +45,98 @@ class ReadonlyTitlePost < Post
43
45
  attr_readonly :title
44
46
  end
45
47
 
48
+ class ProtectedTitlePost < Post
49
+ attr_protected :title
50
+ end
51
+
52
+ class Weird < ActiveRecord::Base; end
53
+
46
54
  class Boolean < ActiveRecord::Base; end
47
55
 
48
56
  class BasicsTest < ActiveRecord::TestCase
49
- fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, :warehouse_things, :authors, :categorizations, :categories, :posts
57
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse_things', :authors, :categorizations, :categories, :posts
58
+
59
+ def test_column_names_are_escaped
60
+ conn = ActiveRecord::Base.connection
61
+ classname = conn.class.name[/[^:]*$/]
62
+ badchar = {
63
+ 'SQLite3Adapter' => '"',
64
+ 'MysqlAdapter' => '`',
65
+ 'Mysql2Adapter' => '`',
66
+ 'PostgreSQLAdapter' => '"',
67
+ 'OracleAdapter' => '"',
68
+ 'IBM_DBAdapter' => '"'
69
+ }.fetch(classname) {
70
+ raise "need a bad char for #{classname}"
71
+ }
72
+
73
+ quoted = conn.quote_column_name "foo#{badchar}bar"
74
+ if current_adapter?(:OracleAdapter)
75
+ # Oracle does not allow double quotes in table and column names at all
76
+ # therefore quoting removes them
77
+ assert_equal("#{badchar}foobar#{badchar}", quoted)
78
+ elsif current_adapter?(:IBM_DBAdapter)
79
+ assert_equal("foo#{badchar}bar", quoted)
80
+ else
81
+ assert_equal("#{badchar}foo#{badchar * 2}bar#{badchar}", quoted)
82
+ end
83
+ end
84
+
85
+ def test_columns_should_obey_set_primary_key
86
+ pk = Subscriber.columns.find { |x| x.name == 'nick' }
87
+ assert pk.primary, 'nick should be primary key'
88
+ end
89
+
90
+ def test_primary_key_with_no_id
91
+ assert_nil Edge.primary_key
92
+ end
93
+
94
+ unless current_adapter?(:PostgreSQLAdapter,:OracleAdapter,:SQLServerAdapter, :IBM_DBAdapter)
95
+ def test_limit_with_comma
96
+ assert_nothing_raised do
97
+ Topic.limit("1,2").all
98
+ end
99
+ end
100
+ end
101
+
102
+ def test_limit_without_comma
103
+ assert_nothing_raised do
104
+ assert_equal 1, Topic.limit("1").all.length
105
+ end
106
+
107
+ assert_nothing_raised do
108
+ assert_equal 1, Topic.limit(1).all.length
109
+ end
110
+ end
111
+
112
+ def test_invalid_limit
113
+ assert_raises(ArgumentError) do
114
+ Topic.limit("asdfadf").all
115
+ end
116
+ end
117
+
118
+ def test_limit_should_sanitize_sql_injection_for_limit_without_comas
119
+ assert_raises(ArgumentError) do
120
+ Topic.limit("1 select * from schema").all
121
+ end
122
+ end
123
+
124
+ def test_limit_should_sanitize_sql_injection_for_limit_with_comas
125
+ assert_raises(ArgumentError) do
126
+ Topic.limit("1, 7 procedure help()").all
127
+ end
128
+ end
129
+
130
+ unless current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter) || current_adapter?(:IBM_DBAdapter)
131
+ def test_limit_should_allow_sql_literal
132
+ assert_equal 1, Topic.limit(Arel.sql('2-1')).all.length
133
+ end
134
+ end
135
+
136
+ def test_select_symbol
137
+ topic_ids = Topic.select(:id).map(&:id).sort
138
+ assert_equal Topic.all.map(&:id).sort, topic_ids
139
+ end
50
140
 
51
141
  def test_table_exists
52
142
  assert !NonExistentTable.table_exists?
@@ -69,24 +159,6 @@ class BasicsTest < ActiveRecord::TestCase
69
159
  end
70
160
  end
71
161
 
72
- def test_use_table_engine_for_quoting_where
73
- relation = Topic.where(Topic.arel_table[:id].eq(1))
74
- engine = relation.table.engine
75
-
76
- fakepool = Class.new(Struct.new(:spec)) {
77
- def with_connection; yield self; end
78
- def connection_pool; self; end
79
- def quote_table_name(*args); raise "lol quote_table_name"; end
80
- }
81
-
82
- relation.table.engine = fakepool.new(engine.connection_pool.spec)
83
-
84
- error = assert_raises(RuntimeError) { relation.to_a }
85
- assert_match('lol', error.message)
86
- ensure
87
- relation.table.engine = engine
88
- end
89
-
90
162
  def test_preserving_time_objects
91
163
  assert_kind_of(
92
164
  Time, Topic.find(1).bonus_time,
@@ -111,7 +183,7 @@ class BasicsTest < ActiveRecord::TestCase
111
183
  with_active_record_default_timezone :utc do
112
184
  time = Time.local(2000)
113
185
  topic = Topic.create('written_on' => time)
114
- saved_time = Topic.find(topic.id).written_on
186
+ saved_time = Topic.find(topic.id).reload.written_on
115
187
  assert_equal time, saved_time
116
188
  assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "EST"], time.to_a
117
189
  assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
@@ -125,7 +197,7 @@ class BasicsTest < ActiveRecord::TestCase
125
197
  Time.use_zone 'Central Time (US & Canada)' do
126
198
  time = Time.zone.local(2000)
127
199
  topic = Topic.create('written_on' => time)
128
- saved_time = Topic.find(topic.id).written_on
200
+ saved_time = Topic.find(topic.id).reload.written_on
129
201
  assert_equal time, saved_time
130
202
  assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
131
203
  assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
@@ -138,7 +210,7 @@ class BasicsTest < ActiveRecord::TestCase
138
210
  with_env_tz 'America/New_York' do
139
211
  time = Time.utc(2000)
140
212
  topic = Topic.create('written_on' => time)
141
- saved_time = Topic.find(topic.id).written_on
213
+ saved_time = Topic.find(topic.id).reload.written_on
142
214
  assert_equal time, saved_time
143
215
  assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a
144
216
  assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a
@@ -151,7 +223,7 @@ class BasicsTest < ActiveRecord::TestCase
151
223
  Time.use_zone 'Central Time (US & Canada)' do
152
224
  time = Time.zone.local(2000)
153
225
  topic = Topic.create('written_on' => time)
154
- saved_time = Topic.find(topic.id).written_on
226
+ saved_time = Topic.find(topic.id).reload.written_on
155
227
  assert_equal time, saved_time
156
228
  assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
157
229
  assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], saved_time.to_a
@@ -177,7 +249,7 @@ class BasicsTest < ActiveRecord::TestCase
177
249
 
178
250
  def test_initialize_with_invalid_attribute
179
251
  begin
180
- topic = Topic.new({ "title" => "test",
252
+ Topic.new({ "title" => "test",
181
253
  "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
182
254
  rescue ActiveRecord::MultiparameterAssignmentErrors => ex
183
255
  assert_equal(1, ex.errors.size)
@@ -302,6 +374,15 @@ class BasicsTest < ActiveRecord::TestCase
302
374
  GUESSED_CLASSES.each(&:reset_table_name)
303
375
  end
304
376
 
377
+ def test_singular_table_name_guesses_for_individual_table
378
+ CreditCard.pluralize_table_names = false
379
+ CreditCard.reset_table_name
380
+ assert_equal "credit_card", CreditCard.table_name
381
+ assert_equal "categories", Category.table_name
382
+ ensure
383
+ CreditCard.pluralize_table_names = true
384
+ CreditCard.reset_table_name
385
+ end
305
386
 
306
387
  if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
307
388
  def test_update_all_with_order_and_limit
@@ -384,6 +465,10 @@ class BasicsTest < ActiveRecord::TestCase
384
465
  assert_equal Topic.find(1), Topic.find(2).topic
385
466
  end
386
467
 
468
+ def test_find_by_slug
469
+ assert_equal Topic.find('1-meowmeow'), Topic.find(1)
470
+ end
471
+
387
472
  def test_equality_of_new_records
388
473
  assert_not_equal Topic.new, Topic.new
389
474
  end
@@ -401,6 +486,19 @@ class BasicsTest < ActiveRecord::TestCase
401
486
  assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
402
487
  end
403
488
 
489
+ def test_comparison
490
+ topic_1 = Topic.create!
491
+ topic_2 = Topic.create!
492
+
493
+ assert_equal [topic_2, topic_1].sort, [topic_1, topic_2]
494
+ end
495
+
496
+ def test_comparison_with_different_objects
497
+ topic = Topic.create
498
+ category = Category.create(:name => "comparison")
499
+ assert_nil topic <=> category
500
+ end
501
+
404
502
  def test_readonly_attributes
405
503
  assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
406
504
 
@@ -414,6 +512,23 @@ class BasicsTest < ActiveRecord::TestCase
414
512
  assert_equal "changed", post.body
415
513
  end
416
514
 
515
+ def test_non_valid_identifier_column_name
516
+ weird = Weird.create('a$b' => 'value')
517
+ weird.reload
518
+ assert_equal 'value', weird.send('a$b')
519
+
520
+ weird.update_column('a$b', 'value2')
521
+ weird.reload
522
+ assert_equal 'value2', weird.send('a$b')
523
+ end
524
+
525
+ def test_attributes_guard_protected_attributes_is_deprecated
526
+ attributes = { "title" => "An amazing title" }
527
+ post = ProtectedTitlePost.new
528
+ assert_deprecated { post.send(:attributes=, attributes, false) }
529
+ assert_equal "An amazing title", post.title
530
+ end
531
+
417
532
  def test_multiparameter_attributes_on_date
418
533
  attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
419
534
  topic = Topic.find(1)
@@ -494,6 +609,29 @@ class BasicsTest < ActiveRecord::TestCase
494
609
  assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
495
610
  end
496
611
 
612
+ def test_multiparameter_attributes_on_time_with_no_date
613
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
614
+ attributes = {
615
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
616
+ }
617
+ topic = Topic.find(1)
618
+ topic.attributes = attributes
619
+ end
620
+ assert_equal("written_on", ex.errors[0].attribute)
621
+ end
622
+
623
+ def test_multiparameter_attributes_on_time_with_invalid_time_params
624
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
625
+ attributes = {
626
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
627
+ "written_on(4i)" => "2004", "written_on(5i)" => "36", "written_on(6i)" => "64",
628
+ }
629
+ topic = Topic.find(1)
630
+ topic.attributes = attributes
631
+ end
632
+ assert_equal("written_on", ex.errors[0].attribute)
633
+ end
634
+
497
635
  def test_multiparameter_attributes_on_time_with_old_date
498
636
  attributes = {
499
637
  "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
@@ -505,6 +643,82 @@ class BasicsTest < ActiveRecord::TestCase
505
643
  assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
506
644
  end
507
645
 
646
+ def test_multiparameter_attributes_on_time_will_raise_on_big_time_if_missing_date_parts
647
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
648
+ attributes = {
649
+ "written_on(4i)" => "16", "written_on(5i)" => "24"
650
+ }
651
+ topic = Topic.find(1)
652
+ topic.attributes = attributes
653
+ end
654
+ assert_equal("written_on", ex.errors[0].attribute)
655
+ end
656
+
657
+ def test_multiparameter_attributes_on_time_with_raise_on_small_time_if_missing_date_parts
658
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
659
+ attributes = {
660
+ "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
661
+ }
662
+ topic = Topic.find(1)
663
+ topic.attributes = attributes
664
+ end
665
+ assert_equal("written_on", ex.errors[0].attribute)
666
+ end
667
+
668
+ def test_multiparameter_attributes_on_time_will_ignore_hour_if_missing
669
+ attributes = {
670
+ "written_on(1i)" => "2004", "written_on(2i)" => "12", "written_on(3i)" => "12",
671
+ "written_on(5i)" => "12", "written_on(6i)" => "02"
672
+ }
673
+ topic = Topic.find(1)
674
+ topic.attributes = attributes
675
+ assert_equal Time.local(2004, 12, 12, 0, 12, 2), topic.written_on
676
+ end
677
+
678
+ def test_multiparameter_attributes_on_time_will_ignore_hour_if_blank
679
+ attributes = {
680
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
681
+ "written_on(4i)" => "", "written_on(5i)" => "12", "written_on(6i)" => "02"
682
+ }
683
+ topic = Topic.find(1)
684
+ topic.attributes = attributes
685
+ assert_equal 1, topic.written_on.year
686
+ assert_equal 1, topic.written_on.month
687
+ assert_equal 1, topic.written_on.day
688
+ assert_equal 0, topic.written_on.hour
689
+ assert_equal 12, topic.written_on.min
690
+ assert_equal 2, topic.written_on.sec
691
+ end
692
+
693
+ def test_multiparameter_attributes_on_time_will_ignore_date_if_empty
694
+ attributes = {
695
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
696
+ "written_on(4i)" => "16", "written_on(5i)" => "24"
697
+ }
698
+ topic = Topic.find(1)
699
+ topic.attributes = attributes
700
+ assert_equal 1, topic.written_on.year
701
+ assert_equal 1, topic.written_on.month
702
+ assert_equal 1, topic.written_on.day
703
+ assert_equal 16, topic.written_on.hour
704
+ assert_equal 24, topic.written_on.min
705
+ assert_equal 0, topic.written_on.sec
706
+ end
707
+ def test_multiparameter_attributes_on_time_with_seconds_will_ignore_date_if_empty
708
+ attributes = {
709
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
710
+ "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
711
+ }
712
+ topic = Topic.find(1)
713
+ topic.attributes = attributes
714
+ assert_equal 1, topic.written_on.year
715
+ assert_equal 1, topic.written_on.month
716
+ assert_equal 1, topic.written_on.day
717
+ assert_equal 16, topic.written_on.hour
718
+ assert_equal 12, topic.written_on.min
719
+ assert_equal 02, topic.written_on.sec
720
+ end
721
+
508
722
  def test_multiparameter_attributes_on_time_with_utc
509
723
  ActiveRecord::Base.default_timezone = :utc
510
724
  attributes = {
@@ -611,6 +825,42 @@ class BasicsTest < ActiveRecord::TestCase
611
825
  assert_equal address, customer.address
612
826
  end
613
827
 
828
+ def test_multiparameter_assignment_of_aggregation_out_of_order
829
+ customer = Customer.new
830
+ address = Address.new("The Street", "The City", "The Country")
831
+ attributes = { "address(3)" => address.country, "address(2)" => address.city, "address(1)" => address.street }
832
+ customer.attributes = attributes
833
+ assert_equal address, customer.address
834
+ end
835
+
836
+ def test_multiparameter_assignment_of_aggregation_with_missing_values
837
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
838
+ customer = Customer.new
839
+ address = Address.new("The Street", "The City", "The Country")
840
+ attributes = { "address(2)" => address.city, "address(3)" => address.country }
841
+ customer.attributes = attributes
842
+ end
843
+ assert_equal("address", ex.errors[0].attribute)
844
+ end
845
+
846
+ def test_multiparameter_assignment_of_aggregation_with_blank_values
847
+ customer = Customer.new
848
+ address = Address.new("The Street", "The City", "The Country")
849
+ attributes = { "address(1)" => "", "address(2)" => address.city, "address(3)" => address.country }
850
+ customer.attributes = attributes
851
+ assert_equal Address.new(nil, "The City", "The Country"), customer.address
852
+ end
853
+
854
+ def test_multiparameter_assignment_of_aggregation_with_large_index
855
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
856
+ customer = Customer.new
857
+ address = Address.new("The Street", "The City", "The Country")
858
+ attributes = { "address(1)" => "The Street", "address(2)" => address.city, "address(3000)" => address.country }
859
+ customer.attributes = attributes
860
+ end
861
+ assert_equal("address", ex.errors[0].attribute)
862
+ end
863
+
614
864
  def test_attributes_on_dummy_time
615
865
  # Oracle, and Sybase do not have a TIME datatype.
616
866
  return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
@@ -660,60 +910,65 @@ class BasicsTest < ActiveRecord::TestCase
660
910
  assert_equal true, Topic.find(1).persisted?
661
911
  end
662
912
 
663
- def test_clone
913
+ def test_dup
664
914
  topic = Topic.find(1)
665
- cloned_topic = nil
666
- assert_nothing_raised { cloned_topic = topic.clone }
667
- assert_equal topic.title, cloned_topic.title
668
- assert !cloned_topic.persisted?
915
+ duped_topic = nil
916
+ assert_nothing_raised { duped_topic = topic.dup }
917
+ assert_equal topic.title, duped_topic.title
918
+ assert !duped_topic.persisted?
669
919
 
670
- # test if the attributes have been cloned
920
+ # test if the attributes have been duped
671
921
  topic.title = "a"
672
- cloned_topic.title = "b"
922
+ duped_topic.title = "b"
673
923
  assert_equal "a", topic.title
674
- assert_equal "b", cloned_topic.title
924
+ assert_equal "b", duped_topic.title
675
925
 
676
- # test if the attribute values have been cloned
926
+ # test if the attribute values have been duped
677
927
  topic.title = {"a" => "b"}
678
- cloned_topic = topic.clone
679
- cloned_topic.title["a"] = "c"
928
+ duped_topic = topic.dup
929
+ duped_topic.title["a"] = "c"
680
930
  assert_equal "b", topic.title["a"]
681
931
 
682
- # test if attributes set as part of after_initialize are cloned correctly
683
- assert_equal topic.author_email_address, cloned_topic.author_email_address
932
+ # test if attributes set as part of after_initialize are duped correctly
933
+ assert_equal topic.author_email_address, duped_topic.author_email_address
684
934
 
685
935
  # test if saved clone object differs from original
686
- cloned_topic.save
687
- assert cloned_topic.persisted?
688
- assert_not_equal cloned_topic.id, topic.id
936
+ duped_topic.save
937
+ assert duped_topic.persisted?
938
+ assert_not_equal duped_topic.id, topic.id
939
+
940
+ duped_topic.reload
941
+ # FIXME: I think this is poor behavior, and will fix it with #5686
942
+ assert_equal({'a' => 'c'}.to_yaml, duped_topic.title)
689
943
  end
690
944
 
691
- def test_clone_with_aggregate_of_same_name_as_attribute
945
+ def test_dup_with_aggregate_of_same_name_as_attribute
692
946
  dev = DeveloperWithAggregate.find(1)
693
947
  assert_kind_of DeveloperSalary, dev.salary
694
948
 
695
- clone = nil
696
- assert_nothing_raised { clone = dev.clone }
697
- assert_kind_of DeveloperSalary, clone.salary
698
- assert_equal dev.salary.amount, clone.salary.amount
699
- assert !clone.persisted?
949
+ dup = nil
950
+ assert_nothing_raised { dup = dev.dup }
951
+ assert_kind_of DeveloperSalary, dup.salary
952
+ assert_equal dev.salary.amount, dup.salary.amount
953
+ assert !dup.persisted?
700
954
 
701
- # test if the attributes have been cloned
702
- original_amount = clone.salary.amount
955
+ # test if the attributes have been dupd
956
+ original_amount = dup.salary.amount
703
957
  dev.salary.amount = 1
704
- assert_equal original_amount, clone.salary.amount
958
+ assert_equal original_amount, dup.salary.amount
705
959
 
706
- assert clone.save
707
- assert clone.persisted?
708
- assert_not_equal clone.id, dev.id
960
+ assert dup.save
961
+ assert dup.persisted?
962
+ assert_not_equal dup.id, dev.id
709
963
  end
710
964
 
711
- def test_clone_does_not_clone_associations
965
+ def test_dup_does_not_copy_associations
712
966
  author = authors(:david)
713
967
  assert_not_equal [], author.posts
968
+ author.send(:clear_association_cache)
714
969
 
715
- author_clone = author.clone
716
- assert_equal [], author_clone.posts
970
+ author_dup = author.dup
971
+ assert_equal [], author_dup.posts
717
972
  end
718
973
 
719
974
  def test_clone_preserves_subtype
@@ -752,24 +1007,24 @@ class BasicsTest < ActiveRecord::TestCase
752
1007
  assert !cloned_developer.salary_changed? # ... and cloned instance should behave same
753
1008
  end
754
1009
 
755
- def test_clone_of_saved_object_marks_attributes_as_dirty
1010
+ def test_dup_of_saved_object_marks_attributes_as_dirty
756
1011
  developer = Developer.create! :name => 'Bjorn', :salary => 100000
757
1012
  assert !developer.name_changed?
758
1013
  assert !developer.salary_changed?
759
1014
 
760
- cloned_developer = developer.clone
1015
+ cloned_developer = developer.dup
761
1016
  assert cloned_developer.name_changed? # both attributes differ from defaults
762
1017
  assert cloned_developer.salary_changed?
763
1018
  end
764
1019
 
765
- def test_clone_of_saved_object_marks_as_dirty_only_changed_attributes
1020
+ def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes
766
1021
  developer = Developer.create! :name => 'Bjorn'
767
- assert !developer.name_changed? # both attributes of saved object should be threated as not changed
1022
+ assert !developer.name_changed? # both attributes of saved object should be treated as not changed
768
1023
  assert !developer.salary_changed?
769
1024
 
770
- cloned_developer = developer.clone
1025
+ cloned_developer = developer.dup
771
1026
  assert cloned_developer.name_changed? # ... but on cloned object should be
772
- assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be threated as not changed on cloned instance
1027
+ assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be treated as not changed on cloned instance
773
1028
  end
774
1029
 
775
1030
  def test_bignum
@@ -815,7 +1070,7 @@ class BasicsTest < ActiveRecord::TestCase
815
1070
  assert g.save
816
1071
 
817
1072
  # Reload and check that we have all the geometric attributes.
818
- h = Geometric.find(g.id)
1073
+ h = ActiveRecord::IdentityMap.without { Geometric.find(g.id) }
819
1074
 
820
1075
  assert_equal '(5,6.1)', h.a_point
821
1076
  assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
@@ -843,7 +1098,7 @@ class BasicsTest < ActiveRecord::TestCase
843
1098
  assert g.save
844
1099
 
845
1100
  # Reload and check that we have all the geometric attributes.
846
- h = Geometric.find(g.id)
1101
+ h = ActiveRecord::IdentityMap.without { Geometric.find(g.id) }
847
1102
 
848
1103
  assert_equal '(5,6.1)', h.a_point
849
1104
  assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
@@ -862,6 +1117,17 @@ class BasicsTest < ActiveRecord::TestCase
862
1117
  self.table_name = 'numeric_data'
863
1118
  end
864
1119
 
1120
+ def test_big_decimal_conditions
1121
+ m = NumericData.new(
1122
+ :bank_balance => 1586.43,
1123
+ :big_bank_balance => BigDecimal("1000234000567.95"),
1124
+ :world_population => 6000000000,
1125
+ :my_house_population => 3
1126
+ )
1127
+ assert m.save
1128
+ assert_equal 0, NumericData.where("bank_balance > ?", 2000.0).count
1129
+ end
1130
+
865
1131
  def test_numeric_fields
866
1132
  m = NumericData.new(
867
1133
  :bank_balance => 1586.43,
@@ -881,13 +1147,14 @@ class BasicsTest < ActiveRecord::TestCase
881
1147
  assert_kind_of Integer, m1.world_population
882
1148
  else
883
1149
  assert_kind_of BigDecimal, m1.world_population
884
- end
1150
+ end
885
1151
  assert_equal 6000000000, m1.world_population
1152
+
886
1153
  unless current_adapter?(:IBM_DBAdapter)
887
1154
  assert_kind_of Fixnum, m1.my_house_population
888
1155
  else
889
1156
  assert_kind_of BigDecimal, m1.my_house_population
890
- end
1157
+ end
891
1158
  assert_equal 3, m1.my_house_population
892
1159
 
893
1160
  assert_kind_of BigDecimal, m1.bank_balance
@@ -966,7 +1233,6 @@ class BasicsTest < ActiveRecord::TestCase
966
1233
  end
967
1234
 
968
1235
  def test_nil_serialized_attribute_with_class_constraint
969
- myobj = MyObject.new('value1', 'value2')
970
1236
  topic = Topic.new
971
1237
  assert_nil topic.content
972
1238
  end
@@ -976,7 +1242,7 @@ class BasicsTest < ActiveRecord::TestCase
976
1242
  topic = Topic.new(:content => myobj)
977
1243
  assert topic.save
978
1244
  Topic.serialize(:content, Hash)
979
- assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
1245
+ assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).reload.content }
980
1246
  ensure
981
1247
  Topic.serialize(:content)
982
1248
  end
@@ -991,6 +1257,87 @@ class BasicsTest < ActiveRecord::TestCase
991
1257
  Topic.serialize(:content)
992
1258
  end
993
1259
 
1260
+ def test_serialized_default_class
1261
+ Topic.serialize(:content, Hash)
1262
+ topic = Topic.new
1263
+ assert_equal Hash, topic.content.class
1264
+ assert_equal Hash, topic.read_attribute(:content).class
1265
+ topic.content["beer"] = "MadridRb"
1266
+ assert topic.save
1267
+ topic.reload
1268
+ assert_equal Hash, topic.content.class
1269
+ assert_equal "MadridRb", topic.content["beer"]
1270
+ ensure
1271
+ Topic.serialize(:content)
1272
+ end
1273
+
1274
+ def test_serialized_no_default_class_for_object
1275
+ topic = Topic.new
1276
+ assert_nil topic.content
1277
+ end
1278
+
1279
+ def test_serialized_boolean_value_true
1280
+ Topic.serialize(:content)
1281
+ topic = Topic.new(:content => true)
1282
+ assert topic.save
1283
+ topic = topic.reload
1284
+ assert_equal topic.content, true
1285
+ end
1286
+
1287
+ def test_serialized_boolean_value_false
1288
+ Topic.serialize(:content)
1289
+ topic = Topic.new(:content => false)
1290
+ assert topic.save
1291
+ topic = topic.reload
1292
+ assert_equal topic.content, false
1293
+ end
1294
+
1295
+ def test_serialize_with_coder
1296
+ coder = Class.new {
1297
+ # Identity
1298
+ def load(thing)
1299
+ thing
1300
+ end
1301
+
1302
+ # base 64
1303
+ def dump(thing)
1304
+ [thing].pack('m')
1305
+ end
1306
+ }.new
1307
+
1308
+ Topic.serialize(:content, coder)
1309
+ s = 'hello world'
1310
+ topic = Topic.new(:content => s)
1311
+ assert topic.save
1312
+ topic = topic.reload
1313
+ assert_equal [s].pack('m'), topic.content
1314
+ ensure
1315
+ Topic.serialize(:content)
1316
+ end
1317
+
1318
+ def test_serialize_with_bcrypt_coder
1319
+ crypt_coder = Class.new {
1320
+ def load(thing)
1321
+ return unless thing
1322
+ BCrypt::Password.new thing
1323
+ end
1324
+
1325
+ def dump(thing)
1326
+ BCrypt::Password.create(thing).to_s
1327
+ end
1328
+ }.new
1329
+
1330
+ Topic.serialize(:content, crypt_coder)
1331
+ password = 'password'
1332
+ topic = Topic.new(:content => password)
1333
+ assert topic.save
1334
+ topic = topic.reload
1335
+ assert_kind_of BCrypt::Password, topic.content
1336
+ assert_equal(true, topic.content == password, 'password should equal')
1337
+ ensure
1338
+ Topic.serialize(:content)
1339
+ end
1340
+
994
1341
  def test_quote
995
1342
  author_name = "\\ \001 ' \n \\n \""
996
1343
  topic = Topic.create('author_name' => author_name)
@@ -1046,9 +1393,14 @@ class BasicsTest < ActiveRecord::TestCase
1046
1393
  end
1047
1394
 
1048
1395
  def test_define_attr_method_with_block
1049
- k = Class.new( ActiveRecord::Base )
1050
- k.send(:define_attr_method, :primary_key) { "sys_" + original_primary_key }
1051
- assert_equal "sys_id", k.primary_key
1396
+ k = Class.new( ActiveRecord::Base ) do
1397
+ class << self
1398
+ attr_accessor :foo_key
1399
+ end
1400
+ end
1401
+ k.foo_key = "id"
1402
+ k.send(:define_attr_method, :foo_key) { "sys_" + original_foo_key }
1403
+ assert_equal "sys_id", k.foo_key
1052
1404
  end
1053
1405
 
1054
1406
  def test_set_table_name_with_value
@@ -1059,6 +1411,16 @@ class BasicsTest < ActiveRecord::TestCase
1059
1411
  assert_equal "bar", k.table_name
1060
1412
  end
1061
1413
 
1414
+ def test_switching_between_table_name
1415
+ assert_difference("GoodJoke.count") do
1416
+ Joke.set_table_name "cold_jokes"
1417
+ Joke.create
1418
+
1419
+ Joke.set_table_name "funny_jokes"
1420
+ Joke.create
1421
+ end
1422
+ end
1423
+
1062
1424
  def test_quoted_table_name_after_set_table_name
1063
1425
  klass = Class.new(ActiveRecord::Base)
1064
1426
 
@@ -1087,6 +1449,7 @@ class BasicsTest < ActiveRecord::TestCase
1087
1449
 
1088
1450
  def test_set_primary_key_with_block
1089
1451
  k = Class.new( ActiveRecord::Base )
1452
+ k.primary_key = 'id'
1090
1453
  k.set_primary_key { "sys_" + original_primary_key }
1091
1454
  assert_equal "sys_id", k.primary_key
1092
1455
  end
@@ -1139,12 +1502,6 @@ class BasicsTest < ActiveRecord::TestCase
1139
1502
  assert_equal res6, res7
1140
1503
  end
1141
1504
 
1142
- def test_interpolate_sql
1143
- assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo@bar') }
1144
- assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar) baz') }
1145
- assert_nothing_raised { Category.new.send(:interpolate_sql, 'foo bar} baz') }
1146
- end
1147
-
1148
1505
  def test_scoped_find_conditions
1149
1506
  scoped_developers = Developer.send(:with_scope, :find => { :conditions => 'salary > 90000' }) do
1150
1507
  Developer.find(:all, :conditions => 'id < 5')
@@ -1153,6 +1510,12 @@ class BasicsTest < ActiveRecord::TestCase
1153
1510
  assert_equal 3, scoped_developers.size
1154
1511
  end
1155
1512
 
1513
+ def test_no_limit_offset
1514
+ assert_nothing_raised do
1515
+ Developer.find(:all, :offset => 2)
1516
+ end
1517
+ end
1518
+
1156
1519
  def test_scoped_find_limit_offset
1157
1520
  scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :offset => 2 }) do
1158
1521
  Developer.find(:all, :order => 'id')
@@ -1278,6 +1641,10 @@ class BasicsTest < ActiveRecord::TestCase
1278
1641
  assert !LooseDescendant.abstract_class?
1279
1642
  end
1280
1643
 
1644
+ def test_abstract_class_table_name
1645
+ assert_nil AbstractCompany.table_name
1646
+ end
1647
+
1281
1648
  def test_base_class
1282
1649
  assert_equal LoosePerson, LoosePerson.base_class
1283
1650
  assert_equal LooseDescendant, LooseDescendant.base_class
@@ -1350,7 +1717,7 @@ class BasicsTest < ActiveRecord::TestCase
1350
1717
 
1351
1718
  def test_inspect_instance
1352
1719
  topic = topics(:first)
1353
- assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil, group: nil>), topic.inspect
1720
+ assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil, group: nil, created_at: "#{topic.created_at.to_s(:db)}", updated_at: "#{topic.updated_at.to_s(:db)}">), topic.inspect
1354
1721
  end
1355
1722
 
1356
1723
  def test_inspect_new_instance
@@ -1432,10 +1799,6 @@ class BasicsTest < ActiveRecord::TestCase
1432
1799
  ActiveRecord::Base.logger = original_logger
1433
1800
  end
1434
1801
 
1435
- def test_dup
1436
- assert !Minimalistic.new.freeze.dup.frozen?
1437
- end
1438
-
1439
1802
  def test_compute_type_success
1440
1803
  assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author')
1441
1804
  end
@@ -1453,34 +1816,93 @@ class BasicsTest < ActiveRecord::TestCase
1453
1816
  end
1454
1817
  end
1455
1818
 
1456
- def test_default_scope_is_reset
1457
- Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
1458
- UnloadablePost.table_name = 'posts'
1459
- UnloadablePost.class_eval do
1460
- default_scope order('posts.comments_count ASC')
1819
+ def test_compute_type_argument_error
1820
+ ActiveSupport::Dependencies.stubs(:constantize).raises(ArgumentError)
1821
+ assert_raises ArgumentError do
1822
+ ActiveRecord::Base.send :compute_type, 'InvalidModel'
1461
1823
  end
1462
- UnloadablePost.scoped_methods # make Thread.current[:UnloadablePost_scoped_methods] not nil
1824
+ end
1825
+
1826
+ def test_clear_cache!
1827
+ # preheat cache
1828
+ c1 = Post.columns
1829
+ ActiveRecord::Base.clear_cache!
1830
+ c2 = Post.columns
1831
+ assert_not_equal c1, c2
1832
+ end
1833
+
1834
+ def test_current_scope_is_reset
1835
+ Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
1836
+ UnloadablePost.send(:current_scope=, UnloadablePost.scoped)
1463
1837
 
1464
1838
  UnloadablePost.unloadable
1465
- assert_not_nil Thread.current[:UnloadablePost_scoped_methods]
1839
+ assert_not_nil Thread.current[:UnloadablePost_current_scope]
1466
1840
  ActiveSupport::Dependencies.remove_unloadable_constants!
1467
- assert_nil Thread.current[:UnloadablePost_scoped_methods]
1841
+ assert_nil Thread.current[:UnloadablePost_current_scope]
1468
1842
  ensure
1469
1843
  Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
1470
1844
  end
1471
1845
 
1472
- protected
1473
- def with_env_tz(new_tz = 'US/Eastern')
1474
- old_tz, ENV['TZ'] = ENV['TZ'], new_tz
1475
- yield
1476
- ensure
1477
- old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
1478
- end
1846
+ def test_marshal_round_trip
1847
+ expected = posts(:welcome)
1848
+ marshalled = Marshal.dump(expected)
1849
+ actual = Marshal.load(marshalled)
1479
1850
 
1480
- def with_active_record_default_timezone(zone)
1481
- old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
1482
- yield
1483
- ensure
1484
- ActiveRecord::Base.default_timezone = old_zone
1485
- end
1851
+ assert_equal expected.attributes, actual.attributes
1852
+ end
1853
+
1854
+ def test_marshal_new_record_round_trip
1855
+ marshalled = Marshal.dump(Post.new)
1856
+ post = Marshal.load(marshalled)
1857
+
1858
+ assert post.new_record?, "should be a new record"
1859
+ end
1860
+
1861
+ def test_marshalling_with_associations
1862
+ post = Post.new
1863
+ post.comments.build
1864
+
1865
+ marshalled = Marshal.dump(post)
1866
+ post = Marshal.load(marshalled)
1867
+
1868
+ assert_equal 1, post.comments.length
1869
+ end
1870
+
1871
+ def test_attribute_names
1872
+ assert_equal ["id", "type", "ruby_type", "firm_id", "firm_name", "name", "client_of", "rating", "account_id"],
1873
+ Company.attribute_names
1874
+ end
1875
+
1876
+ def test_attribute_names_on_table_not_exists
1877
+ assert_equal [], NonExistentTable.attribute_names
1878
+ end
1879
+
1880
+ def test_attribtue_names_on_abstract_class
1881
+ assert_equal [], AbstractCompany.attribute_names
1882
+ end
1883
+
1884
+ def test_cache_key_for_existing_record_is_not_timezone_dependent
1885
+ ActiveRecord::Base.time_zone_aware_attributes = true
1886
+
1887
+ Time.zone = "UTC"
1888
+ utc_key = Developer.first.cache_key
1889
+
1890
+ Time.zone = "EST"
1891
+ est_key = Developer.first.cache_key
1892
+
1893
+ assert_equal utc_key, est_key
1894
+ ensure
1895
+ ActiveRecord::Base.time_zone_aware_attributes = false
1896
+ end
1897
+
1898
+ def test_cache_key_format_for_existing_record_with_updated_at
1899
+ dev = Developer.first
1900
+ assert_equal "developers/#{dev.id}-#{dev.updated_at.utc.to_s(:number)}", dev.cache_key
1901
+ end
1902
+
1903
+ def test_cache_key_format_for_existing_record_with_nil_updated_at
1904
+ dev = Developer.first
1905
+ dev.update_attribute(:updated_at, nil)
1906
+ assert_match /\/#{dev.id}$/, dev.cache_key
1907
+ end
1486
1908
  end