ibm_db 2.5.6 → 2.5.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) 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/test/cases/adapter_test.rb +25 -22
  9. data/test/cases/associations/belongs_to_associations_test.rb +245 -43
  10. data/test/cases/associations/cascaded_eager_loading_test.rb +28 -26
  11. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +60 -156
  12. data/test/cases/associations/join_model_test.rb +96 -146
  13. data/test/cases/attribute_methods_test.rb +98 -33
  14. data/test/cases/base_test.rb +525 -103
  15. data/test/cases/calculations_test.rb +92 -8
  16. data/test/cases/migration_test.rb +533 -207
  17. data/test/cases/persistence_test.rb +636 -0
  18. data/test/cases/query_cache_test.rb +242 -0
  19. data/test/cases/relations_test.rb +1019 -0
  20. data/test/cases/schema_dumper_test.rb +37 -17
  21. data/test/cases/transaction_callbacks_test.rb +300 -0
  22. data/test/cases/validations/uniqueness_validation_test.rb +38 -22
  23. data/test/cases/xml_serialization_test.rb +276 -0
  24. data/test/config.yml +154 -0
  25. data/test/connections/native_ibm_db/connection.rb +2 -0
  26. data/test/models/warehouse_thing.rb +4 -4
  27. data/test/schema/i5/ibm_db_specific_schema.rb +3 -1
  28. data/test/schema/ids/ibm_db_specific_schema.rb +3 -1
  29. data/test/schema/luw/ibm_db_specific_schema.rb +2 -0
  30. data/test/schema/schema.rb +174 -89
  31. data/test/schema/zOS/ibm_db_specific_schema.rb +3 -1
  32. metadata +14 -8
  33. data/test/cases/associations/eager_test.rb +0 -862
  34. data/test/cases/associations/has_many_through_associations_test.rb +0 -461
  35. data/test/cases/finder_test.rb +0 -1088
  36. 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