sequel 4.48.0 → 4.49.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +56 -0
  3. data/doc/advanced_associations.rdoc +1 -1
  4. data/doc/opening_databases.rdoc +3 -2
  5. data/doc/release_notes/4.49.0.txt +222 -0
  6. data/lib/sequel/adapters/ibmdb.rb +6 -1
  7. data/lib/sequel/adapters/jdbc.rb +3 -1
  8. data/lib/sequel/adapters/jdbc/h2.rb +10 -1
  9. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -2
  10. data/lib/sequel/adapters/jdbc/sqlserver.rb +9 -2
  11. data/lib/sequel/adapters/mock.rb +3 -0
  12. data/lib/sequel/adapters/mysql2.rb +1 -1
  13. data/lib/sequel/adapters/postgres.rb +2 -1
  14. data/lib/sequel/adapters/shared/mysql.rb +4 -1
  15. data/lib/sequel/adapters/shared/oracle.rb +26 -3
  16. data/lib/sequel/connection_pool.rb +9 -2
  17. data/lib/sequel/connection_pool/sharded_single.rb +1 -1
  18. data/lib/sequel/connection_pool/sharded_threaded.rb +1 -1
  19. data/lib/sequel/connection_pool/single.rb +2 -2
  20. data/lib/sequel/connection_pool/threaded.rb +2 -2
  21. data/lib/sequel/database/connecting.rb +3 -3
  22. data/lib/sequel/database/dataset_defaults.rb +14 -1
  23. data/lib/sequel/dataset.rb +1 -1
  24. data/lib/sequel/dataset/actions.rb +54 -0
  25. data/lib/sequel/dataset/dataset_module.rb +58 -0
  26. data/lib/sequel/dataset/query.rb +3 -3
  27. data/lib/sequel/exceptions.rb +8 -0
  28. data/lib/sequel/extensions/_model_pg_row.rb +5 -2
  29. data/lib/sequel/extensions/current_datetime_timestamp.rb +2 -1
  30. data/lib/sequel/extensions/date_arithmetic.rb +1 -0
  31. data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -2
  32. data/lib/sequel/extensions/migration.rb +5 -2
  33. data/lib/sequel/extensions/null_dataset.rb +1 -0
  34. data/lib/sequel/model/associations.rb +3 -0
  35. data/lib/sequel/model/base.rb +10 -55
  36. data/lib/sequel/model/dataset_module.rb +5 -43
  37. data/lib/sequel/model/errors.rb +2 -1
  38. data/lib/sequel/model/inflections.rb +17 -5
  39. data/lib/sequel/plugins/active_model.rb +2 -2
  40. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  41. data/lib/sequel/plugins/composition.rb +2 -2
  42. data/lib/sequel/plugins/dataset_associations.rb +25 -13
  43. data/lib/sequel/plugins/json_serializer.rb +2 -2
  44. data/lib/sequel/plugins/pg_row.rb +4 -2
  45. data/lib/sequel/plugins/serialization.rb +1 -0
  46. data/lib/sequel/plugins/single_table_inheritance.rb +6 -1
  47. data/lib/sequel/plugins/touch.rb +2 -1
  48. data/lib/sequel/plugins/validation_helpers.rb +10 -2
  49. data/lib/sequel/sql.rb +16 -7
  50. data/lib/sequel/version.rb +1 -1
  51. data/spec/adapters/mssql_spec.rb +4 -4
  52. data/spec/adapters/mysql_spec.rb +5 -1
  53. data/spec/adapters/oracle_spec.rb +4 -0
  54. data/spec/bin_spec.rb +7 -1
  55. data/spec/core/connection_pool_spec.rb +28 -14
  56. data/spec/core/database_spec.rb +149 -0
  57. data/spec/core/dataset_spec.rb +173 -0
  58. data/spec/extensions/class_table_inheritance_spec.rb +58 -17
  59. data/spec/extensions/composition_spec.rb +13 -0
  60. data/spec/extensions/dataset_associations_spec.rb +12 -0
  61. data/spec/extensions/many_through_many_spec.rb +4 -4
  62. data/spec/extensions/null_dataset_spec.rb +1 -1
  63. data/spec/extensions/serialization_spec.rb +1 -1
  64. data/spec/extensions/single_table_inheritance_spec.rb +16 -0
  65. data/spec/extensions/validation_helpers_spec.rb +1 -2
  66. data/spec/integration/associations_test.rb +8 -0
  67. data/spec/integration/plugin_test.rb +8 -3
  68. data/spec/model/association_reflection_spec.rb +1 -1
  69. data/spec/model/associations_spec.rb +29 -9
  70. data/spec/model/class_dataset_methods_spec.rb +6 -0
  71. data/spec/model/eager_loading_spec.rb +8 -8
  72. data/spec/model/plugins_spec.rb +34 -0
  73. data/spec/model/record_spec.rb +1 -1
  74. data/spec/spec_config.rb +2 -0
  75. metadata +5 -2
