sequel 3.30.0 → 3.31.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 (45) hide show
  1. data/CHANGELOG +40 -0
  2. data/Rakefile +12 -2
  3. data/doc/association_basics.rdoc +28 -0
  4. data/doc/dataset_filtering.rdoc +8 -0
  5. data/doc/opening_databases.rdoc +1 -0
  6. data/doc/release_notes/3.31.0.txt +146 -0
  7. data/lib/sequel/adapters/jdbc.rb +7 -6
  8. data/lib/sequel/adapters/jdbc/derby.rb +5 -0
  9. data/lib/sequel/adapters/jdbc/h2.rb +6 -1
  10. data/lib/sequel/adapters/mock.rb +21 -2
  11. data/lib/sequel/adapters/shared/db2.rb +10 -0
  12. data/lib/sequel/adapters/shared/mssql.rb +40 -5
  13. data/lib/sequel/adapters/shared/mysql.rb +19 -2
  14. data/lib/sequel/adapters/shared/oracle.rb +13 -1
  15. data/lib/sequel/adapters/shared/postgres.rb +52 -8
  16. data/lib/sequel/adapters/shared/sqlite.rb +4 -3
  17. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  18. data/lib/sequel/database/schema_generator.rb +9 -2
  19. data/lib/sequel/dataset/actions.rb +37 -19
  20. data/lib/sequel/dataset/features.rb +10 -0
  21. data/lib/sequel/dataset/prepared_statements.rb +0 -10
  22. data/lib/sequel/dataset/query.rb +13 -1
  23. data/lib/sequel/dataset/sql.rb +6 -1
  24. data/lib/sequel/model/associations.rb +14 -4
  25. data/lib/sequel/model/base.rb +10 -0
  26. data/lib/sequel/plugins/serialization.rb +82 -43
  27. data/lib/sequel/version.rb +1 -1
  28. data/spec/adapters/mssql_spec.rb +46 -0
  29. data/spec/adapters/mysql_spec.rb +3 -0
  30. data/spec/adapters/postgres_spec.rb +61 -24
  31. data/spec/core/database_spec.rb +31 -18
  32. data/spec/core/dataset_spec.rb +90 -13
  33. data/spec/core/mock_adapter_spec.rb +37 -0
  34. data/spec/extensions/instance_filters_spec.rb +1 -0
  35. data/spec/extensions/nested_attributes_spec.rb +1 -1
  36. data/spec/extensions/serialization_spec.rb +49 -5
  37. data/spec/extensions/sharding_spec.rb +1 -1
  38. data/spec/integration/associations_test.rb +15 -0
  39. data/spec/integration/dataset_test.rb +71 -0
  40. data/spec/integration/prepared_statement_test.rb +8 -0
  41. data/spec/model/association_reflection_spec.rb +27 -0
  42. data/spec/model/associations_spec.rb +18 -3
  43. data/spec/model/base_spec.rb +20 -0
  44. data/spec/model/eager_loading_spec.rb +21 -0
  45. metadata +4 -2
@@ -571,11 +571,7 @@ describe "Database#table_exists?" do
571
571
  end
572
572
  end
573
573
 
574
- describe "Database#transaction" do
575
- before do
576
- @db = Sequel.mock(:servers=>{:test=>{}})
577
- end
578
-
574
+ shared_examples_for "Database#transaction" do
579
575
  specify "should wrap the supplied block with BEGIN + COMMIT statements" do
580
576
  @db.transaction{@db.execute 'DROP TABLE test;'}
581
577
  @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
@@ -699,7 +695,7 @@ describe "Database#transaction" do
699
695
  q1.pop
700
696
  cc.should be_a_kind_of(Sequel::Mock::Connection)
701
697
  tr = @db.instance_variable_get(:@transactions)
702
- tr.should == {cc=>{:savepoint_level=>1}}
698
+ tr.keys.should == [cc]
703
699
  q.push nil
704
700
  t.join
705
701
  tr.should be_empty
@@ -796,6 +792,26 @@ describe "Database#transaction" do
796
792
  @db.sqls.should == ['BEGIN', 'ROLLBACK', 'foo']
797
793
  end
798
794
 
