db2 2.6.2 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
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)