sequel 3.30.0 → 3.31.0

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