@@ -1895,6 +1895,126 @@ describe "Dataset#with_extend" do
1895
1895
  end
1896
1896
  end
1897
1897
 
1898
+ describe "Dataset#with_extend custom methods" do
1899
+ before do
1900
+ @ds = Sequel.mock[:items]
1901
+ end
1902
+
1903
+ it "should have dataset_module support a where method" do
1904
+ @ds = @ds.with_extend{where :released, :released}
1905
+ @ds.released.sql.must_equal 'SELECT * FROM items WHERE released'
1906
+ @ds.where(:foo).released.sql.must_equal 'SELECT * FROM items WHERE (foo AND released)'
1907
+ end
1908
+
1909
+ it "should have dataset_module support a having method" do
1910
+ @ds = @ds.with_extend{having(:released){released}}
1911
+ @ds.released.sql.must_equal 'SELECT * FROM items HAVING released'
1912
+ @ds.where(:foo).released.sql.must_equal 'SELECT * FROM items WHERE foo HAVING released'
1913
+ end
1914
+
1915
+ it "should have dataset_module support an exclude method" do
1916
+ @ds = @ds.with_extend{exclude :released, :released}
1917
+ @ds.released.sql.must_equal 'SELECT * FROM items WHERE NOT released'
1918
+ @ds.where(:foo).released.sql.must_equal 'SELECT * FROM items WHERE (foo AND NOT released)'
1919
+ end
1920
+
1921
+ it "should have dataset_module support an exclude_having method" do
1922
+ @ds = @ds.with_extend{exclude_having :released, :released}
1923
+ @ds.released.sql.must_equal 'SELECT * FROM items HAVING NOT released'
1924
+ @ds.where(:foo).released.sql.must_equal 'SELECT * FROM items WHERE foo HAVING NOT released'
1925
+ end
1926
+
1927
+ it "should have dataset_module support a distinct method" do
1928
+ @ds = @ds.with_extend{def supports_distinct_on?; true end; distinct :foo, :baz}
1929
+ @ds.foo.sql.must_equal 'SELECT DISTINCT ON (baz) * FROM items'
1930
+ @ds.where(:bar).foo.sql.must_equal 'SELECT DISTINCT ON (baz) * FROM items WHERE bar'
1931
+ end
1932
+
1933
+ it "should have dataset_module support a grep method" do
1934
+ @ds = @ds.with_extend{grep :foo, :baz, 'quux%'}
1935
+ @ds.foo.sql.must_equal 'SELECT * FROM items WHERE ((baz LIKE \'quux%\' ESCAPE \'\\\'))'
1936
+ @ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE (bar AND ((baz LIKE \'quux%\' ESCAPE \'\\\')))'
1937
+ end
1938
+
1939
+ it "should have dataset_module support a group method" do
1940
+ @ds = @ds.with_extend{group :foo, :baz}
1941
+ @ds.foo.sql.must_equal 'SELECT * FROM items GROUP BY baz'
1942
+ @ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE bar GROUP BY baz'
1943
+ end
1944
+
1945
+ it "should have dataset_module support a group_and_count method" do
1946
+ @ds = @ds.with_extend{group_and_count :foo, :baz}
1947
+ @ds.foo.sql.must_equal 'SELECT baz, count(*) AS count FROM items GROUP BY baz'
1948
+ @ds.where(:bar).foo.sql.must_equal 'SELECT baz, count(*) AS count FROM items WHERE bar GROUP BY baz'
1949
+ end
1950
+
1951
+ it "should have dataset_module support a group_append method" do
1952
+ @ds = @ds.with_extend{group_append :foo, :baz}
1953
+ @ds.foo.sql.must_equal 'SELECT * FROM items GROUP BY baz'
1954
+ @ds.group(:bar).foo.sql.must_equal 'SELECT * FROM items GROUP BY bar, baz'
1955
+ end
1956
+
1957
+ it "should have dataset_module support a limit method" do
1958
+ @ds = @ds.with_extend{limit :foo, 1}
1959
+ @ds.foo.sql.must_equal 'SELECT * FROM items LIMIT 1'
1960
+ @ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE bar LIMIT 1'
1961
+ end
1962
+
1963
+ it "should have dataset_module support a offset method" do
1964
+ @ds = @ds.with_extend{offset :foo, 1}
1965
+ @ds.foo.sql.must_equal 'SELECT * FROM items OFFSET 1'
1966
+ @ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE bar OFFSET 1'
1967
+ end
1968
+
1969
+ it "should have dataset_module support a order method" do
1970
+ @ds = @ds.with_extend{order(:foo){:baz}}
1971
+ @ds.foo.sql.must_equal 'SELECT * FROM items ORDER BY baz'
1972
+ @ds.where(:bar).foo.sql.must_equal 'SELECT * FROM items WHERE bar ORDER BY baz'
1973
+ end
1974
+
1975
+ it "should have dataset_module support a order_append method" do
1976
+ @ds = @ds.with_extend{order_append :foo, :baz}
1977
+ @ds.foo.sql.must_equal 'SELECT * FROM items ORDER BY baz'
1978
+ @ds.order(:bar).foo.sql.must_equal 'SELECT * FROM items ORDER BY bar, baz'
1979
+ end
1980
+
1981
+ it "should have dataset_module support a order_prepend method" do
1982
+ @ds = @ds.with_extend{order_prepend :foo, :baz}
1983
+ @ds.foo.sql.must_equal 'SELECT * FROM items ORDER BY baz'
1984
+ @ds.order(:bar).foo.sql.must_equal 'SELECT * FROM items ORDER BY baz, bar'
1985
+ end
1986
+
1987
+ it "should have dataset_module support a select method" do
1988
+ @ds = @ds.with_extend{select :foo, :baz}
1989
+ @ds.foo.sql.must_equal 'SELECT baz FROM items'
1990
+ @ds.where(:bar).foo.sql.must_equal 'SELECT baz FROM items WHERE bar'
1991
+ end
1992
+
1993
+ it "should have dataset_module support a select_all method" do
1994
+ @ds = @ds.with_extend{select_all :foo, :baz}
1995
+ @ds.foo.sql.must_equal 'SELECT baz.* FROM items'
1996
+ @ds.where(:bar).foo.sql.must_equal 'SELECT baz.* FROM items WHERE bar'
1997
+ end
1998
+
1999
+ it "should have dataset_module support a select_append method" do
2000
+ @ds = @ds.with_extend{select_append :foo, :baz}
2001
+ @ds.foo.sql.must_equal 'SELECT *, baz FROM items'
2002
+ @ds.where(:bar).foo.sql.must_equal 'SELECT *, baz FROM items WHERE bar'
2003
+ end
2004
+
2005
+ it "should have dataset_module support a select_group method" do
2006
+ @ds = @ds.with_extend{select_group :foo, :baz}
2007
+ @ds.foo.sql.must_equal 'SELECT baz FROM items GROUP BY baz'
2008
+ @ds.where(:bar).foo.sql.must_equal 'SELECT baz FROM items WHERE bar GROUP BY baz'
2009
+ end
2010
+
2011
+ it "should have dataset_module support a server method" do
2012
+ @ds = @ds.with_extend{server :foo, :baz}
2013
+ @ds.foo.opts[:server].must_equal :baz
2014
+ @ds.where(:bar).foo.opts[:server].must_equal :baz
2015
+ end
2016
+ end
2017
+
1898
2018
  describe "Dataset#with_row_proc" do
1899
2019
  it "should returned clone dataset with the given row_proc" do
1900
2020
  d = Sequel.mock.dataset
@@ -5533,3 +5653,56 @@ describe "Dataset#output_identifier" do
5533
5653
  meth.call('A').must_equal :a
5534
5654
  end
5535
5655
  end
5656
+
5657
+ describe "Dataset#where_all" do
5658
+ before do
5659
+ @ds = Sequel.mock(:fetch=>{:id=>1})[:items].freeze
5660
+ end
5661
+
5662
+ it "should filter dataset with condition, and return related rows" do
5663
+ 5.times do
5664
+ @ds.where_all(:id=>1).must_equal [{:id=>1}]
5665
+ @ds.db.sqls.must_equal ['SELECT * FROM items WHERE (id = 1)']
5666
+ end
5667
+ end
5668
+
5669
+ it "should yield each row to the given block" do
5670
+ 5.times do
5671
+ a = []
5672
+ @ds.where_all(:id=>1){|r| a << r}.must_equal [{:id=>1}]
5673
+ a.must_equal [{:id=>1}]
5674
+ @ds.db.sqls.must_equal ['SELECT * FROM items WHERE (id = 1)']
5675
+ end
5676
+ end
5677
+ end
5678
+
5679
+ describe "Dataset#where_each" do
5680
+ before do
5681
+ @ds = Sequel.mock(:fetch=>{:id=>1})[:items].freeze
5682
+ end
5683
+
5684
+ it "should yield each row to the given block" do
5685
+ 5.times do
5686
+ a = []
5687
+ @ds.where_each(:id=>1){|r| a << r}
5688
+ a.must_equal [{:id=>1}]
5689
+ @ds.db.sqls.must_equal ['SELECT * FROM items WHERE (id = 1)']
5690
+ end
5691
+ end
5692
+ end
5693
+
5694
+ describe "Dataset#where_single_value" do
5695
+ before do
5696
+ @ds = Sequel.mock(:fetch=>{:id=>1})[:items].with_extend do
5697
+ select :only_id, :id
5698
+ end.freeze
5699
+ end
5700
+
5701
+ it "should return single value" do
5702
+ 5.times do
5703
+ a = []
5704
+ @ds.only_id.where_single_value(:id=>1).must_equal 1
5705
+ @ds.db.sqls.must_equal ['SELECT id FROM items WHERE (id = 1) LIMIT 1']
5706
+ end
5707
+ end
5708
+ end
@@ -585,7 +585,7 @@ describe "class_table_inheritance plugin with :alias option" do
585
585
  Executive.cti_models.first.must_equal Employee
586
586
  end
587
587
 
588
- it "#cti_columns should be a mapping of table names to columns" do
588
+ deprecated "#cti_columns should be a mapping of table names to columns" do
589
589
  Executive.cti_columns.must_equal(:employees=>[:id, :name, :kind], :managers=>[:id, :num_staff], :executives=>[:id, :num_managers])
590
590
  end
591
591
 
@@ -1080,15 +1080,13 @@ describe "class_table_inheritance plugin with :alias option" do
1080
1080
  class ::Staff < Employee
1081
1081
  many_to_one :manager
1082
1082
  end
1083
+ class ::Intern < Employee
1084
+ end
1083
1085
  @ds = Employee.dataset
1084
1086
  @db.sqls
1085
1087
  end
1086
1088
  after do
1087
- Object.send(:remove_const, :Ceo)
1088
- Object.send(:remove_const, :Executive)
1089
- Object.send(:remove_const, :Manager)
1090
- Object.send(:remove_const, :Staff)
1091
- Object.send(:remove_const, :Employee)
1089
+ [:Intern, :Ceo, :Executive, :Manager, :Staff, :Employee].each{|s| Object.send(:remove_const, s)}
1092
1090
  end
1093
1091
 
1094
1092
  it "should freeze CTI information when freezing model class" do
@@ -1119,7 +1117,7 @@ describe "class_table_inheritance plugin with :alias option" do
1119
1117
  Executive.cti_models.first.must_equal Employee
1120
1118
  end
1121
1119
 
1122
- it "#cti_columns should be a mapping of table names to columns" do
1120
+ deprecated "#cti_columns should be a mapping of table names to columns" do
1123
1121
  Executive.cti_columns.must_equal(:employees=>[:id, :name, :kind], :managers=>[:id, :num_staff], :executives=>[:id, :num_managers])
1124
1122
  end
1125
1123
 
@@ -1128,6 +1126,7 @@ describe "class_table_inheritance plugin with :alias option" do
1128
1126
  Executive.simple_table.must_be_nil
1129
1127
  Ceo.simple_table.must_be_nil
1130
1128
  Staff.simple_table.must_be_nil
1129
+ Intern.simple_table.must_be_nil
1131
1130
  end
1132
1131
 
1133
1132
  it "should have working row_proc if using set_dataset in subclass to remove columns" do
@@ -1142,10 +1141,11 @@ describe "class_table_inheritance plugin with :alias option" do
1142
1141
  Executive.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)) AS employees'
1143
1142
  Ceo.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (employees.kind IN (\'Ceo\'))) AS employees'
1144
1143
  Staff.dataset.sql.must_equal 'SELECT * FROM (SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)) AS employees'
1144
+ Intern.dataset.sql.must_equal 'SELECT * FROM employees WHERE (employees.kind IN (\'Intern\'))'
1145
1145
  end
1146
1146
 
1147
1147
  it "should return rows with the correct class based on the polymorphic_key value" do
1148
- @ds.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}]).all.collect{|x| x.class}.must_equal [Employee, Manager, Executive, Ceo, Staff]
1148
+ @ds.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}, {:kind=>'Intern'}]).all.collect{|x| x.class}.must_equal [Employee, Manager, Executive, Ceo, Staff, Intern]
1149
1149
  end