795
+ specify "should raise an error if you attempt to use after_commit inside a prepared transaction" do
796
+ @db.meta_def(:supports_prepared_transactions?){true}
797
+ proc{@db.transaction(:prepare=>'XYZ'){@db.after_commit{@db.execute('foo')}}}.should raise_error(Sequel::Error)
798
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
799
+ end
800
+
801
+ specify "should raise an error if you attempt to use after_rollback inside a prepared transaction" do
802
+ @db.meta_def(:supports_prepared_transactions?){true}
803
+ proc{@db.transaction(:prepare=>'XYZ'){@db.after_rollback{@db.execute('foo')}}}.should raise_error(Sequel::Error)
804
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
805
+ end
806
+ end
807
+
808
+ describe "Database#transaction with savepoint support" do
809
+ before do
810
+ @db = Sequel.mock(:servers=>{:test=>{}})
811
+ end
812
+
813
+ it_should_behave_like "Database#transaction"
814
+
799
815
  specify "should support after_commit inside savepoints" do
800
816
  @db.meta_def(:supports_savepoints?){true}
801
817
  @db.transaction do
@@ -817,18 +833,6 @@ describe "Database#transaction" do
817
833
  @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'RELEASE SAVEPOINT autopoint_1', 'ROLLBACK', 'foo', 'bar', 'baz']
818
834
  end
819
835
 
820
- specify "should raise an error if you attempt to use after_commit inside a prepared transaction" do
821
- @db.meta_def(:supports_prepared_transactions?){true}
822
- proc{@db.transaction(:prepare=>'XYZ'){@db.after_commit{@db.execute('foo')}}}.should raise_error(Sequel::Error)
823
- @db.sqls.should == ['BEGIN', 'ROLLBACK']
824
- end
825
-
826
- specify "should raise an error if you attempt to use after_rollback inside a prepared transaction" do
827
- @db.meta_def(:supports_prepared_transactions?){true}
828
- proc{@db.transaction(:prepare=>'XYZ'){@db.after_rollback{@db.execute('foo')}}}.should raise_error(Sequel::Error)
829
- @db.sqls.should == ['BEGIN', 'ROLLBACK']
830
- end
831
-
832
836
  specify "should raise an error if you attempt to use after_commit inside a savepoint in a prepared transaction" do
833
837
  @db.meta_def(:supports_savepoints?){true}
834
838
  @db.meta_def(:supports_prepared_transactions?){true}
@@ -843,7 +847,16 @@ describe "Database#transaction" do
843
847
  @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1','ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
844
848
  end
845
849
  end
850
+
851
+ describe "Database#transaction without savepoint support" do
852
+ before do
853
+ @db = Sequel.mock(:servers=>{:test=>{}})
854
+ @db.meta_def(:supports_savepoints?){false}
855
+ end
846
856
 
857
+ it_should_behave_like "Database#transaction"
858
+ end
859
+
847
860
  describe "Sequel.transaction" do
848
861
  before do
849
862
  @sqls = []
@@ -342,12 +342,12 @@ describe "Dataset#exists" do
342
342
  @ds1.filter(@ds2.exists).sql.should ==
343
343
  'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)))'
344
344
  @ds1.filter(@ds2.exists & @ds3.exists).sql.should ==
345
- 'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)) AND EXISTS (SELECT * FROM test WHERE (price > 50)))'
345
+ 'SELECT * FROM test WHERE ((EXISTS (SELECT * FROM test WHERE (price < 100))) AND (EXISTS (SELECT * FROM test WHERE (price > 50))))'
346
346
  end
347
347
 
348
348
  specify "should work in select" do
349
349
  @ds1.select(@ds2.exists.as(:a), @ds3.exists.as(:b)).sql.should ==
350
- 'SELECT EXISTS (SELECT * FROM test WHERE (price < 100)) AS a, EXISTS (SELECT * FROM test WHERE (price > 50)) AS b FROM test'
350
+ 'SELECT (EXISTS (SELECT * FROM test WHERE (price < 100))) AS a, (EXISTS (SELECT * FROM test WHERE (price > 50))) AS b FROM test'
351
351
  end
352
352
  end
353
353
 
@@ -844,7 +844,7 @@ end
844
844
 
845
845
  describe "Dataset#group_by" do
846
846
  before do
847
- @dataset = Sequel::Dataset.new(nil).from(:test).group_by(:type_id)
847
+ @dataset = Sequel.mock[:test].group_by(:type_id)
848
848
  end
849
849
 
