sequel 3.31.0 → 3.32.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 (56) hide show
  1. data/CHANGELOG +54 -0
  2. data/MIT-LICENSE +1 -1
  3. data/doc/advanced_associations.rdoc +17 -0
  4. data/doc/association_basics.rdoc +74 -30
  5. data/doc/release_notes/3.32.0.txt +202 -0
  6. data/doc/schema_modification.rdoc +1 -1
  7. data/lib/sequel/adapters/jdbc/db2.rb +7 -0
  8. data/lib/sequel/adapters/jdbc/derby.rb +13 -0
  9. data/lib/sequel/adapters/jdbc/h2.rb +10 -1
  10. data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
  11. data/lib/sequel/adapters/jdbc/oracle.rb +7 -0
  12. data/lib/sequel/adapters/mock.rb +4 -0
  13. data/lib/sequel/adapters/mysql.rb +3 -0
  14. data/lib/sequel/adapters/oracle.rb +7 -3
  15. data/lib/sequel/adapters/shared/db2.rb +9 -2
  16. data/lib/sequel/adapters/shared/mssql.rb +48 -2
  17. data/lib/sequel/adapters/shared/mysql.rb +24 -4
  18. data/lib/sequel/adapters/shared/oracle.rb +7 -6
  19. data/lib/sequel/adapters/shared/progress.rb +1 -1
  20. data/lib/sequel/adapters/shared/sqlite.rb +16 -10
  21. data/lib/sequel/core.rb +22 -0
  22. data/lib/sequel/database/query.rb +13 -4
  23. data/lib/sequel/dataset/actions.rb +20 -11
  24. data/lib/sequel/dataset/mutation.rb +7 -1
  25. data/lib/sequel/dataset/prepared_statements.rb +11 -0
  26. data/lib/sequel/dataset/sql.rb +21 -24
  27. data/lib/sequel/extensions/query.rb +1 -1
  28. data/lib/sequel/model.rb +5 -2
  29. data/lib/sequel/model/associations.rb +70 -16
  30. data/lib/sequel/model/base.rb +11 -6
  31. data/lib/sequel/plugins/active_model.rb +13 -1
  32. data/lib/sequel/plugins/composition.rb +43 -10
  33. data/lib/sequel/plugins/many_through_many.rb +4 -1
  34. data/lib/sequel/plugins/nested_attributes.rb +65 -10
  35. data/lib/sequel/plugins/serialization.rb +13 -8
  36. data/lib/sequel/plugins/serialization_modification_detection.rb +22 -10
  37. data/lib/sequel/version.rb +1 -1
  38. data/spec/adapters/mssql_spec.rb +33 -10
  39. data/spec/adapters/mysql_spec.rb +111 -91
  40. data/spec/adapters/oracle_spec.rb +18 -0
  41. data/spec/core/database_spec.rb +1 -0
  42. data/spec/core/dataset_spec.rb +110 -15
  43. data/spec/extensions/active_model_spec.rb +13 -0
  44. data/spec/extensions/many_through_many_spec.rb +14 -14
  45. data/spec/extensions/query_spec.rb +6 -0
  46. data/spec/extensions/serialization_modification_detection_spec.rb +36 -1
  47. data/spec/extensions/serialization_spec.rb +9 -0
  48. data/spec/integration/associations_test.rb +278 -154
  49. data/spec/integration/dataset_test.rb +39 -2
  50. data/spec/integration/plugin_test.rb +63 -3
  51. data/spec/integration/prepared_statement_test.rb +10 -3
  52. data/spec/integration/schema_test.rb +61 -14
  53. data/spec/integration/transaction_test.rb +10 -0
  54. data/spec/model/associations_spec.rb +170 -80
  55. data/spec/model/hooks_spec.rb +40 -0
  56. metadata +4 -2
@@ -31,6 +31,24 @@ describe "An Oracle database" do
31
31
  ORACLE_DB.pool.size.should == 0
32
32
  end
33
33
 
