db2 2.6.2 → 2.7.0

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 (41) hide show
  1. data/CHANGES +17 -0
  2. data/README +79 -141
  3. data/ext/Makefile.nt32 +3 -3
  4. data/ext/Makefile.nt32.191 +212 -0
  5. data/ext/extconf.rb +75 -14
  6. data/ext/ibm_db.c +504 -47
  7. data/ext/ruby_ibm_db.h +4 -1
  8. data/ext/ruby_ibm_db_cli.c +108 -1
  9. data/ext/ruby_ibm_db_cli.h +54 -1
  10. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +423 -124
  11. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1 -1
  12. data/test/cases/adapter_test.rb +169 -164
  13. data/test/cases/associations/belongs_to_associations_test.rb +268 -43
  14. data/test/cases/associations/cascaded_eager_loading_test.rb +31 -33
  15. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +90 -156
  16. data/test/cases/associations/join_model_test.rb +100 -150
  17. data/test/cases/attribute_methods_test.rb +259 -58
  18. data/test/cases/base_test.rb +785 -138
  19. data/test/cases/calculations_test.rb +128 -8
  20. data/test/cases/migration_test.rb +680 -286
  21. data/test/cases/persistence_test.rb +642 -0
  22. data/test/cases/query_cache_test.rb +257 -0
  23. data/test/cases/relations_test.rb +1182 -0
  24. data/test/cases/schema_dumper_test.rb +41 -17
  25. data/test/cases/transaction_callbacks_test.rb +300 -0
  26. data/test/cases/validations/uniqueness_validation_test.rb +38 -22
  27. data/test/cases/xml_serialization_test.rb +408 -0
  28. data/test/config.yml +154 -0
  29. data/test/connections/native_ibm_db/connection.rb +2 -0
  30. data/test/models/warehouse_thing.rb +4 -4
  31. data/test/schema/i5/ibm_db_specific_schema.rb +3 -1
  32. data/test/schema/ids/ibm_db_specific_schema.rb +3 -1
  33. data/test/schema/luw/ibm_db_specific_schema.rb +2 -0
  34. data/test/schema/schema.rb +196 -92
  35. data/test/schema/zOS/ibm_db_specific_schema.rb +3 -1
  36. metadata +73 -68
  37. data/.gitignore +0 -1
  38. data/test/cases/associations/eager_test.rb +0 -862
  39. data/test/cases/associations/has_many_through_associations_test.rb +0 -461
  40. data/test/cases/finder_test.rb +0 -1088
  41. data/test/cases/fixtures_test.rb +0 -684
@@ -1,7 +1,9 @@
1
1
  require "cases/helper"
2
+ require 'active_support/core_ext/object/inclusion'
2
3
  require 'models/tag'
3
4
  require 'models/tagging'
4
5
  require 'models/post'
6
+ require 'models/rating'
5
7
  require 'models/item'
6
8
  require 'models/comment'
7
9
  require 'models/author'
@@ -11,9 +13,13 @@ require 'models/vertex'
11
13
  require 'models/edge'
12
14
  require 'models/book'
13
15
  require 'models/citation'
16
+ require 'models/aircraft'
17
+ require 'models/engine'
18
+ require 'models/car'
14
19
 
15
20
  class AssociationsJoinModelTest < ActiveRecord::TestCase
16
- self.use_transactional_fixtures = false
21
+ self.use_transactional_fixtures = false unless supports_savepoints?
22
+
17
23
  fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :vertices, :items, :books,
18
24
  # Reload edges table from fixtures as otherwise repeated test was failing
19
25
  :edges
@@ -43,43 +49,14 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
43
49
  assert_queries(1) { assert_equal 0, author.unique_categorized_posts.count(:title, :conditions => "title is NULL") }
44
50
  assert !authors(:mary).unique_categorized_posts.loaded?
45
51
  end
46
-
47
- unless current_adapter?(:IBM_DBAdapter)
48
- def test_has_many_uniq_through_find
49
- assert_equal 1, authors(:mary).unique_categorized_posts.find(:all).size
50
- end
51
-
52
- def test_has_many_uniq_through_dynamic_find
53
- assert_equal 1, authors(:mary).unique_categorized_posts.find_all_by_title("So I was thinking").size
54
- end
55
- # LUW: [IBM][CLI Driver][DB2/LINUX] SQL0214N
56
- # An expression in the ORDER BY clause in the following position,
57
- # or starting with "DEVELOPERS" in the "ORDER BY" clause is not valid.
58
- # Reason code = "2". SQLSTATE=42822 SQLCODE=-214:
59
- # SELECT DISTINCT projects.id FROM projects
60
- # LEFT OUTER JOIN developers_projects ON developers_projects.project_id = projects.id
61
- # LEFT OUTER JOIN developers ON developers.id = developers_projects.developer_id
62
- # ORDER BY developers.created_at
63
- #
64
- # i5: [IBM][CLI Driver][AS] SQL0214N
65
- # An expression in the ORDER BY clause in the following position,
66
- # or starting with "1" in the "CREATED_AT" clause is not valid.
67
- # Reason code = "2". SQLSTATE=42822 SQLCODE=-214:
68
- # SELECT DISTINCT projects.id FROM projects
69
- # LEFT OUTER JOIN developers_projects ON developers_projects.project_id = projects.id
70
- # LEFT OUTER JOIN developers ON developers.id = developers_projects.developer_id
71
- # ORDER BY developers.created_at
72
- #
73
- # zOS 9:[IBM][CLI Driver][DB2] SQL0214N
74
- # An expression in the ORDER BY clause in the following position,
75
- # or starting with "CREATED_AT" in the "ORDER BY" clause is not valid.
76
- # Reason code = "2". SQLSTATE=42822 SQLCODE=-214:
77
- # SELECT DISTINCT projects.id FROM projects
78
- # LEFT OUTER JOIN developers_projects ON developers_projects.project_id = projects.id
79
- # LEFT OUTER JOIN developers ON developers.id = developers_projects.developer_id
80
- # ORDER BY developers.created_at
81
- #
82
- end
52
+
53
+ def test_has_many_uniq_through_find
54
+ assert_equal 1, authors(:mary).unique_categorized_posts.find(:all).size
55
+ end
56
+
57
+ def test_has_many_uniq_through_dynamic_find
58
+ assert_equal 1, authors(:mary).unique_categorized_posts.find_all_by_title("So I was thinking").size
59
+ end
83
60
 