1150
1150
 
1151
1151
  it "should return rows with the correct class based on the polymorphic_key value for subclasses" do
@@ -1165,7 +1165,7 @@ describe "class_table_inheritance plugin with :alias option" do
1165
1165
 
1166
1166
  it "should return rows with the current class if cti_key is nil" do
1167
1167
  Employee.plugin(:class_table_inheritance, :alias=>:employees)
1168
- Employee.dataset.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}]).all.map{|x| x.class}.must_equal [Employee, Employee, Employee, Employee, Employee]
1168
+ Employee.dataset.with_fetch([{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Ceo'}, {:kind=>'Staff'}, {:kind=>'Intern'}]).all.map{|x| x.class}.must_equal [Employee, Employee, Employee, Employee, Employee, Employee]
1169
1169
  end
1170
1170
 
1171
1171
  it "should return rows with the current class if cti_key is nil in subclasses" do
@@ -1178,15 +1178,17 @@ describe "class_table_inheritance plugin with :alias option" do
1178
1178
  end
1179
1179
 
1180
1180
  it "should handle a model map with integer values" do
1181
- Employee.plugin(:class_table_inheritance, :key=>:kind, :model_map=>{0=>:Employee, 1=>:Manager, 2=>:Executive, 3=>:Ceo}, :alias=>:employees)
1181
+ Employee.plugin(:class_table_inheritance, :key=>:kind, :model_map=>{0=>:Employee, 1=>:Manager, 2=>:Executive, 3=>:Ceo, 4=>:Intern}, :alias=>:employees)
1182
+ Object.send(:remove_const, :Intern)
1182
1183
  Object.send(:remove_const, :Ceo)
1183
1184
  Object.send(:remove_const, :Executive)
1184
1185
  Object.send(:remove_const, :Manager)
1186
+ class ::Intern < Employee; end
1185
1187
  class ::Manager < Employee; end
1186
1188
  class ::Executive < Manager; end
1187
1189
  class ::Ceo < Executive; end
1188
- Employee.dataset = Employee.dataset.with_fetch([{:kind=>nil},{:kind=>0},{:kind=>1}, {:kind=>2}, {:kind=>3}])
1189
- Employee.all.collect{|x| x.class}.must_equal [Employee, Employee, Manager, Executive, Ceo]
1190
+ Employee.dataset = Employee.dataset.with_fetch([{:kind=>nil},{:kind=>0},{:kind=>1}, {:kind=>2}, {:kind=>3}, {:kind=>4}])
1191
+ Employee.all.collect{|x| x.class}.must_equal [Employee, Employee, Manager, Executive, Ceo, Intern]
1190
1192
  Manager.dataset = Manager.dataset.with_fetch([{:kind=>nil},{:kind=>0},{:kind=>1}, {:kind=>2}, {:kind=>3}])
1191
1193
  Manager.all.collect{|x| x.class}.must_equal [Manager, Employee, Manager, Executive, Ceo]
1192
1194
  end
@@ -1204,6 +1206,11 @@ describe "class_table_inheritance plugin with :alias option" do
1204
1206
  @db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Employee')"]
1205
1207
  end
1206
1208
 
1209
+ it "should sets the model class name for the key when creating new class records for subclass without separate table" do
1210
+ Intern.create
1211
+ @db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Intern')"]
1212
+ end
1213
+
1207
1214
  it "should sets the model class name for the key when creating new subclass records" do
1208
1215
  Ceo.create
1209
1216
  @db.sqls.must_equal ["INSERT INTO employees (kind) VALUES ('Ceo')",
@@ -1278,10 +1285,11 @@ describe "class_table_inheritance plugin with :alias option" do
1278
1285
  Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer})
1279
1286
  Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer}, :num_managers=>{:type=>:integer})