34
+ specify "should have working view_exists?" do
35
+ begin
36
+ ORACLE_DB.view_exists?(:cats).should be_false
37
+ ORACLE_DB.create_view(:cats, ORACLE_DB[:categories])
38
+ ORACLE_DB.view_exists?(:cats).should be_true
39
+ om = ORACLE_DB.identifier_output_method
40
+ im = ORACLE_DB.identifier_input_method
41
+ ORACLE_DB.identifier_output_method = :reverse
42
+ ORACLE_DB.identifier_input_method = :reverse
43
+ ORACLE_DB.view_exists?(:STAC).should be_true
44
+ ORACLE_DB.view_exists?(:cats).should be_false
45
+ ensure
46
+ ORACLE_DB.identifier_output_method = om
47
+ ORACLE_DB.identifier_input_method = im
48
+ ORACLE_DB.drop_view(:cats)
49
+ end
50
+ end
51
+
34
52
  specify "should be able to get current sequence value with SQL" do
35
53
  begin
36
54
  ORACLE_DB.create_table!(:foo){primary_key :id}
@@ -566,6 +566,7 @@ describe "Database#table_exists?" do
566
566
  specify "should try to select the first record from the table's dataset" do
567
567
  db = Sequel.mock(:fetch=>[Sequel::Error, [], [{:a=>1}]])
568
568
  db.table_exists?(:a).should be_false
569
+ db.sqls.should == ["SELECT NULL FROM a LIMIT 1"]
569
570
  db.table_exists?(:b).should be_true
570
571
  db.table_exists?(:c).should be_true
571
572
  end
@@ -495,29 +495,42 @@ describe "Dataset#where" do
495
495
  "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
496
496
  end
497
497
 
498
+ specify "should handle all types of IN/NOT IN queries with empty arrays" do
499
+ @dataset.filter(:id => []).sql.should == "SELECT * FROM test WHERE (id != id)"
500
+ @dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
501
+ @dataset.exclude(:id => []).sql.should == "SELECT * FROM test WHERE (id = id)"
502
+ @dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 = id1) AND (id2 = id2))"
503
+ end
504
+
505
+ specify "should handle all types of IN/NOT IN queries with empty arrays" do
506
+ begin
507
+ Sequel.empty_array_handle_nulls = false
508
+ @dataset.filter(:id => []).sql.should == "SELECT * FROM test WHERE (1 = 0)"
509
+ @dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 0)"
510
+ @dataset.exclude(:id => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
511
+ @dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
512
+ ensure
513
+ Sequel.empty_array_handle_nulls = true
514
+ end
515
+ end
516
+
498
517
  specify "should handle all types of IN/NOT IN queries" do
499
518
  @dataset.filter(:id => @d1.select(:id)).sql.should == "SELECT * FROM test WHERE (id IN (SELECT id FROM test WHERE (region = 'Asia')))"
500
- @dataset.filter(:id => []).sql.should == "SELECT * FROM test WHERE (id != id)"
501
519
  @dataset.filter(:id => [1, 2]).sql.should == "SELECT * FROM test WHERE (id IN (1, 2))"
502
520
  @dataset.filter([:id1, :id2] => @d1.select(:id1, :id2)).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN (SELECT id1, id2 FROM test WHERE (region = 'Asia')))"