84
61
  def test_polymorphic_has_many_going_through_join_model
85
62
  assert_equal tags(:general), tag = posts(:welcome).tags.first
@@ -114,16 +91,9 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
114
91
  end
115
92
  end
116
93
 
117
- def test_polymorphic_has_many_going_through_join_model_with_disabled_include
118
- assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
119
- assert_queries 1 do
120
- tag.tagging
121
- end
122
- end
123
-
124
94
  def test_polymorphic_has_many_going_through_join_model_with_custom_select_and_joins
125
95
  assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
126
- tag.author_id
96
+ assert_nothing_raised(NoMethodError) { tag.author_id }
127
97
  end
128
98
 
129
99
  def test_polymorphic_has_many_going_through_join_model_with_custom_foreign_key
@@ -169,7 +139,21 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
169
139
  def test_set_polymorphic_has_one
170
140
  tagging = tags(:misc).taggings.create
171
141
  posts(:thinking).tagging = tagging
172
- assert_equal "Post", tagging.taggable_type
142
+
143
+ assert_equal "Post", tagging.taggable_type
144
+ assert_equal posts(:thinking).id, tagging.taggable_id
145
+ assert_equal posts(:thinking), tagging.taggable
146
+ end
147
+
148
+ def test_set_polymorphic_has_one_on_new_record
149
+ tagging = tags(:misc).taggings.create
150
+ post = Post.new :title => "foo", :body => "bar"
151
+ post.tagging = tagging
152
+ post.save!
153
+
154
+ assert_equal "Post", tagging.taggable_type
155
+ assert_equal post.id, tagging.taggable_id
156
+ assert_equal post, tagging.taggable
173
157
  end
174
158
 
175
159
  def test_create_polymorphic_has_many_with_scope
@@ -188,14 +172,14 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
188
172
 
189
173
  def test_create_polymorphic_has_one_with_scope
190
174
  old_count = Tagging.count
191
- tagging = posts(:welcome).tagging.create(:tag => tags(:misc))
175
+ tagging = posts(:welcome).create_tagging(:tag => tags(:misc))
192
176
  assert_equal "Post", tagging.taggable_type
193
177
  assert_equal old_count+1, Tagging.count
194
178
  end
195
179
 
196
180
  def test_delete_polymorphic_has_many_with_delete_all
197
181
  assert_equal 1, posts(:welcome).taggings.count
198
- posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDeleteAll'
182
+ posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyDeleteAll'
199
183
  post = find_post_with_dependency(1, :has_many, :taggings, :delete_all)
200
184
 
201
185
  old_count = Tagging.count
@@ -206,7 +190,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
206
190
 
207
191
  def test_delete_polymorphic_has_many_with_destroy
208
192
  assert_equal 1, posts(:welcome).taggings.count
209
- posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDestroy'
193
+ posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyDestroy'
210
194
  post = find_post_with_dependency(1, :has_many, :taggings, :destroy)
211
195
 
212
196
  old_count = Tagging.count
@@ -217,7 +201,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
217
201
 
218
202
  def test_delete_polymorphic_has_many_with_nullify
219
203
  assert_equal 1, posts(:welcome).taggings.count
220
- posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyNullify'
204
+ posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyNullify'
221
205
  post = find_post_with_dependency(1, :has_many, :taggings, :nullify)
222
206
 
223
207
  old_count = Tagging.count
@@ -228,7 +212,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
228
212
 
229
213
  def test_delete_polymorphic_has_one_with_destroy
230
214
  assert posts(:welcome).tagging
231
- posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneDestroy'
215
+ posts(:welcome).tagging.update_column :taggable_type, 'PostWithHasOneDestroy'
232
216
  post = find_post_with_dependency(1, :has_one, :tagging, :destroy)
233
217
 
234
218
  old_count = Tagging.count
@@ -239,7 +223,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
239
223
 
240
224
  def test_delete_polymorphic_has_one_with_nullify
241
225
  assert posts(:welcome).tagging
242
- posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneNullify'
226
+ posts(:welcome).tagging.update_column :taggable_type, 'PostWithHasOneNullify'
243
227
  post = find_post_with_dependency(1, :has_one, :tagging, :nullify)
244
228
 
245
229
  old_count = Tagging.count
@@ -249,7 +233,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
249
233
  end
250
234
 
251
235
  def test_has_many_with_piggyback
252
- assert_equal "2", categories(:sti_test).authors.first.post_id.to_s
236
+ assert_equal "2", categories(:sti_test).authors_with_select.first.post_id.to_s
253
237
  end
254
238
 
255
239
  def test_include_has_many_through
@@ -323,10 +307,26 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
323
307
  end
324
308
 
325
309
  def test_has_many_going_through_join_model_with_custom_foreign_key
326
- assert_equal [], posts(:thinking).authors
310
+ assert_equal [authors(:bob)], posts(:thinking).authors
327
311
  assert_equal [authors(:mary)], posts(:authorless).authors