1280
1287
  Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :manager_id=>{:type=>:integer})
1288
+ Intern.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string})
1281
1289
  end
1282
1290
 
1283
1291
  it "should use the correct primary key (which should have the same name in all subclasses)" do
1284
- [Employee, Manager, Executive, Ceo, Staff].each{|c| c.primary_key.must_equal :id}
1292
+ [Employee, Manager, Executive, Ceo, Staff, Intern].each{|c| c.primary_key.must_equal :id}
1285
1293
  end
1286
1294
 
1287
1295
  it "should have table_name return the table name of the most specific table" do
@@ -1290,20 +1298,43 @@ describe "class_table_inheritance plugin with :alias option" do
1290
1298
  Executive.table_name.must_equal :employees
1291
1299
  Ceo.table_name.must_equal :employees
1292
1300
  Staff.table_name.must_equal :employees
1301
+ Intern.table_name.must_equal :employees
1293
1302
  end
1294
1303
 
1295
1304
  it "should delete the correct rows from all tables when deleting" do
1305
+ Employee.load(:id=>1).delete
1306
+ @db.sqls.must_equal ["DELETE FROM employees WHERE (id = 1)"]
1307
+
1308
+ Intern.load(:id=>1).delete
1309
+ @db.sqls.must_equal ["DELETE FROM employees WHERE (id = 1)"]
1310
+
1296
1311
  Ceo.load(:id=>1).delete