850
850
  specify "should raise when trying to generate an update statement" do
@@ -881,6 +881,23 @@ describe "Dataset#group_by" do
881
881
  @dataset.group{[type_id > 1, type_id < 2]}.sql.should == "SELECT * FROM test GROUP BY (type_id > 1), (type_id < 2)"
882
882
  @dataset.group(:foo){type_id > 1}.sql.should == "SELECT * FROM test GROUP BY foo, (type_id > 1)"
883
883
  end
884
+
885
+ specify "should support a #group_rollup method if the database supports it" do
886
+ @dataset.meta_def(:supports_group_rollup?){true}
887
+ @dataset.group(:type_id).group_rollup.select_sql.should == "SELECT * FROM test GROUP BY ROLLUP(type_id)"
888
+ @dataset.group(:type_id, :b).group_rollup.select_sql.should == "SELECT * FROM test GROUP BY ROLLUP(type_id, b)"
889
+ end
890
+
891
+ specify "should support a #group_cube method if the database supports it" do
892
+ @dataset.meta_def(:supports_group_cube?){true}
893
+ @dataset.group(:type_id).group_cube.select_sql.should == "SELECT * FROM test GROUP BY CUBE(type_id)"
894
+ @dataset.group(:type_id, :b).group_cube.select_sql.should == "SELECT * FROM test GROUP BY CUBE(type_id, b)"
895
+ end
896
+
897
+ specify "should have #group_cube and #group_rollup methods raise an Error if not supported it" do
898
+ proc{@dataset.group(:type_id).group_rollup}.should raise_error(Sequel::Error)
899
+ proc{@dataset.group(:type_id).group_cube}.should raise_error(Sequel::Error)
900
+ end
884
901
  end
885
902
 
886
903
  describe "Dataset#as" do
@@ -930,6 +947,21 @@ describe "Dataset#literal" do
930
947
  @dataset.literal(:"items__na#m$e").should == "items.na#m$e"
931
948
  end
932
949
 
950
+ specify "should call sql_literal_append with dataset and sql on type if not natively supported and the object responds to it" do
951
+ @a = Class.new do
952
+ def sql_literal_append(ds, sql)
953
+ sql << "called #{ds.blah}"
954
+ end
955
+ def sql_literal(ds)
956
+ "not called #{ds.blah}"
957
+ end
958
+ end
959
+ def @dataset.blah
960
+ "ds"
961
+ end
962
+ @dataset.literal(@a.new).should == "called ds"
963
+ end
964
+
933
965
  specify "should call sql_literal with dataset on type if not natively supported and the object responds to it" do
934
966
  @a = Class.new do
935
967
  def sql_literal(ds)
@@ -1033,10 +1065,9 @@ describe "Dataset#literal" do
1033
1065
  end
1034
1066
 
1035
1067
  specify "should not modify literal strings" do
1068
+ @dataset.quote_identifiers = true
1036
1069
  @dataset.literal('col1 + 2'.lit).should == 'col1 + 2'
1037
-
1038
- @dataset.update_sql(:a => 'a + 2'.lit).should ==
1039
- 'UPDATE test SET a = a + 2'
1070
+ @dataset.update_sql(Sequel::SQL::Identifier.new('a'.lit) => 'a + 2'.lit).should == 'UPDATE "test" SET a = a + 2'
1040
1071
  end
1041
1072
 
1042
1073
  specify "should literalize BigDecimal instances correctly" do
@@ -1103,6 +1134,10 @@ describe "Dataset#from" do
1103
1134
  @dataset.from(:'#___#').select_sql.should == 'SELECT * FROM # AS #'
1104
1135
  end
1105
1136
 
1137
+ specify "should not handle :foo__schema__table___alias specially" do
1138
+ @dataset.from(:foo__schema__table___alias).select_sql.should == "SELECT * FROM foo.schema__table AS alias"
1139
+ end
1140
+
1106
1141
  specify "should hoist WITH clauses from subqueries if the dataset doesn't support CTEs in subselects" do
1107
1142
  @dataset.meta_def(:supports_cte?){true}
1108
1143
  @dataset.meta_def(:supports_cte_in_subselect?){false}
@@ -1770,9 +1805,9 @@ describe "Dataset#empty?" do
1770
1805
  specify "should return true if records exist in the dataset" do