328
312
  end
329
313
 
314
+ def test_has_many_going_through_join_model_with_custom_primary_key
315
+ assert_equal [authors(:david)], posts(:thinking).authors_using_author_id
316
+ end
317
+
318
+ def test_has_many_going_through_polymorphic_join_model_with_custom_primary_key
319
+ assert_equal [tags(:general)], posts(:eager_other).tags_using_author_id
320
+ end
321
+
322
+ def test_has_many_through_with_custom_primary_key_on_belongs_to_source
323
+ assert_equal [authors(:david), authors(:david)], posts(:thinking).author_using_custom_pk
324
+ end
325
+
326
+ def test_has_many_through_with_custom_primary_key_on_has_many_source
327
+ assert_equal [authors(:david), authors(:bob)], posts(:thinking).authors_using_custom_pk.order('authors.id')
328
+ end
329
+
330
330
  def test_both_scoped_and_explicit_joins_should_be_respected
331
331
  assert_nothing_raised do
332
332
  Post.send(:with_scope, :find => {:joins => "left outer join comments on comments.id = posts.id"}) do
@@ -353,11 +353,16 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
353
353
  end
354
354
 
355
355
  def test_has_many_polymorphic
356
- assert_raise ActiveRecord::HasManyThroughAssociationPolymorphicError do
357
- assert_equal posts(:welcome, :thinking), tags(:general).taggables
356
+ assert_raise ActiveRecord::HasManyThroughAssociationPolymorphicSourceError do
357
+ tags(:general).taggables
358
+ end
359
+
360
+ assert_raise ActiveRecord::HasManyThroughAssociationPolymorphicThroughError do
361
+ taggings(:welcome_general).things
358
362
  end
363
+
359
364
  assert_raise ActiveRecord::EagerLoadPolymorphicError do
360
- assert_equal posts(:welcome, :thinking), tags(:general).taggings.find(:all, :include => :taggable, :conditions => 'bogus_table.column = 1')
365
+ tags(:general).taggings.find(:all, :include => :taggable, :conditions => 'bogus_table.column = 1')
361
366
  end
362
367
  end
363
368
 
@@ -398,7 +403,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
398
403
  end
399
404
 
400
405
  def test_has_many_through_polymorphic_has_one
401
- assert_equal Tagging.find(1,2).sort_by { |t| t.id }, authors(:david).tagging
406
+ assert_equal Tagging.find(1,2).sort_by { |t| t.id }, authors(:david).tagging.order(:id)
402
407
  end
403
408
 
404
409
  def test_has_many_through_polymorphic_has_many
@@ -413,50 +418,16 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
413
418
  end
414
419
  end
415
420
 
416
- def test_has_many_through_has_many_through
417
- assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tags }
418
- end
419
-
420
- def test_has_many_through_habtm
421
- assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).post_categories }
422
- end
423
-
424
- unless current_adapter?(:IBM_DBAdapter)
421
+ unless current_adapter?(:IBM_DBAdapter)
422
+ # DB2 throws SQL0214N
425
423
  def test_eager_load_has_many_through_has_many
426
424
  author = Author.find :first, :conditions => ['name = ?', 'David'], :include => :comments, :order => 'comments.id'
427
425
  SpecialComment.new; VerySpecialComment.new
428
426
  assert_no_queries do
429
- assert_equal [1,2,3,5,6,7,8,9,10], author.comments.collect(&:id)
427
+ assert_equal [1,2,3,5,6,7,8,9,10,12], author.comments.collect(&:id)
430
428
  end
431
429
  end
432
- # LUW: [IBM][CLI Driver][DB2/LINUX] SQL0214N
433
- # An expression in the ORDER BY clause in the following position,
434
- # or starting with "DEVELOPERS" in the "ORDER BY" clause is not valid.
435
- # Reason code = "2". SQLSTATE=42822 SQLCODE=-214:
436
- # SELECT DISTINCT projects.id FROM projects
437
- # LEFT OUTER JOIN developers_projects ON developers_projects.project_id = projects.id
438
- # LEFT OUTER JOIN developers ON developers.id = developers_projects.developer_id
439
- # ORDER BY developers.created_at
440
- #
441
- # i5: [IBM][CLI Driver][AS] SQL0214N
442
- # An expression in the ORDER BY clause in the following position,
443
- # or starting with "1" in the "CREATED_AT" clause is not valid.
444
- # Reason code = "2". SQLSTATE=42822 SQLCODE=-214:
445
- # SELECT DISTINCT projects.id FROM projects
446
- # LEFT OUTER JOIN developers_projects ON developers_projects.project_id = projects.id
447
- # LEFT OUTER JOIN developers ON developers.id = developers_projects.developer_id
448
- # ORDER BY developers.created_at
449
- #
450
- # zOS 9:[IBM][CLI Driver][DB2] SQL0214N
451
- # An expression in the ORDER BY clause in the following position,
452
- # or starting with "CREATED_AT" in the "ORDER BY" clause is not valid.
453
- # Reason code = "2". SQLSTATE=42822 SQLCODE=-214:
454
- # SELECT DISTINCT projects.id FROM projects
455
- # LEFT OUTER JOIN developers_projects ON developers_projects.project_id = projects.id
456
- # LEFT OUTER JOIN developers ON developers.id = developers_projects.developer_id
457
- # ORDER BY developers.created_at
458
- #
459
- end
430
+ end
460
431
 
461
432
  def test_eager_load_has_many_through_has_many_with_conditions
462
433
  post = Post.find(:first, :include => :invalid_tags)