1297
1312
  @db.sqls.must_equal ["DELETE FROM executives WHERE (id = 1)", "DELETE FROM managers WHERE (id = 1)", "DELETE FROM employees WHERE (id = 1)"]
1298
1313
  end
1299
1314
 
1300
1315
  it "should not allow deletion of frozen object" do
1301
- o = Ceo.load(:id=>1)
1302
- o.freeze
1303
- proc{o.delete}.must_raise(Sequel::Error)
1304
- @db.sqls.must_equal []
1316
+ [Ceo, Executive, Employee, Manager, Intern].each do |c|
1317
+ o = c.load(:id=>1)
1318
+ o.freeze
1319
+ proc{o.delete}.must_raise(Sequel::Error)
1320
+ @db.sqls.must_equal []
1321
+ end
1305
1322
  end
1306
1323
 
1324
+ it "should insert the correct rows into all tables when inserting into parent class" do
1325
+ Employee.create(:name=>'E')
1326
+ sqls = @db.sqls
1327
+ sqls.length.must_equal 1
1328
+ sqls[0].must_match(/INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Employee)', '(E|Employee)'\)/)
1329
+ end
1330
+
1331
+ it "should insert the correct rows into all tables when inserting into subclass without separate table" do
1332
+ Intern.create(:name=>'E')
1333
+ sqls = @db.sqls
1334
+ sqls.length.must_equal 1
1335
+ sqls[0].must_match(/INSERT INTO employees \((name|kind), (name|kind)\) VALUES \('(E|Intern)', '(E|Intern)'\)/)
1336
+ end
1337
+
1307
1338
  it "should insert the correct rows into all tables when inserting" do