1771
1806
  db = Sequel.mock(:fetch=>proc{|sql| {1=>1} unless sql =~ /WHERE 'f'/})
1772
1807
  db.from(:test).should_not be_empty
1773
- db.sqls.should == ['SELECT 1 FROM test LIMIT 1']
1808
+ db.sqls.should == ['SELECT 1 AS one FROM test LIMIT 1']
1774
1809
  db.from(:test).filter(false).should be_empty
1775
- db.sqls.should == ["SELECT 1 FROM test WHERE 'f' LIMIT 1"]
1810
+ db.sqls.should == ["SELECT 1 AS one FROM test WHERE 'f' LIMIT 1"]
1776
1811
  end
1777
1812
  end
1778
1813
 
@@ -1819,9 +1854,9 @@ describe "Dataset#first_source_table" do
1819
1854
  end
1820
1855
 
1821
1856
  specify "should be the unaliased part if aliased" do
1822
- @ds.from(:t___a).first_source_table.should == :t.identifier
1823
- @ds.from(:s__t___a).first_source_table.should == :t.qualify(:s)
1824
- @ds.from(:t.as(:a)).first_source_table.should == :t
1857
+ @ds.literal(@ds.from(:t___a).first_source_table).should == "t"
1858
+ @ds.literal(@ds.from(:s__t___a).first_source_table).should == "s.t"
1859
+ @ds.literal(@ds.from(:t.as(:a)).first_source_table).should == "t"
1825
1860
  end
1826
1861
 
1827
1862
  specify "should raise exception if table doesn't have a source" do
@@ -2145,7 +2180,7 @@ end
2145
2180
 
2146
2181
  describe "Dataset#insert_multiple" do
2147
2182
  before do
2148
- @db = Sequel.mock
2183
+ @db = Sequel.mock(:autoid=>2)
2149
2184
  @ds = @db[:items]
2150
2185
  end
2151
2186
 
@@ -2163,6 +2198,10 @@ describe "Dataset#insert_multiple" do
2163
2198
  "INSERT INTO items VALUES ('herro')",
2164
2199
  "INSERT INTO items VALUES ('the ticking crock')"]
2165
2200
  end
2201
+
2202
+ specify "should return array of inserted ids" do
2203
+ @ds.insert_multiple(['aa', 5, 3, {:a => 2}]).should == [2, 3, 4, 5]
2204
+ end
2166
2205
  end
2167
2206
 
2168
2207
  describe "Dataset aggregate methods" do
@@ -2588,6 +2627,11 @@ describe "Dataset#import" do
2588
2627
  @ds = @db[:items]
2589
2628
  end
2590
2629
 
2630
+ specify "should return nil without a query if no values" do
2631
+ @ds.import(['x', 'y'], []).should == nil
2632
+ @db.sqls.should == []
2633
+ end
2634
+
2591
2635
  specify "should accept string keys as column names" do
2592
2636
  @ds.import(['x', 'y'], [[1, 2], [3, 4]])