@@ -484,7 +455,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
484
455
  end
485
456
 
486
457
  def test_has_many_through_uses_conditions_specified_on_the_has_many_association
487
- author = Author.find(:first)
458
+ author = Author.order(:id).first
488
459
  assert_present author.comments
489
460
  assert_blank author.nonexistant_comments
490
461
  end
@@ -503,7 +474,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
503
474
  assert saved_post.tags.include?(new_tag)
504
475
 
505
476
  assert new_tag.persisted?
506
- assert saved_post.reload.tags(true).include?(new_tag)
477
+ assert new_tag.in?(saved_post.reload.tags(true))
507
478
 
508
479
 
509
480
  new_post = Post.new(:title => "Association replacmenet works!", :body => "You best believe it.")
@@ -516,7 +487,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
516
487
 
517
488
  new_post.save!
518
489
  assert new_post.persisted?
519
- assert new_post.reload.tags(true).include?(saved_tag)
490
+ assert saved_tag.in?(new_post.reload.tags(true))
520
491
 
521
492
  assert !posts(:thinking).tags.build.persisted?
522
493
  assert !posts(:thinking).tags.new.persisted?
@@ -554,17 +525,21 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
554
525
  assert_nothing_raised { vertices(:vertex_1).sinks << vertices(:vertex_5) }
555
526
  end
556
527
 
528
+ def test_add_to_join_table_with_no_id
529
+ assert_nothing_raised { vertices(:vertex_1).sinks << vertices(:vertex_5) }
530
+ end
531
+
557
532
  def test_has_many_through_collection_size_doesnt_load_target_if_not_loaded
558
533
  author = authors(:david)
559
- assert_equal 9, author.comments.size
534
+ assert_equal 10, author.comments.size
560
535
  assert !author.comments.loaded?
561
536
  end
562
537
 
563
538
  def test_has_many_through_collection_size_uses_counter_cache_if_it_exists
564
- author = authors(:david)
565
- author.stubs(:read_attribute).with('comments_count').returns(100)
566
- assert_equal 100, author.comments.size
567
- assert !author.comments.loaded?
539
+ c = categories(:general)
540
+ c.categorizations_count = 100
541
+ assert_equal 100, c.categorizations.size
542
+ assert !c.categorizations.loaded?
568
543
  end
569
544
 
570
545
  def test_adding_junk_to_has_many_through_should_raise_type_mismatch
@@ -645,37 +620,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
645
620
  def test_uniq_has_many_through_should_retain_order
646
621
  comment_ids = authors(:david).comments.map(&:id)
647
622
  assert_equal comment_ids.sort, authors(:david).ordered_uniq_comments.map(&:id)
648
-
649
- unless current_adapter?(:IBM_DBAdapter)
650
- assert_equal comment_ids.sort.reverse, authors(:david).ordered_uniq_comments_desc.map(&:id)
651
- # LUW: [IBM][CLI Driver][DB2/LINUX] SQL0214N
652
- # An expression in the ORDER BY clause in the following position,
653
- # or starting with "DEVELOPERS" in the "ORDER BY" clause is not valid.
654
- # Reason code = "2". SQLSTATE=42822 SQLCODE=-214:
655
- # SELECT DISTINCT projects.id FROM projects
656
- # LEFT OUTER JOIN developers_projects ON developers_projects.project_id = projects.id
657
- # LEFT OUTER JOIN developers ON developers.id = developers_projects.developer_id
658
- # ORDER BY developers.created_at
659
- #
660
- # i5: [IBM][CLI Driver][AS] SQL0214N
661
- # An expression in the ORDER BY clause in the following position,
662
- # or starting with "1" in the "CREATED_AT" clause is not valid.
663
- # Reason code = "2". SQLSTATE=42822 SQLCODE=-214:
664
- # SELECT DISTINCT projects.id FROM projects
665
- # LEFT OUTER JOIN developers_projects ON developers_projects.project_id = projects.id
666
- # LEFT OUTER JOIN developers ON developers.id = developers_projects.developer_id
667
- # ORDER BY developers.created_at
668
- #
669
- # zOS 9:[IBM][CLI Driver][DB2] SQL0214N
670
- # An expression in the ORDER BY clause in the following position,
671
- # or starting with "CREATED_AT" in the "ORDER BY" clause is not valid.
672
- # Reason code = "2". SQLSTATE=42822 SQLCODE=-214:
673
- # SELECT DISTINCT projects.id FROM projects
674
- # LEFT OUTER JOIN developers_projects ON developers_projects.project_id = projects.id
675
- # LEFT OUTER JOIN developers ON developers.id = developers_projects.developer_id
676
- # ORDER BY developers.created_at
677
- #
678
- end
623
+ assert_equal comment_ids.sort.reverse, authors(:david).ordered_uniq_comments_desc.map(&:id)
679
624
  end
680
625
 
681
626
  def test_polymorphic_has_many
@@ -707,7 +652,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
707
652
  end
708
653
 
709
654
  def test_preload_polymorph_many_types
710
- taggings = Tagging.find :all, :include => :taggable, :conditions => ['taggable_type != ?', 'FakeModel']
655
+ taggings = Tagging.find :all, :include => :taggable, :conditions => ['taggable_type != ?', 'FakeModel'], :order => 'id'
711
656
  assert_no_queries do
712
657
  taggings.first.taggable.id
713
658
  taggings[1].taggable.id
@@ -720,7 +665,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
720
665
 
721
666
  def test_preload_nil_polymorphic_belongs_to
722
667
  assert_nothing_raised do
723
- taggings = Tagging.find(:all, :include => :taggable, :conditions => ['taggable_type IS NULL'])
668
+ Tagging.find(:all, :include => :taggable, :conditions => ['taggable_type IS NULL'])
724
669
  end