503
- @dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
504
521
  @dataset.filter([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN ((1, 2), (3, 4)))"
505
522
  @dataset.filter([:id1, :id2] => [[1, 2], [3,4]]).sql.should == "SELECT * FROM test WHERE ((id1, id2) IN ((1, 2), (3, 4)))"
506
523
 
507
524
  @dataset.exclude(:id => @d1.select(:id)).sql.should == "SELECT * FROM test WHERE (id NOT IN (SELECT id FROM test WHERE (region = 'Asia')))"
508
- @dataset.exclude(:id => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
509
525
  @dataset.exclude(:id => [1, 2]).sql.should == "SELECT * FROM test WHERE (id NOT IN (1, 2))"
510
526
  @dataset.exclude([:id1, :id2] => @d1.select(:id1, :id2)).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN (SELECT id1, id2 FROM test WHERE (region = 'Asia')))"
511
- @dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
512
527
  @dataset.exclude([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN ((1, 2), (3, 4)))"
513
528
  @dataset.exclude([:id1, :id2] => [[1, 2], [3,4]]).sql.should == "SELECT * FROM test WHERE ((id1, id2) NOT IN ((1, 2), (3, 4)))"
514
529
  end
515
530
 
516
531
  specify "should handle IN/NOT IN queries with multiple columns and an array where the database doesn't support it" do
517
532
  @dataset.meta_def(:supports_multiple_column_in?){false}
518
- @dataset.filter([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
519
533
  @dataset.filter([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
520
- @dataset.exclude([:id1, :id2] => []).sql.should == "SELECT * FROM test WHERE (1 = 1)"
521
534
  @dataset.exclude([:id1, :id2] => [[1, 2], [3,4]].sql_array).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
522
535
  end
523
536
 
@@ -537,10 +550,25 @@ describe "Dataset#where" do
537
550
  d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
538
551
  @dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
539
552
  db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
540
- @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (1 = 1)"
553
+ @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE ((id1 = id1) AND (id2 = id2))"
541
554
  db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
542
555
  end
543
556
 
557
+ specify "should handle IN/NOT IN queries with multiple columns and an empty dataset where the database doesn't support it with correct NULL handling" do
558
+ begin
559
+ Sequel.empty_array_handle_nulls = false
560
+ @dataset.meta_def(:supports_multiple_column_in?){false}
561
+ db = Sequel.mock
562
+ d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
563
+ @dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (1 = 0)"
564
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
565
+ @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (1 = 1)"
566
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
567
+ ensure
568
+ Sequel.empty_array_handle_nulls = true
569
+ end
570
+ end
571
+
544
572
  specify "should handle IN/NOT IN queries for datasets with row_procs" do
545
573
  @dataset.meta_def(:supports_multiple_column_in?){false}
546
574
  db = Sequel.mock(:fetch=>[{:id1=>1, :id2=>2}, {:id1=>3, :id2=>4}])
@@ -1581,10 +1609,20 @@ describe "Dataset#limit" do
1581
1609
  end
1582
1610
 
1583
1611
  describe "Dataset#naked" do
1584
- specify "should remove any existing row_proc" do
1612
+ specify "should returned clone dataset without row_proc" do
1585
1613
  d = Sequel::Dataset.new(nil)
1586
1614
  d.row_proc = Proc.new{|r| r}
1587
1615
  d.naked.row_proc.should be_nil
1616
+ d.row_proc.should_not be_nil
1617
+ end
1618
+ end
1619
+
1620
+ describe "Dataset#naked!" do
1621
+ specify "should remove any existing row_proc" do
1622
+ d = Sequel::Dataset.new(nil)
1623
+ d.row_proc = Proc.new{|r| r}
1624
+ d.naked!.row_proc.should be_nil
1625
+ d.row_proc.should be_nil
1588
1626
  end
1589
1627
  end
1590
1628
 
@@ -2778,10 +2816,10 @@ describe "Dataset#multi_insert" do
2778
2816
 
2779
2817
  specify "should accept string keys as column names" do
2780
2818
  @ds.multi_insert([{'x'=>1, 'y'=>2}, {'x'=>3, 'y'=>4}])
2781
- @db.sqls.should == ['BEGIN',
2782
- "INSERT INTO items (x, y) VALUES (1, 2)",
2783
- "INSERT INTO items (x, y) VALUES (3, 4)",
2784
- 'COMMIT']
2819
+ sqls = @db.sqls
2820
+ ["INSERT INTO items (x, y) VALUES (1, 2)", "INSERT INTO items (y, x) VALUES (2, 1)"].should include(sqls.slice!(1))
2821
+ ["INSERT INTO items (x, y) VALUES (3, 4)", "INSERT INTO items (y, x) VALUES (4, 3)"].should include(sqls.slice!(1))
2822
+ sqls.should == ['BEGIN', 'COMMIT']
2785
2823
  end
2786
2824
 
2787
2825
  specify "should not do anything if no hashes are provided" do
@@ -3096,12 +3134,16 @@ describe "Dataset prepared statements and bound variables " do
3096
3134
 
3097
3135
  specify "#call should take a type and bind hash and interpolate it" do
3098
3136
  @ds.filter(:num=>:$n).call(:select, :n=>1)
3137
+ @ds.filter(:num=>:$n).call([:map, :a], :n=>1)
3138
+ @ds.filter(:num=>:$n).call([:to_hash, :a, :b], :n=>1)
3099
3139
  @ds.filter(:num=>:$n).call(:first, :n=>1)
3100
3140
  @ds.filter(:num=>:$n).call(:delete, :n=>1)
3101
3141
  @ds.filter(:num=>:$n).call(:update, {:n=>1, :n2=>2}, :num=>:$n2)
3102
3142
  @ds.call(:insert, {:n=>1}, :num=>:$n)
3103
3143
  @ds.call(:insert_select, {:n=>1}, :num=>:$n)
3104
3144
  @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
3145
+ 'SELECT * FROM items WHERE (num = 1)',
3146
+ 'SELECT * FROM items WHERE (num = 1)',
3105
3147
  'SELECT * FROM items WHERE (num = 1) LIMIT 1',
3106
3148
  'DELETE FROM items WHERE (num = 1)',
3107
3149
  'UPDATE items SET num = 2 WHERE (num = 1)',
@@ -3112,20 +3154,26 @@ describe "Dataset prepared statements and bound variables " do
3112
3154
  specify "#prepare should take a type and name and store it in the database for later use with call" do
3113
3155
  pss = []
3114
3156
  pss << @ds.filter(:num=>:$n).prepare(:select, :sn)
3157
+ pss << @ds.filter(:num=>:$n).prepare([:map, :a], :sm)
3158
+ pss << @ds.filter(:num=>:$n).prepare([:to_hash, :a, :b], :sh)
3115
3159
  pss << @ds.filter(:num=>:$n).prepare(:first, :fn)
3116
3160
  pss << @ds.filter(:num=>:$n).prepare(:delete, :dn)
3117
3161
  pss << @ds.filter(:num=>:$n).prepare(:update, :un, :num=>:$n2)
3118
3162
  pss << @ds.prepare(:insert, :in, :num=>:$n)
3119
3163
  pss << @ds.prepare(:insert_select, :ins, :num=>:$n)
3120
- @db.prepared_statements.keys.sort_by{|k| k.to_s}.should == [:dn, :fn, :in, :ins, :sn, :un]
3121
- [:sn, :fn, :dn, :un, :in, :ins].each_with_index{|x, i| @db.prepared_statements[x].should == pss[i]}
3164
+ @db.prepared_statements.keys.sort_by{|k| k.to_s}.should == [:dn, :fn, :in, :ins, :sh, :sm, :sn, :un]
3165
+ [:sn, :sm, :sh, :fn, :dn, :un, :in, :ins].each_with_index{|x, i| @db.prepared_statements[x].should == pss[i]}
3122
3166
  @db.call(:sn, :n=>1)
3167
+ @db.call(:sm, :n=>1)
3168
+ @db.call(:sh, :n=>1)
3123
3169
  @db.call(:fn, :n=>1)
3124
3170
  @db.call(:dn, :n=>1)
3125
3171
  @db.call(:un, :n=>1, :n2=>2)
3126
3172
  @db.call(:in, :n=>1)
3127
3173
  @db.call(:ins, :n=>1)
3128
3174
  @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
3175
+ 'SELECT * FROM items WHERE (num = 1)',
3176
+ 'SELECT * FROM items WHERE (num = 1)',
3129
3177
  'SELECT * FROM items WHERE (num = 1) LIMIT 1',
3130
3178
  'DELETE FROM items WHERE (num = 1)',
3131
3179
  'UPDATE items SET num = 2 WHERE (num = 1)',
@@ -3740,6 +3788,12 @@ describe "Sequel::Dataset#select_map" do
3740
3788
  @ds.db.sqls.should == ['SELECT a.b FROM t']
3741
3789
  end
3742
3790
 
3791
+ specify "should raise if multiple arguments and can't determine alias" do
3792
+ proc{@ds.select_map([:a.sql_function, :b])}.should raise_error(Sequel::Error)
3793
+ proc{@ds.select_map(:a.sql_function){b}}.should raise_error(Sequel::Error)
3794
+ proc{@ds.select_map{[a{}, b]}}.should raise_error(Sequel::Error)
3795
+ end
3796
+
3743
3797
  specify "should handle implicit aliases in arguments" do
3744
3798
  @ds.select_map(:a___b).should == [1, 2]
3745
3799
  @ds.db.sqls.should == ['SELECT a AS b FROM t']
@@ -3750,11 +3804,31 @@ describe "Sequel::Dataset#select_map" do
3750
3804
  @ds.db.sqls.should == ['SELECT a AS b FROM t']
3751
3805
  end
3752
3806
 
3807
+ specify "should handle identifiers with strings" do
3808
+ @ds.select_map([Sequel::SQL::Identifier.new('c'), :c]).should == [[1, 1], [2, 2]]
3809
+ @ds.db.sqls.should == ['SELECT c, c FROM t']
3810
+ end
3811
+
3753
3812
  specify "should accept a block" do
3754
3813
  @ds.select_map{a(t__c)}.should == [1, 2]
3755
3814
  @ds.db.sqls.should == ['SELECT a(t.c) FROM t']
3756
3815
  end
3757
3816
 
3817
+ specify "should accept a block with an array of columns" do
3818
+ @ds.select_map{[a(t__c).as(c), a(t__c).as(c)]}.should == [[1, 1], [2, 2]]
3819
+ @ds.db.sqls.should == ['SELECT a(t.c) AS c, a(t.c) AS c FROM t']
3820
+ end
3821
+
3822
+ specify "should accept a block with a column" do
3823
+ @ds.select_map(:c){a(t__c).as(c)}.should == [[1, 1], [2, 2]]
3824
+ @ds.db.sqls.should == ['SELECT c, a(t.c) AS c FROM t']
3825
+ end
3826
+
3827
+ specify "should accept a block and array of arguments" do
3828
+ @ds.select_map([:c, :c]){[a(t__c).as(c), a(t__c).as(c)]}.should == [[1, 1, 1, 1], [2, 2, 2, 2]]
3829
+ @ds.db.sqls.should == ['SELECT c, c, a(t.c) AS c, a(t.c) AS c FROM t']
3830
+ end
3831
+
3758
3832
  specify "should handle an array of columns" do
3759
3833
  @ds.select_map([:c, :c]).should == [[1, 1], [2, 2]]
3760
3834
  @ds.db.sqls.should == ['SELECT c, c FROM t']
@@ -3783,6 +3857,12 @@ describe "Sequel::Dataset#select_order_map" do
3783
3857
  @ds.db.sqls.should == ['SELECT a.b FROM t ORDER BY a.b']
3784
3858
  end
3785
3859
 
3860
+ specify "should raise if multiple arguments and can't determine alias" do
3861
+ proc{@ds.select_order_map([:a.sql_function, :b])}.should raise_error(Sequel::Error)
3862
+ proc{@ds.select_order_map(:a.sql_function){b}}.should raise_error(Sequel::Error)
3863
+ proc{@ds.select_order_map{[a{}, b]}}.should raise_error(Sequel::Error)
3864
+ end
3865
+
3786
3866
  specify "should handle implicit aliases in arguments" do
3787
3867
  @ds.select_order_map(:a___b).should == [1, 2]
3788
3868
  @ds.db.sqls.should == ['SELECT a AS b FROM t ORDER BY a']
@@ -3808,6 +3888,21 @@ describe "Sequel::Dataset#select_order_map" do
3808
3888
  @ds.db.sqls.should == ['SELECT a(t.c) FROM t ORDER BY a(t.c)']
3809
3889
  end
3810
3890
 
3891
+ specify "should accept a block with an array of columns" do
3892
+ @ds.select_order_map{[c.desc, a(t__c).as(c)]}.should == [[1, 1], [2, 2]]
3893
+ @ds.db.sqls.should == ['SELECT c, a(t.c) AS c FROM t ORDER BY c DESC, a(t.c)']
3894
+ end
3895
+
3896
+ specify "should accept a block with a column" do
3897
+ @ds.select_order_map(:c){a(t__c).as(c)}.should == [[1, 1], [2, 2]]
3898
+ @ds.db.sqls.should == ['SELECT c, a(t.c) AS c FROM t ORDER BY c, a(t.c)']
3899
+ end
3900
+
3901
+ specify "should accept a block and array of arguments" do
3902
+ @ds.select_order_map([:c, :c]){[a(t__c).as(c), c.desc]}.should == [[1, 1, 1, 1], [2, 2, 2, 2]]
3903
+ @ds.db.sqls.should == ['SELECT c, c, a(t.c) AS c, c FROM t ORDER BY c, c, a(t.c), c DESC']
3904
+ end
3905
+
3811
3906
  specify "should handle an array of columns" do
3812
3907
  @ds.select_order_map([:c, :c]).should == [[1, 1], [2, 2]]
3813
3908
  @ds.db.sqls.should == ['SELECT c, c FROM t ORDER BY c, c']
@@ -3873,7 +3968,7 @@ describe "Sequel::Dataset#select_hash" do
3873
3968
  end
3874
3969
 
3875
3970
  specify "should raise an error if the resulting symbol cannot be determined" do
3876
- proc{@ds.select_hash(:c.as(:a), 'foo')}.should raise_error(Sequel::Error)
3971
+ proc{@ds.select_hash(:c.as(:a), :b.sql_function)}.should raise_error(Sequel::Error)
3877
3972
  end
3878
3973
  end
3879
3974
 
@@ -24,6 +24,11 @@ describe "ActiveModel plugin" do
24
24
  columns :id, :id2
25
25
  def delete; end
26
26
  end
27
+ module ::Blog
28
+ class Post < Sequel::Model
29
+ plugin :active_model
30
+ end
31
+ end
27
32
  @c = AMLintTest
28
33
  @c.plugin :active_model
29
34
  @m = @model = @c.new
@@ -33,6 +38,7 @@ describe "ActiveModel plugin" do
33
38
  def teardown
34
39
  super
35
40
  Object.send(:remove_const, :AMLintTest)
41
+ Object.send(:remove_const, :Blog)
36
42
  end
37
43
  include ActiveModel::Lint::Tests
38
44
 
@@ -80,6 +86,13 @@ describe "ActiveModel plugin" do
80
86
  assert_equal false, @m.persisted?
81
87
  assert_equal false, @o.persisted?
82
88
  end
89
+
90
+ # Should return self, not a proxy object
91
+ def test__to_partial_path
92
+ assert_equal 'am_lint_tests/am_lint_test', @m.to_partial_path
93
+ assert_equal 'blog/posts/post', Blog::Post.new.to_partial_path
94
+ end
95
+
83
96
  end
84
97
  if defined?(MiniTest::Unit)
85
98
  tc.instance_methods.map{|x| x.to_s}.reject{|n| n !~ /\Atest_/}.each do |m|
@@ -101,52 +101,52 @@ describe Sequel::Model, "many_through_many" do
101
101
 
102
102
  it "should allowing filtering by many_through_many associations" do
103
103
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
104
- @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
104
+ @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
105
105
  end
106
106
 
107
107
  it "should allowing filtering by many_through_many associations with a single through table" do
108
108
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id]]
109
- @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists WHERE ((albums_artists.album_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
109
+ @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists WHERE ((albums_artists.album_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
110
110
  end
111
111
 
112
112
  it "should allowing filtering by many_through_many associations with aliased tables" do
113
113
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums_artists, :id, :id], [:albums_artists, :album_id, :tag_id]]
114
- @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.id = albums_artists.album_id) INNER JOIN albums_artists AS albums_artists_1 ON (albums_artists_1.album_id = albums_artists_0.id) WHERE ((albums_artists_1.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
114
+ @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.id = albums_artists.album_id) INNER JOIN albums_artists AS albums_artists_1 ON (albums_artists_1.album_id = albums_artists_0.id) WHERE ((albums_artists_1.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
115
115
  end
116
116
 
117
117
  it "should allowing filtering by many_through_many associations with composite keys" do
118
118
  @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
119
- @c1.filter(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE ((id, yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
119
+ @c1.filter(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
120
120
  end
121
121
 
122
122
  it "should allowing excluding by many_through_many associations" do
123
123
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
124
- @c1.exclude(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE ((id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL)))) OR (id IS NULL))'
124
+ @c1.exclude(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id IS NULL))'
125
125
  end
126
126
 
127
127
  it "should allowing excluding by many_through_many associations with composite keys" do
128
128
  @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
129
- @c1.exclude(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE (((id, yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (id IS NULL) OR (yyy IS NULL))'
129
+ @c1.exclude(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))'
130
130
  end
131
131
 
132
132
  it "should allowing filtering by multiple many_through_many associations" do
133
133
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
134
- @c1.filter(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL))))'
134
+ @c1.filter(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL))))'
135
135
  end
136
136
 
137
137
  it "should allowing filtering by multiple many_through_many associations with composite keys" do
138
138
  @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
139
- @c1.filter(:tags=>[@c2.load(:h1=>1234, :h2=>85), @c2.load(:h1=>2345, :h2=>95)]).sql.should == 'SELECT * FROM artists WHERE ((id, yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
139
+ @c1.filter(:tags=>[@c2.load(:h1=>1234, :h2=>85), @c2.load(:h1=>2345, :h2=>95)]).sql.should == 'SELECT * FROM artists WHERE ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
140
140
  end
141
141
 
142
142
  it "should allowing excluding by multiple many_through_many associations" do
143
143
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
144
- @c1.exclude(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == 'SELECT * FROM artists WHERE ((id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL)))) OR (id IS NULL))'
144
+ @c1.exclude(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == 'SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id IS NULL))'
145
145
  end
146
146
 
147
147
  it "should allowing excluding by multiple many_through_many associations with composite keys" do
148
148
  @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
149
- @c1.exclude(:tags=>[@c2.load(:h1=>1234, :h2=>85), @c2.load(:h1=>2345, :h2=>95)]).sql.should == 'SELECT * FROM artists WHERE (((id, yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (id IS NULL) OR (yyy IS NULL))'
149
+ @c1.exclude(:tags=>[@c2.load(:h1=>1234, :h2=>85), @c2.load(:h1=>2345, :h2=>95)]).sql.should == 'SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))'
150
150
  end
151
151
 
152
152
  it "should allowing filtering/excluding many_through_many associations with NULL values" do
@@ -157,22 +157,22 @@ describe Sequel::Model, "many_through_many" do
157
157
 
158
158
  it "should allowing filtering by many_through_many association datasets" do
159
159
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
160
- @c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT id FROM tags WHERE ((x = 1) AND (id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL))))'
160
+ @c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL))))'
161
161
  end
162
162
 
163
163
  it "should allowing filtering by many_through_many association datasets with composite keys" do
164
164
  @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
165
- @c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE ((id, yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT h1, h2 FROM tags WHERE ((x = 1) AND (h1 IS NOT NULL) AND (h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
165
+ @c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT tags.h1, tags.h2 FROM tags WHERE ((x = 1) AND (tags.h1 IS NOT NULL) AND (tags.h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
166
166
  end
167
167
 
168
168
  it "should allowing excluding by many_through_many association datasets" do
169
169
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
170
- @c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE ((id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT id FROM tags WHERE ((x = 1) AND (id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL)))) OR (id IS NULL))'
170
+ @c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id IS NULL))'
171
171
  end
172
172
 
173
173
  it "should allowing excluding by many_through_many association datasets with composite keys" do
174
174
  @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
175
- @c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE (((id, yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT h1, h2 FROM tags WHERE ((x = 1) AND (h1 IS NOT NULL) AND (h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (id IS NULL) OR (yyy IS NULL))'
175
+ @c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT tags.h1, tags.h2 FROM tags WHERE ((x = 1) AND (tags.h1 IS NOT NULL) AND (tags.h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))'
176
176
  end
177
177
 
178
178
  it "should support a :conditions option" do
@@ -17,6 +17,12 @@ describe "Dataset#query" do
17
17
  @d = Sequel::Dataset.new(nil)
18
18
  end
19
19
 
20
+ specify "should allow cloning without arguments" do
21
+ q = @d.query {clone}
22
+ q.class.should == @d.class
23
+ q.sql.should == "SELECT *"
24
+ end
25
+
20
26
  specify "should support #from" do
21
27
  q = @d.query {from :xxx}
22
28
  q.class.should == @d.class
@@ -9,12 +9,20 @@ describe "serialization_modification_detection plugin" do
9
9
  plugin :serialization, :yaml, :h
10
10
  plugin :serialization_modification_detection
11
11
  end
12
- @o1 = @c.new
12
+ @o1 = @c.new(:h=>{})
13
13
  @o2 = @c.load(:id=>1, :h=>"--- {}\n\n")
14
+ @o3 = @c.new
15
+ @o4 = @c.load(:id=>1, :h=>nil)
14
16
  MODEL_DB.reset
15
17
  end
16
18
 
17
19
  it "should not detect columns that haven't been changed" do
20
+ @o1.changed_columns.should == []
21
+ @o1.h.should == {}
22
+ @o1.h[1] = 2
23
+ @o1.h.clear
24
+ @o1.changed_columns.should == []
25
+
18
26
  @o2.changed_columns.should == []
19
27
  @o2.h.should == {}
20
28
  @o2.h[1] = 2
@@ -23,15 +31,42 @@ describe "serialization_modification_detection plugin" do
23
31
  end
24
32
 
25
33
  it "should detect columns that have been changed" do
34
+ @o1.changed_columns.should == []
35
+ @o1.h.should == {}
36
+ @o1.h[1] = 2
37
+ @o1.changed_columns.should == [:h]
38
+
26
39
  @o2.changed_columns.should == []
27
40
  @o2.h.should == {}
28
41
  @o2.h[1] = 2
29
42
  @o2.changed_columns.should == [:h]
43
+
44
+ @o3.changed_columns.should == []
45
+ @o3.h.should == nil
46
+ @o3.h = {}
47
+ @o3.changed_columns.should == [:h]
48
+
49
+ @o4.changed_columns.should == []
50
+ @o4.h.should == nil
51
+ @o4.h = {}
52
+ @o4.changed_columns.should == [:h]
30
53
  end
31
54
 
32
55
  it "should report correct changed_columns after saving" do
56
+ @o1.h[1] = 2
57
+ @o1.save
58
+ @o1.changed_columns.should == []
59
+
33
60
  @o2.h[1] = 2
34
61
  @o2.save_changes
35
62
  @o2.changed_columns.should == []
63
+
64
+ @o3.h = {1=>2}
65
+ @o3.save
66
+ @o3.changed_columns.should == []
67
+
68
+ @o4.h = {1=>2}
69
+ @o4.save
70
+ @o4.changed_columns.should == []
36
71
  end
37
72
  end