1308
1339
  Ceo.create(:num_managers=>3, :num_staff=>2, :name=>'E')
1309
1340
  sqls = @db.sqls
@@ -1342,6 +1373,16 @@ describe "class_table_inheritance plugin with :alias option" do
1342
1373
  sqls[2].must_match(/INSERT INTO executives \((num_managers|id), (num_managers|id)\) VALUES \([23], [23]\)/)
1343
1374
  end
1344
1375
 
1376
+ it "should update the correct rows in all tables when updating parent class" do
1377
+ Employee.load(:id=>2).update(:name=>'E')
1378
+ @db.sqls.must_equal ["UPDATE employees SET name = 'E' WHERE (id = 2)"]
1379
+ end
1380
+
1381
+ it "should update the correct rows in all tables when updating subclass without separate table" do
1382
+ Intern.load(:id=>2).update(:name=>'E')
1383
+ @db.sqls.must_equal ["UPDATE employees SET name = 'E' WHERE (id = 2)"]
1384
+ end
1385
+
1345
1386
  it "should update the correct rows in all tables when updating" do
1346
1387
  Ceo.load(:id=>2).update(:num_managers=>3, :num_staff=>2, :name=>'E')
1347
1388
  @db.sqls.must_equal ["UPDATE employees SET name = 'E' WHERE (id = 2)", "UPDATE managers SET num_staff = 2 WHERE (id = 2)", "UPDATE executives SET num_managers = 3 WHERE (id = 2)"]