725
670
  end
726
671
 
@@ -763,16 +708,15 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
763
708
  end
764
709
  assert ! david.categories.loaded?
765
710
  end
766
-
767
- unless current_adapter?(:IBM_DBAdapter) #works fine stand alone, however when run as part of whole suite it breaks unique constraint
768
- def test_has_many_through_include_returns_false_for_non_matching_record_to_verify_scoping
769
- david = authors(:david)
770
- category = Category.create!(:name => 'Not Associated')
771
-
772
- assert ! david.categories.loaded?
773
- assert ! david.categories.include?(category)
774
- end
775
- end
711
+
712
+ def test_has_many_through_include_returns_false_for_non_matching_record_to_verify_scoping
713
+ david = authors(:david)
714
+ category = Category.create!(:name => 'Not Associated')
715
+
716
+ assert ! david.categories.loaded?
717
+ assert ! david.categories.include?(category)
718
+ end
719
+
776
720
  def test_has_many_through_goes_through_all_sti_classes
777
721
  sub_sti_post = SubStiPost.create!(:title => 'test', :body => 'test', :author_id => 1)
778
722
  new_comment = sub_sti_post.comments.create(:body => 'test')
@@ -780,13 +724,19 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
780
724
  assert_equal [9, 10, new_comment.id], authors(:david).sti_post_comments.map(&:id).sort
781
725
  end
782
726
 
727
+ def test_has_many_with_pluralize_table_names_false
728
+ aircraft = Aircraft.create!(:name => "Airbus 380")
729
+ engine = Engine.create!(:car_id => aircraft.id)
730
+ assert_equal aircraft.engines, [engine]
731
+ end
732
+
783
733
  private
784
734
  # create dynamic Post models to allow different dependency options
785
735
  def find_post_with_dependency(post_id, association, association_name, dependency)
786
736
  class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
787
- Post.find(post_id).update_attribute :type, class_name
737
+ Post.find(post_id).update_column :type, class_name
788
738
  klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
789
- klass.set_table_name 'posts'
739
+ klass.table_name = 'posts'
790
740
  klass.send(association, association_name, :as => :taggable, :dependent => dependency)
791
741
  klass.find(post_id)
792
742
  end
@@ -1,4 +1,5 @@
1
1
  require "cases/helper"
2
+ require 'active_support/core_ext/object/inclusion'
2
3
  require 'models/minimalistic'
3
4
  require 'models/developer'
4
5
  require 'models/auto_id'
@@ -8,6 +9,8 @@ require 'models/topic'
8
9
  require 'models/company'
9
10
  require 'models/category'
10
11
  require 'models/reply'
12
+ require 'models/contact'
13
+ require 'models/keyboard'
11
14
 
12
15
  class AttributeMethodsTest < ActiveRecord::TestCase
13
16
  fixtures :topics, :developers, :companies, :computers
@@ -27,9 +30,36 @@ class AttributeMethodsTest < ActiveRecord::TestCase
27
30
  t = Topic.new
28
31
  t.title = "hello there!"
29
32
  t.written_on = Time.now
33
+ t.author_name = ""
30
34
  assert t.attribute_present?("title")
31
35
  assert t.attribute_present?("written_on")
32
36
  assert !t.attribute_present?("content")
37
+ assert !t.attribute_present?("author_name")
38
+
39
+ end
40
+
41
+ def test_attribute_present_with_booleans
42
+ b1 = Boolean.new
43
+ b1.value = false
44
+ assert b1.attribute_present?(:value)
45
+
46
+ b2 = Boolean.new
47
+ b2.value = true
48
+ assert b2.attribute_present?(:value)
49
+
50
+ b3 = Boolean.new
51
+ assert !b3.attribute_present?(:value)
52
+
53
+ b4 = Boolean.new
54
+ b4.value = false
55
+ b4.save!
56
+ assert Boolean.find(b4.id).attribute_present?(:value)
57
+ end
58
+
59
+ def test_caching_nil_primary_key
60
+ klass = Class.new(Minimalistic)
61
+ klass.expects(:reset_primary_key).returns(nil).once
62
+ 2.times { klass.primary_key }
33
63
  end
34
64
 
35
65
  def test_attribute_keys_on_new_instance
@@ -75,6 +105,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
75
105
  def test_respond_to?
76
106
  topic = Topic.find(1)
77
107
  assert_respond_to topic, "title"
108
+ assert_respond_to topic, "_title"
78
109
  assert_respond_to topic, "title?"
79
110
  assert_respond_to topic, "title="
80
111
  assert_respond_to topic, :title
@@ -86,6 +117,39 @@ class AttributeMethodsTest < ActiveRecord::TestCase
86
117
  assert !topic.respond_to?(:nothingness)
87
118
  end
88
119
 
120
+ def test_deprecated_underscore_method
121
+ topic = Topic.find(1)
122
+ assert_equal topic.title, assert_deprecated { topic._title }
123
+ end
124
+
125
+ def test_respond_to_with_custom_primary_key
126
+ keyboard = Keyboard.create
127
+ assert_not_nil keyboard.key_number
128
+ assert_equal keyboard.key_number, keyboard.id
129
+ assert keyboard.respond_to?('key_number')
130
+ assert keyboard.respond_to?('_key_number')
131
+ assert keyboard.respond_to?('id')
132
+ assert keyboard.respond_to?('_id')
133
+ end
134
+
135
+ # Syck calls respond_to? before actually calling initialize
136
+ def test_respond_to_with_allocated_object
137
+ topic = Topic.allocate
138
+ assert !topic.respond_to?("nothingness")
139
+ assert !topic.respond_to?(:nothingness)
140
+ assert_respond_to topic, "title"
141
+ assert_respond_to topic, :title
142
+ end
143
+
144
+ # IRB inspects the return value of "MyModel.allocate"
145
+ # by inspecting it.
146
+ def test_allocated_object_can_be_inspected
147
+ topic = Topic.allocate
148
+ topic.instance_eval { @attributes = nil }
149
+ assert_nothing_raised { topic.inspect }
150
+ assert topic.inspect, "#<Topic not initialized>"
151
+ end
152
+
89
153
  def test_array_content