2593
2637
  @db.sqls.should == ['BEGIN',
@@ -2635,11 +2679,16 @@ end
2635
2679
 
2636
2680
  describe "Dataset#multi_insert" do
2637
2681
  before do
2638
- @db = Sequel.mock
2682
+ @db = Sequel.mock(:servers=>{:s1=>{}})
2639
2683
  @ds = @db[:items]
2640
2684
  @list = [{:name => 'abc'}, {:name => 'def'}, {:name => 'ghi'}]
2641
2685
  end
2642
2686
 
2687
+ specify "should return nil without a query if no values" do
2688
+ @ds.multi_insert([]).should == nil
2689
+ @db.sqls.should == []
2690
+ end
2691
+
2643
2692
  specify "should issue multiple insert statements inside a transaction" do
2644
2693
  @ds.multi_insert(@list)
2645
2694
  @db.sqls.should == ['BEGIN',
@@ -2649,6 +2698,34 @@ describe "Dataset#multi_insert" do
2649
2698
  'COMMIT']
2650
2699
  end
2651
2700
 
2701
+ specify "should respect :server option" do
2702
+ @ds.multi_insert(@list, :server=>:s1)
2703
+ @db.sqls.should == ['BEGIN -- s1',
2704
+ "INSERT INTO items (name) VALUES ('abc') -- s1",
2705
+ "INSERT INTO items (name) VALUES ('def') -- s1",
2706
+ "INSERT INTO items (name) VALUES ('ghi') -- s1",
2707
+ 'COMMIT -- s1']
2708
+ end
2709
+
2710
+ specify "should respect existing :server option on dataset" do
2711
+ @ds.server(:s1).multi_insert(@list)
2712
+ @db.sqls.should == ['BEGIN -- s1',
2713
+ "INSERT INTO items (name) VALUES ('abc') -- s1",
2714
+ "INSERT INTO items (name) VALUES ('def') -- s1",
2715
+ "INSERT INTO items (name) VALUES ('ghi') -- s1",
2716
+ 'COMMIT -- s1']
2717
+ end
2718
+
2719
+ specify "should respect :return=>:primary_key option" do
2720
+ @db.autoid = 1
2721
+ @ds.multi_insert(@list, :return=>:primary_key).should == [1, 2, 3]
2722
+ @db.sqls.should == ['BEGIN',
2723
+ "INSERT INTO items (name) VALUES ('abc')",
2724
+ "INSERT INTO items (name) VALUES ('def')",
2725
+ "INSERT INTO items (name) VALUES ('ghi')",
2726
+ 'COMMIT']
2727
+ end
2728
+
2652
2729
  specify "should handle different formats for tables" do
2653
2730
  @ds = @ds.from(:sch__tab)
2654
2731
  @ds.multi_insert(@list)
@@ -126,6 +126,11 @@ describe "Sequel Mock Adapter" do
126
126
  rs.should == [{:a=>1}, {:b=>2}]
127
127
  end
128
128
 
129
+ specify "should raise Error if given an invalid object to fetch" do
130
+ proc{Sequel.mock(:fetch=>Class.new).get(1)}.should raise_error(Sequel::Error)
131
+ proc{Sequel.mock(:fetch=>Object.new).get(1)}.should raise_error(Sequel::Error)
132
+ end
133
+
129
134
  specify "should be able to set the number of rows modified by update and delete using :numrows option as an integer" do
130
135
  db = Sequel.mock(:numrows=>2)
131
136
  db[:t].update(:a=>1).should == 2
@@ -188,6 +193,13 @@ describe "Sequel Mock Adapter" do
188
193
  ds.delete.should == 3
189
194
  end
190
195
 
196
+ specify "should raise Error if given an invalid object for numrows or autoid" do
197
+ proc{Sequel.mock(:numrows=>Class.new)[:a].delete}.should raise_error(Sequel::Error)
198
+ proc{Sequel.mock(:numrows=>Object.new)[:a].delete}.should raise_error(Sequel::Error)
199
+ proc{Sequel.mock(:autoid=>Class.new)[:a].insert}.should raise_error(Sequel::Error)
200
+ proc{Sequel.mock(:autoid=>Object.new)[:a].insert}.should raise_error(Sequel::Error)
201
+ end
202
+
191
203
  specify "should be able to set the autogenerated primary key returned by insert using :autoid option as an integer" do
192
204
  db = Sequel.mock(:autoid=>1)
193
205
  db[:t].insert(:a=>1).should == 1
@@ -279,6 +291,19 @@ describe "Sequel Mock Adapter" do
279
291
  db[:t].columns.should == [:c, :d]
280
292
  end
281
293
 
294
+ specify "should raise Error if given an invalid columns" do
295
+ proc{Sequel.mock(:columns=>Object.new)[:a].columns}.should raise_error(Sequel::Error)
296
+ end
297
+
298
+ specify "should not quote identifiers by default" do
299
+ Sequel.mock.send(:quote_identifiers_default).should be_false
300
+ end
301
+
302
+ specify "should not have identifier input/output methods by default" do
303
+ Sequel.mock.send(:identifier_input_method_default).should be_nil
304
+ Sequel.mock.send(:identifier_output_method_default).should be_nil
305
+ end
306
+
282
307
  specify "should keep a record of all executed SQL in #sqls" do
283
308
  db = Sequel.mock
284
309
  db[:t].all
@@ -379,4 +404,16 @@ describe "Sequel Mock Adapter" do
379
404
  ds.should be_a_kind_of(Sequel::Mock::Dataset)
380
405
  ds.columns.should == [:id, :a, :b]
381
406
  end
407
+
408
+ specify "should be able to load dialects based on the database name" do
409
+ Sequel.mock(:host=>'access').select(Date.new(2011, 12, 13)).sql.should == 'SELECT #2011-12-13#'
410
+ Sequel.mock(:host=>'db2').select(1).sql.should == 'SELECT 1 FROM "SYSIBM"."SYSDUMMY1"'
411
+ Sequel.mock(:host=>'firebird')[:a].distinct.limit(1, 2).sql.should == 'SELECT DISTINCT FIRST 1 SKIP 2 * FROM a'
412
+ Sequel.mock(:host=>'informix')[:a].distinct.limit(1, 2).sql.should == 'SELECT SKIP 2 FIRST 1 DISTINCT * FROM a'
413
+ Sequel.mock(:host=>'mssql', :quote_identifiers=>true, :identifier_input_method=>:upcase)[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM [A] WHERE (CONTAINS ([B], 'c'))"
414
+ Sequel.mock(:host=>'mysql', :quote_identifiers=>true)[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM `a` WHERE (MATCH (`b`) AGAINST ('c'))"
415
+ Sequel.mock(:host=>'oracle', :quote_identifiers=>true)[:a].limit(1).sql.should == 'SELECT * FROM (SELECT * FROM "a") "t1" WHERE (ROWNUM <= 1)'
416
+ Sequel.mock(:host=>'postgres', :quote_identifiers=>true)[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM \"a\" WHERE (to_tsvector('simple', (COALESCE(\"b\", ''))) @@ to_tsquery('simple', 'c'))"
417
+ Sequel.mock(:host=>'sqlite', :quote_identifiers=>true)[:a___b].sql.should == "SELECT * FROM `a` AS 'b'"
418
+ end
382
419
  end
@@ -7,6 +7,7 @@ describe "instance_filters plugin" do
7
7
  @c.columns :id, :name, :num
8
8
  @c.plugin :instance_filters
9
9
  @p = @c.load(:id=>1, :name=>'John', :num=>1)
10
+ MODEL_DB.sqls
10
11
  end
11
12
 
12
13
  specify "should raise an error when updating a stale record" do
@@ -148,7 +148,7 @@ describe "NestedAttributes plugin" do
148
148
  @db.sqls.should == []
149
149
  @Album.dataset._fetch = {:id=>1}
150
150
  ar.save
151
- check_sql_array("SELECT 1 FROM albums WHERE ((albums.artist_id = 20) AND (id = 10)) LIMIT 1",
151
+ check_sql_array("SELECT 1 AS one FROM albums WHERE ((albums.artist_id = 20) AND (id = 10)) LIMIT 1",
152
152
  ["UPDATE albums SET artist_id = NULL, name = 'Al' WHERE (id = 10)", "UPDATE albums SET name = 'Al', artist_id = NULL WHERE (id = 10)"],
153
153
  "UPDATE artists SET name = 'Ar' WHERE (id = 20)")
154
154
  end
@@ -37,11 +37,6 @@ describe "Serialization plugin" do
37
37
  MODEL_DB.sqls.map{|s| s.sub("...\n", '')}.should == ["INSERT INTO items (abc) VALUES ('--- 1\n')", "INSERT INTO items (abc) VALUES ('--- hello\n')"]
38
38
  end
39
39
 
40
- it "serialization_format should be the serialization format used" do
41
- @c.plugin :serialization, :yaml, :abc
42
- @c.serialization_format.should == :yaml
43
- end
44
-
45
40
  it "serialized_columns should be the columns serialized" do
46
41
  @c.plugin :serialization, :yaml, :abc
47
42
  @c.serialized_columns.should == [:abc]
@@ -71,6 +66,12 @@ describe "Serialization plugin" do
71
66
  ]
72
67
  end
73
68
 
69
+ it "should allow serializing attributes using arbitrary callable" do
70
+ @c.plugin :serialization, [proc{|s| s.reverse}, proc{}], :abc
71
+ @c.create(:abc => "hello")
72
+ MODEL_DB.sqls.should == ["INSERT INTO items (abc) VALUES ('olleh')"]
73
+ end
74
+
74
75
  it "should translate values to and from yaml serialization format using accessor methods" do
75
76
  @c.set_primary_key :id
76
77
  @c.plugin :serialization, :yaml, :abc, :def
@@ -133,6 +134,49 @@ describe "Serialization plugin" do
133
134
  "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
134
135
  end
135
136
 
137
+ it "should translate values to and from arbitrary callables using accessor methods" do
138
+ @c.set_primary_key :id
139
+ @c.plugin :serialization, [proc{|s| s.reverse}, proc{|s| s.reverse}], :abc, :def
140
+ @c.dataset._fetch = {:id => 1, :abc => 'cba', :def => 'olleh'}
141
+
142
+ o = @c.first
143
+ o.id.should == 1
144
+ o.abc.should == 'abc'
145
+ o.abc.should == 'abc'
146
+ o.def.should == "hello"
147
+ o.def.should == "hello"
148
+
149
+ o.update(:abc => 'foo')
150
+ @c.create(:abc => 'bar')
151
+
152
+ MODEL_DB.sqls.should == ["SELECT * FROM items LIMIT 1",
153
+ "UPDATE items SET abc = 'oof' WHERE (id = 1)",
154
+ "INSERT INTO items (abc) VALUES ('rab')",
155
+ "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
156
+ end
157
+
158
+ it "should handle registration of custom serializer/deserializer pairs" do
159
+ @c.set_primary_key :id
160
+ Sequel::Plugins::Serialization.register_format(:reverse, proc{|s| s.reverse}, proc{|s| s.reverse})
161
+ @c.plugin :serialization, :reverse, :abc, :def
162
+ @c.dataset._fetch = {:id => 1, :abc => 'cba', :def => 'olleh'}
163
+
164
+ o = @c.first
165
+ o.id.should == 1
166
+ o.abc.should == 'abc'
167
+ o.abc.should == 'abc'
168
+ o.def.should == "hello"
169
+ o.def.should == "hello"
170
+
171
+ o.update(:abc => 'foo')
172
+ @c.create(:abc => 'bar')
173
+
174
+ MODEL_DB.sqls.should == ["SELECT * FROM items LIMIT 1",
175
+ "UPDATE items SET abc = 'oof' WHERE (id = 1)",
176
+ "INSERT INTO items (abc) VALUES ('rab')",
177
+ "SELECT * FROM items WHERE (id = 10) LIMIT 1"]
178
+ end
179
+
136
180
  it "should copy serialization formats and columns to subclasses" do
137
181
  @c.set_primary_key :id
138
182
  @c.plugin :serialization, :yaml, :abc, :def
@@ -172,7 +172,7 @@ describe "sharding plugin" do
172
172
  # Should select from current object's shard to check existing association, but update associated object's shard
173
173
  sqls = @db.sqls
174
174
  ["UPDATE albums SET artist_id = NULL, name = 'T' WHERE (id = 5) -- s4", "UPDATE albums SET name = 'T', artist_id = NULL WHERE (id = 5) -- s4"].should include(sqls.pop)
175
- sqls.should == ["SELECT 1 FROM albums WHERE ((albums.artist_id = 2) AND (id = 5)) LIMIT 1 -- s2"]
175
+ sqls.should == ["SELECT 1 AS one FROM albums WHERE ((albums.artist_id = 2) AND (id = 5)) LIMIT 1 -- s2"]
176
176
  end
177
177
 
178
178
  specify "should be able to set a shard to use for any object using set_server" do
@@ -584,6 +584,21 @@ describe "Sequel::Model Simple Associations" do
584
584
  it_should_behave_like "eager limit strategies"
585
585
  end unless Sequel.guarded?(:mysql, :db2, :oracle)
586
586
 
587
+ specify "should handle many_to_one associations with same name as :key" do
588
+ Album.def_column_alias(:artist_id_id, :artist_id)
589
+ Album.many_to_one :artist_id, :key_column =>:artist_id, :class=>Artist
590
+ @album.update(:artist_id_id => @artist.id)
591
+ @album.artist_id.should == @artist
592
+
593
+ as = Album.eager(:artist_id).all
594
+ as.should == [@album]
595
+ as.map{|a| a.artist_id}.should == [@artist]
596
+
597
+ as = Album.eager_graph(:artist_id).all
598
+ as.should == [@album]
599
+ as.map{|a| a.artist_id}.should == [@artist]
600
+ end
601
+
587
602
  specify "should handle aliased tables when eager_graphing" do
588
603
  @album.update(:artist => @artist)
589
604
  @album.add_tag(@tag)