@@ -44,6 +44,19 @@ describe "Composition plugin" do
44
44
  o.valid?.must_equal true
45
45
  end
46
46
 
47
+ it "should have decomposer work with column_conflicts plugin" do
48
+ @c.plugin :column_conflicts
49
+ @c.set_column_conflict! :year
50
+ @c.composition :date, :mapping=>[:year, :month, :day]
51
+ o = @c.new
52
+ def o.validate
53
+ [:year, :month, :day].each{|c| errors.add(c, "not present") unless send(c)}
54
+ end
55
+ o.valid?.must_equal false
56
+ o.date = Date.new(1, 2, 3)
57
+ o.valid?.must_equal true
58
+ end
59
+
47
60
  it "should set column values even when not validating" do
48
61
  @c.composition :date, :mapping=>[:year, :month, :day]
49
62
  @c.load({}).set(:date=>Date.new(4, 8, 12)).save(:validate=>false)
@@ -13,14 +13,17 @@ describe "Sequel::Plugins::DatasetAssociations" do
13
13
  @Artist = Class.new(@Base)
14
14
  @Album = Class.new(@Base)
15
15
  @Tag = Class.new(@Base)
16
+ @Track = Class.new(@Base)
16
17
 
17
18
  def @Artist.name; 'Artist' end
18
19
  def @Album.name; 'Album' end
19
20
  def @Tag.name; 'Tag' end
21
+ def @Track.name; 'Track' end
20
22
 
21
23
  @Artist.dataset = @db[:artists]
22
24
  @Album.dataset = @db[:albums]
23
25
  @Tag.dataset = @db[:tags]
26
+ @Track.dataset = @db[:tracks]
24
27
 
25
28
  @Artist.columns :id, :name
26
29
  @Album.columns :id, :name, :artist_id
@@ -28,6 +31,7 @@ describe "Sequel::Plugins::DatasetAssociations" do
28
31
 
29
32
  @Album.plugin :many_through_many
30
33
  @Artist.plugin :many_through_many
34
+ @Track.plugin :many_through_many
31
35
  @Artist.plugin :pg_array_associations
32
36
  @Tag.plugin :pg_array_associations
33
37
  @Artist.one_to_many :albums, :class=>@Album
@@ -41,6 +45,7 @@ describe "Sequel::Plugins::DatasetAssociations" do
41
45
  @Tag.many_to_pg_array :artists, :class=>@Artist
42
46
  @Artist.many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]], :class=>@Tag