90
154
  topic = Topic.new
91
155
  topic.content = %w( one two three )
@@ -96,34 +160,55 @@ class AttributeMethodsTest < ActiveRecord::TestCase
96
160
 
97
161
  def test_read_attributes_before_type_cast
98
162
  category = Category.new({:name=>"Test categoty", :type => nil})
99
- category_attrs = {"name"=>"Test categoty", "type" => nil, "categorizations_count" => nil}
163
+ category_attrs = {"name"=>"Test categoty", "id" => nil, "type" => nil, "categorizations_count" => nil}
100
164
  assert_equal category_attrs , category.attributes_before_type_cast
101
165
  end
102
166
 
103
167
  if current_adapter?(:MysqlAdapter)
104
168
  def test_read_attributes_before_type_cast_on_boolean
105
169
  bool = Boolean.create({ "value" => false })
106
- assert_equal "0", bool.reload.attributes_before_type_cast["value"]
170
+ if RUBY_PLATFORM =~ /java/
171
+ # JRuby will return the value before typecast as string
172
+ assert_equal "0", bool.reload.attributes_before_type_cast["value"]
173
+ else
174
+ assert_equal 0, bool.reload.attributes_before_type_cast["value"]
175
+ end
107
176
  end
108
177
  end
109
178
 
110
- unless current_adapter?(:Mysql2Adapter)
111
- def test_read_attributes_before_type_cast_on_datetime
112
- developer = Developer.find(:first)
113
- # Oracle adapter returns Time before type cast
114
- unless current_adapter?(:OracleAdapter)
115
- assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"]
116
- else
117
- assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"].to_s(:db)
179
+ def test_read_attributes_before_type_cast_on_datetime
180
+ in_time_zone "Pacific Time (US & Canada)" do
181
+ record = @target.new
118
182
 
119
- developer.created_at = "345643456"
120
- assert_equal developer.created_at_before_type_cast, "345643456"
121
- assert_equal developer.created_at, nil
183
+ record.written_on = "345643456"
184
+ assert_equal "345643456", record.written_on_before_type_cast
185
+ assert_equal nil, record.written_on
122
186
 
123
- developer.created_at = "2010-03-21 21:23:32"
124
- assert_equal developer.created_at_before_type_cast, "2010-03-21 21:23:32"
125
- assert_equal developer.created_at, Time.parse("2010-03-21 21:23:32")
126
- end
187
+ record.written_on = "2009-10-11 12:13:14"
188
+ assert_equal "2009-10-11 12:13:14", record.written_on_before_type_cast
189
+ assert_equal Time.zone.parse("2009-10-11 12:13:14"), record.written_on
190
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
191
+ end
192
+ end
193
+
194
+ def test_read_attributes_after_type_cast_on_datetime
195
+ tz = "Pacific Time (US & Canada)"
196
+
197
+ in_time_zone tz do
198
+ record = @target.new
199
+
200
+ date_string = "2011-03-24"
201
+ time = Time.zone.parse date_string
202
+
203
+ record.written_on = date_string
204
+ assert_equal date_string, record.written_on_before_type_cast
205
+ assert_equal time, record.written_on
206
+ assert_equal ActiveSupport::TimeZone[tz], record.written_on.time_zone
207
+
208
+ record.save
209
+ record.reload
210
+
211
+ assert_equal time, record.written_on
127
212
  end
128
213
  end
129
214
 
@@ -157,7 +242,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
157
242
 
158
243
  def test_case_sensitive_attributes_hash
159
244
  # DB2 is not case-sensitive
160
- return true if current_adapter?(:DB2Adapter)
245
+ return true if current_adapter?(:DB2Adapter,:IBM_DBAdapter)
161
246
 
162
247
  assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.find(:first).attributes
163
248
  end
@@ -184,8 +269,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
184
269
  topic.send(:write_attribute, :title, "Still another topic")
185
270
  assert_equal "Still another topic", topic.title
186
271
 
187
- topic.send(:write_attribute, "title", "Still another topic: part 2")
272
+ topic[:title] = "Still another topic: part 2"
188
273
  assert_equal "Still another topic: part 2", topic.title
274
+
275
+ topic.send(:write_attribute, "title", "Still another topic: part 3")
276
+ assert_equal "Still another topic: part 3", topic.title
277
+
278
+ topic["title"] = "Still another topic: part 4"
279
+ assert_equal "Still another topic: part 4", topic.title
189
280
  end
190
281
 
191
282
  def test_read_attribute
@@ -238,6 +329,45 @@ class AttributeMethodsTest < ActiveRecord::TestCase
238
329
  # puts ""
239
330
  end
240
331
 
332
+ def test_overridden_write_attribute
333
+ topic = Topic.new
334
+ def topic.write_attribute(attr_name, value)
335
+ super(attr_name, value.downcase)
336
+ end
337
+
338
+ topic.send(:write_attribute, :title, "Yet another topic")
339
+ assert_equal "yet another topic", topic.title
340
+
341
+ topic[:title] = "Yet another topic: part 2"
342
+ assert_equal "yet another topic: part 2", topic.title
343
+
344
+ topic.send(:write_attribute, "title", "Yet another topic: part 3")
345
+ assert_equal "yet another topic: part 3", topic.title
346
+
347
+ topic["title"] = "Yet another topic: part 4"
348
+ assert_equal "yet another topic: part 4", topic.title
349
+ end
350
+
351
+ def test_overridden_read_attribute
352
+ topic = Topic.new
353
+ topic.title = "Stop changing the topic"
354
+ def topic.read_attribute(attr_name)
355
+ super(attr_name).upcase
356
+ end
357
+
358
+ assert_equal "STOP CHANGING THE TOPIC", topic.send(:read_attribute, "title")
359
+ assert_equal "STOP CHANGING THE TOPIC", topic["title"]
360
+
361
+ assert_equal "STOP CHANGING THE TOPIC", topic.send(:read_attribute, :title)
362
+ assert_equal "STOP CHANGING THE TOPIC", topic[:title]
363
+ end
364
+
365
+ def test_read_overridden_attribute
366
+ topic = Topic.new(:title => 'a')
367
+ def topic.title() 'b' end
368
+ assert_equal 'a', topic[:title]
369
+ end
370
+
241
371
  def test_query_attribute_string
242
372
  [nil, "", " "].each do |value|
243
373
  assert_equal false, Topic.new(:author_name => value).author_name?
@@ -352,7 +482,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
352
482
 
353
483
  def test_typecast_attribute_from_select_to_false
354
484
  topic = Topic.create(:title => 'Budget')
355
- # Oracle does not support boolean expressions in SELECT
485
+ # Oracle and DB2 does not support boolean expressions in SELECT
356
486
  if current_adapter?(:OracleAdapter) || current_adapter?(:IBM_DBAdapter)
357
487
  topic = Topic.find(:first, :select => "topics.*, 0 as is_test")
358
488
  else
@@ -363,7 +493,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
363
493
 
364
494
  def test_typecast_attribute_from_select_to_true
365
495
  topic = Topic.create(:title => 'Budget')
366
- # Oracle does not support boolean expressions in SELECT
496
+ # Oracle and DB2 does not support boolean expressions in SELECT
367
497
  if current_adapter?(:OracleAdapter) || current_adapter?(:IBM_DBAdapter)
368
498
  topic = Topic.find(:first, :select => "topics.*, 1 as is_test")
369
499
  else
@@ -372,30 +502,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
372
502
  assert topic.is_test?
373
503
  end
374
504
 
375
- def test_kernel_methods_not_implemented_in_activerecord
376
- %w(test name display y).each do |method|
377
- assert !ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
378
- end
379
- end
380
-
381
- def test_defined_kernel_methods_implemented_in_model
382
- %w(test name display y).each do |method|
383
- klass = Class.new ActiveRecord::Base
384
- klass.class_eval "def #{method}() 'defined #{method}' end"
385
- assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
386
- end
387
- end
388
-
389
- def test_defined_kernel_methods_implemented_in_model_abstract_subclass
390
- %w(test name display y).each do |method|
391
- abstract = Class.new ActiveRecord::Base
392
- abstract.class_eval "def #{method}() 'defined #{method}' end"
393
- abstract.abstract_class = true
394
- klass = Class.new abstract
395
- assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
396
- end
397
- end
398
-
399
505
  def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
400
506
  %w(save create_or_update).each do |method|
401
507
  klass = Class.new ActiveRecord::Base
@@ -419,12 +525,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
419
525
  Topic.instance_variable_set "@cached_attributes", nil
420
526
  end
421
527
 
422
- def test_time_related_columns_are_actually_cached
423
- column_types = %w(datetime timestamp time date).map(&:to_sym)
424
- column_names = Topic.columns.select{|c| column_types.include?(c.type) }.map(&:name)
425
-
426
- assert_equal column_names.sort, Topic.cached_attributes.sort
427
- assert_equal time_related_columns_on_topic.sort, Topic.cached_attributes.sort
528
+ def test_cacheable_columns_are_actually_cached
529
+ assert_equal cached_columns.sort, Topic.cached_attributes.sort
428
530
  end
429
531
 
430
532
  def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_else
@@ -435,8 +537,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
435
537
  assert cache.empty?
436
538
 
437
539
  all_columns = Topic.columns.map(&:name)
438
- cached_columns = time_related_columns_on_topic
439
- uncached_columns = all_columns - cached_columns
540
+ uncached_columns = all_columns - cached_columns
440
541
 
441
542
  all_columns.each do |attr_name|
442
543
  attribute_gets_cached = Topic.cache_attribute?(attr_name)
@@ -451,6 +552,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
451
552
  end
452
553
  end
453
554
 
555
+ def test_write_nil_to_time_attributes
556
+ in_time_zone "Pacific Time (US & Canada)" do
557
+ record = @target.new
558
+ record.written_on = nil
559
+ assert_nil record.written_on
560
+ end
561
+ end
562
+
454
563
  def test_time_attributes_are_retrieved_in_current_time_zone
455
564
  in_time_zone "Pacific Time (US & Canada)" do
456
565
  utc_time = Time.utc(2008, 1, 1)
@@ -486,6 +595,17 @@ class AttributeMethodsTest < ActiveRecord::TestCase
486
595
  end
487
596
  end
488
597
 
598
+ def test_setting_time_zone_aware_read_attribute
599
+ utc_time = Time.utc(2008, 1, 1)
600
+ cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
601
+ in_time_zone "Pacific Time (US & Canada)" do
602
+ record = @target.create(:written_on => cst_time).reload
603
+ assert_equal utc_time, record[:written_on]
604
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record[:written_on].time_zone
605
+ assert_equal Time.utc(2007, 12, 31, 16), record[:written_on].time
606
+ end
607
+ end
608
+
489
609
  def test_setting_time_zone_aware_attribute_with_string