43
47
  @Artist.one_through_many :otag, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]], :class=>@Tag
48
+ @Track.many_through_many :artist_tracks, [[:albums, :id, :artist_id], [:albums, :artist_id, :id]], :class=>@Track, :left_primary_key=>:album_id, :right_primary_key=>:album_id
44
49
  end
45
50
 
46
51
  it "should work for many_to_one associations" do
@@ -85,6 +90,13 @@ describe "Sequel::Plugins::DatasetAssociations" do
85
90
  ds.sql.must_equal "SELECT tags.* FROM tags WHERE (tags.id IN (SELECT albums_tags.tag_id FROM artists INNER JOIN albums ON (albums.artist_id = artists.id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) INNER JOIN tags ON (tags.id = albums_tags.tag_id) WHERE (albums.artist_id IN (SELECT artists.id FROM artists))))"
86
91
  end
87
92
 
93
+ it "should work for self referential many_through_many associations" do
94
+ ds = @Track.artist_tracks
95
+ ds.must_be_kind_of(Sequel::Dataset)
96
+ ds.model.must_equal @Track
97
+ ds.sql.must_equal "SELECT tracks.* FROM tracks WHERE (tracks.album_id IN (SELECT albums_0.id FROM tracks INNER JOIN albums ON (albums.id = tracks.album_id) INNER JOIN albums AS albums_0 ON (albums_0.artist_id = albums.artist_id) INNER JOIN tracks AS tracks_0 ON (tracks_0.album_id = albums_0.id) WHERE (albums.id IN (SELECT tracks.album_id FROM tracks))))"
98
+ end
99
+
88
100
  it "should work for many_through_many associations with a single join table" do
89
101
  ds = @Album.mthm_tags
90
102
  ds.must_be_kind_of(Sequel::Dataset)
@@ -59,10 +59,10 @@ describe Sequel::Model, "many_through_many" do
59
59
  DB.sqls.must_equal []
60
60
  end
61
61
 
62
- it "should handle a :eager_loading_predicate_key option to change the SQL used in the lookup" do
62
+ it "should handle a :predicate_key option to change the SQL used in the lookup" do
63
63
  @c1.dataset = @c1.dataset.with_fetch(:id=>1)
64
64
  @c2.dataset = @c2.dataset.with_fetch(:id=>4, :x_foreign_key_x=>1)
65
- @c1.many_through_many :tags, :through=>[[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager_loading_predicate_key=>(Sequel[:albums_artists][:artist_id] / 3)
65
+ @c1.many_through_many :tags, :through=>[[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :predicate_key=>(Sequel[:albums_artists][:artist_id] / 3)
66
66
  a = @c1.eager(:tags).all
67
67
  a.must_equal [@c1.load(:id => 1)]
68
68
  DB.sqls.must_equal ['SELECT * FROM artists', "SELECT tags.*, (albums_artists.artist_id / 3) AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE ((albums_artists.artist_id / 3) IN (1))"]
@@ -1231,10 +1231,10 @@ describe Sequel::Model, "one_through_many" do
1231
1231
  DB.sqls.must_equal []
1232
1232
  end
1233
1233
 
1234
- it "should handle a :eager_loading_predicate_key option to change the SQL used in the lookup" do
1234
+ it "should handle a :predicate_key option to change the SQL used in the lookup" do
1235
1235
  @c1.dataset = @c1.dataset.with_fetch(:id=>1)
1236
1236
  @c2.dataset = @c2.dataset.with_fetch(:id=>4, :x_foreign_key_x=>1)
1237
- @c1.one_through_many :tag, :through=>[[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager_loading_predicate_key=>(Sequel[:albums_artists][:artist_id] / 3)
1237
+ @c1.one_through_many :tag, :through=>[[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :predicate_key=>(Sequel[:albums_artists][:artist_id] / 3)
1238
1238
  a = @c1.eager(:tag).all
1239
1239
  a.must_equal [@c1.load(:id => 1)]
1240
1240
  DB.sqls.must_equal ['SELECT * FROM artists', "SELECT tags.*, (albums_artists.artist_id / 3) AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE ((albums_artists.artist_id / 3) IN (1))"]