490
610
  utc_time = Time.utc(2008, 1, 1)
491
611
  (-11..13).each do |timezone_offset|
@@ -505,6 +625,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
505
625
  record = @target.new
506
626
  record.written_on = ' '
507
627
  assert_nil record.written_on
628
+ assert_nil record[:written_on]
508
629
  end
509
630
  end
510
631
 
@@ -546,7 +667,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
546
667
  topic = @target.new(:title => "The pros and cons of programming naked.")
547
668
  assert !topic.respond_to?(:title)
548
669
  exception = assert_raise(NoMethodError) { topic.title }
549
- assert_equal "Attempt to call private method", exception.message
670
+ assert exception.message.include?("private method")
550
671
  assert_equal "I'm private", topic.send(:title)
551
672
  end
552
673
 
@@ -556,7 +677,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
556
677
  topic = @target.new
557
678
  assert !topic.respond_to?(:title=)
558
679
  exception = assert_raise(NoMethodError) { topic.title = "Pants"}
559
- assert_equal "Attempt to call private method", exception.message
680
+ assert exception.message.include?("private method")
560
681
  topic.send(:title=, "Very large pants")
561
682
  end
562
683
 
@@ -566,14 +687,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
566
687
  topic = @target.new(:title => "Isaac Newton's pants")
567
688
  assert !topic.respond_to?(:title?)
568
689
  exception = assert_raise(NoMethodError) { topic.title? }
569
- assert_equal "Attempt to call private method", exception.message
690
+ assert exception.message.include?("private method")
570
691
  assert topic.send(:title?)
571
692
  end
572
693
 
573
694
  def test_bulk_update_respects_access_control
574
695
  privatize("title=(value)")
575
696
 
576
- assert_raise(ActiveRecord::UnknownAttributeError) { topic = @target.new(:title => "Rants about pants") }
697
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new(:title => "Rants about pants") }
577
698
  assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
578
699
  end
579
700
 
@@ -592,10 +713,90 @@ class AttributeMethodsTest < ActiveRecord::TestCase
592
713
  Object.send(:undef_method, :title) # remove test method from object
593
714
  end
594
715
 
716
+ def test_list_of_serialized_attributes
717
+ assert_equal %w(content), Topic.serialized_attributes.keys
718
+ assert_equal %w(preferences), Contact.serialized_attributes.keys
719
+ end
720
+
721
+ def test_instance_method_should_be_defined_on_the_base_class
722
+ subklass = Class.new(Topic)
723
+
724
+ Topic.define_attribute_methods
725
+
726
+ instance = subklass.new
727
+ instance.id = 5
728
+ assert_equal 5, instance.id
729
+ assert subklass.method_defined?(:id), "subklass is missing id method"
730
+
731
+ Topic.undefine_attribute_methods
732
+
733
+ assert_equal 5, instance.id
734
+ assert subklass.method_defined?(:id), "subklass is missing id method"
735
+ end
736
+
737
+ def test_dispatching_column_attributes_through_method_missing_deprecated
738
+ Topic.define_attribute_methods
739
+
740
+ topic = Topic.new(:id => 5)
741
+ topic.id = 5
742
+
743
+ topic.method(:id).owner.send(:undef_method, :id)
744
+
745
+ assert_deprecated do
746
+ assert_equal 5, topic.id
747
+ end
748
+ ensure
749
+ Topic.undefine_attribute_methods
750
+ end
751
+
752
+ def test_read_attribute_with_nil_should_not_asplode
753
+ assert_equal nil, Topic.new.read_attribute(nil)
754
+ end
755
+
756
+ # If B < A, and A defines an accessor for 'foo', we don't want to override
757
+ # that by defining a 'foo' method in the generated methods module for B.
758
+ # (That module will be inserted between the two, e.g. [B, <GeneratedAttributes>, A].)
759
+ def test_inherited_custom_accessors
760
+ klass = Class.new(ActiveRecord::Base) do
761
+ self.table_name = "topics"
762
+ self.abstract_class = true
763
+ def title; "omg"; end
764
+ def title=(val); self.author_name = val; end
765
+ end
766
+ subklass = Class.new(klass)
767
+ [klass, subklass].each(&:define_attribute_methods)
768
+
769
+ topic = subklass.find(1)
770
+ assert_equal "omg", topic.title
771
+
772
+ topic.title = "lol"
773
+ assert_equal "lol", topic.author_name
774
+ end
775
+
776
+ def test_inherited_hook_removed
777
+ parent = Class.new(ActiveRecord::Base)
778
+ parent.table_name = "posts"
779
+ def parent.inherited(k)
780
+ end
781
+
782
+ klass = Class.new(parent)
783
+ assert_deprecated { klass.define_attribute_methods }
784
+ end
785
+
786
+ def test_setting_new_attributes_deprecated
787
+ t = Topic.new
788
+ assert_deprecated { t[:foo] = "bar" }
789
+ assert_equal "bar", t.foo
790
+ assert_equal "bar", t[:foo]
791
+ end
595
792
 
596
793
  private
794
+ def cached_columns
795
+ @cached_columns ||= time_related_columns_on_topic.map(&:name)
796
+ end
797
+
597
798
  def time_related_columns_on_topic
598
- Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
799
+ Topic.columns.select { |c| c.type.in?([:time, :date, :datetime, :timestamp]) }
599
800
  end
600
801
 
601
802
  def in_time_zone(zone)