sequel 5.18.0 → 5.20.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +40 -0
  3. data/doc/opening_databases.rdoc +5 -2
  4. data/doc/release_notes/5.19.0.txt +28 -0
  5. data/doc/release_notes/5.20.0.txt +89 -0
  6. data/doc/sharding.rdoc +12 -0
  7. data/doc/transactions.rdoc +38 -0
  8. data/lib/sequel/adapters/jdbc.rb +7 -2
  9. data/lib/sequel/adapters/mysql2.rb +2 -2
  10. data/lib/sequel/adapters/shared/postgres.rb +8 -8
  11. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  12. data/lib/sequel/adapters/sqlanywhere.rb +33 -17
  13. data/lib/sequel/adapters/sqlite.rb +20 -13
  14. data/lib/sequel/connection_pool.rb +0 -5
  15. data/lib/sequel/database/misc.rb +10 -9
  16. data/lib/sequel/database/query.rb +1 -1
  17. data/lib/sequel/database/schema_generator.rb +1 -1
  18. data/lib/sequel/database/transactions.rb +57 -5
  19. data/lib/sequel/dataset/actions.rb +6 -5
  20. data/lib/sequel/dataset/graph.rb +2 -2
  21. data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
  22. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  23. data/lib/sequel/dataset/query.rb +1 -1
  24. data/lib/sequel/extensions/constraint_validations.rb +14 -0
  25. data/lib/sequel/extensions/pg_enum.rb +23 -15
  26. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  27. data/lib/sequel/model/associations.rb +38 -12
  28. data/lib/sequel/model/base.rb +1 -1
  29. data/lib/sequel/model/plugins.rb +104 -0
  30. data/lib/sequel/plugins/association_dependencies.rb +3 -3
  31. data/lib/sequel/plugins/association_pks.rb +14 -4
  32. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  33. data/lib/sequel/plugins/composition.rb +13 -9
  34. data/lib/sequel/plugins/finder.rb +2 -2
  35. data/lib/sequel/plugins/hook_class_methods.rb +17 -5
  36. data/lib/sequel/plugins/inverted_subsets.rb +2 -2
  37. data/lib/sequel/plugins/json_serializer.rb +3 -3
  38. data/lib/sequel/plugins/nested_attributes.rb +1 -1
  39. data/lib/sequel/plugins/pg_array_associations.rb +8 -4
  40. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +61 -32
  41. data/lib/sequel/plugins/prepared_statements.rb +1 -1
  42. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -1
  43. data/lib/sequel/plugins/subset_conditions.rb +2 -2
  44. data/lib/sequel/plugins/validation_class_methods.rb +5 -3
  45. data/lib/sequel/plugins/validation_helpers.rb +2 -2
  46. data/lib/sequel/sql.rb +1 -1
  47. data/lib/sequel/version.rb +1 -1
  48. data/spec/adapters/postgres_spec.rb +40 -0
  49. data/spec/core/database_spec.rb +73 -2
  50. data/spec/core/schema_spec.rb +7 -1
  51. data/spec/extensions/class_table_inheritance_spec.rb +30 -8
  52. data/spec/extensions/constraint_validations_spec.rb +20 -2
  53. data/spec/extensions/core_refinements_spec.rb +1 -1
  54. data/spec/extensions/hook_class_methods_spec.rb +22 -0
  55. data/spec/extensions/migration_spec.rb +13 -0
  56. data/spec/extensions/pg_auto_constraint_validations_spec.rb +8 -0
  57. data/spec/extensions/pg_enum_spec.rb +5 -0
  58. data/spec/extensions/s_spec.rb +1 -1
  59. data/spec/extensions/schema_dumper_spec.rb +4 -2
  60. data/spec/integration/plugin_test.rb +15 -0
  61. data/spec/integration/transaction_test.rb +50 -0
  62. data/spec/model/associations_spec.rb +84 -4
  63. data/spec/model/plugins_spec.rb +111 -0
  64. metadata +7 -3
@@ -149,7 +149,7 @@ module Sequel
149
149
  # Use a prepared statement to update this model's columns in the database.
150
150
  def _update_without_checking(columns)
151
151
  if use_prepared_statements_for?(:update)
152
- _set_prepared_statement_server(model.send(:prepared_update, columns.keys)).call(Hash[columns].merge!(pk_hash))
152
+ _set_prepared_statement_server(model.send(:prepared_update, columns.keys)).call(columns.merge(pk_hash))
153
153
  else
154
154
  super
155
155
  end
@@ -67,7 +67,7 @@ module Sequel
67
67
  # of free columns.
68
68
  def before_create
69
69
  if v = model.prepared_statements_column_defaults
70
- @values = Hash[v].merge!(values)
70
+ @values = v.merge(values)
71
71
  end
72
72
  super
73
73
  end
@@ -26,8 +26,8 @@ module Sequel
26
26
  # Album.where(Album.published_conditions | {ready: true}).sql
27
27
  # # SELECT * FROM albums WHERE ((published IS TRUE) OR (ready IS TRUE))
28
28
  module SubsetConditions
29
- def self.apply(mod, &block)
30
- mod.instance_exec do
29
+ def self.apply(model, &block)
30
+ model.instance_exec do
31
31
  @dataset_module_class = Class.new(@dataset_module_class) do
32
32
  include DatasetModuleMethods
33
33
  end
@@ -188,13 +188,17 @@ module Sequel
188
188
  # Sequel will attempt to insert a NULL value into the database, instead of using the
189
189
  # database's default.
190
190
  # :allow_nil :: Whether to skip the validation if the value is nil.
191
- # :if :: A symbol (indicating an instance_method) or proc (which is instance_execed)
191
+ # :if :: A symbol (indicating an instance_method) or proc (which is used to define an instance method)
192
192
  # skipping this validation if it returns nil or false.
193
193
  # :tag :: The tag to use for this validation.
194
194
  def validates_each(*atts, &block)
195
195
  opts = extract_options!(atts)
196
196
  blank_meth = db.method(:blank_object?).to_proc
197
197
  blk = if (i = opts[:if]) || (am = opts[:allow_missing]) || (an = opts[:allow_nil]) || (ab = opts[:allow_blank])
198
+ if i.is_a?(Proc)
199
+ i = Plugins.def_sequel_method(self, "validation_class_methods_if", 0, &i)
200
+ end
201
+
198
202
  proc do |o,a,v|
199
203
  next if i && !validation_if_proc(o, i)
200
204
  next if an && Array(v).all?(&:nil?)
@@ -434,8 +438,6 @@ module Sequel
434
438
  case i
435
439
  when Symbol
436
440
  o.get_column_value(i)
437
- when Proc
438
- o.instance_exec(&i)
439
441
  else
440
442
  raise(::Sequel::Error, "invalid value for :if validation option")
441
443
  end
@@ -244,7 +244,7 @@ module Sequel
244
244
  def validates_unique(*atts)
245
245
  opts = default_validation_helpers_options(:unique)
246
246
  if atts.last.is_a?(Hash)
247
- opts = Hash[opts].merge!(atts.pop)
247
+ opts = opts.merge(atts.pop)
248
248
  end
249
249
  message = validation_error_message(opts[:message])
250
250
  from_values = opts[:from] == :values
@@ -302,7 +302,7 @@ module Sequel
302
302
  # Merge the given options with the default options for the given type
303
303
  # and call validatable_attributes with the merged options.
304
304
  def validatable_attributes_for_type(type, atts, opts, &block)
305
- validatable_attributes(atts, Hash[default_validation_helpers_options(type)].merge!(opts), &block)
305
+ validatable_attributes(atts, default_validation_helpers_options(type).merge(opts), &block)
306
306
  end
307
307
 
308
308
  # The validation error message to use, as a string. If message
@@ -1472,7 +1472,7 @@ module Sequel
1472
1472
 
1473
1473
  # Return a new function call with the given opts merged into the current opts.
1474
1474
  def with_opts(opts)
1475
- self.class.new!(name, args, Hash[@opts].merge!(opts))
1475
+ self.class.new!(name, args, @opts.merge(opts))
1476
1476
  end
1477
1477
  end
1478
1478
 
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 18
9
+ MINOR = 20
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
@@ -104,6 +104,24 @@ describe "PostgreSQL", '#create_table' do
104
104
  @db.check_constraints(:tmp_dolls).must_equal(:ic=>{:definition=>"CHECK ((i > 2))", :columns=>[:i]}, :jc=>{:definition=>"CHECK ((j > 2))", :columns=>[:j]}, :ijc=>{:definition=>"CHECK (((i - j) > 2))", :columns=>[:i, :j]})
105
105
  end
106
106
 
107
+ it "should have #check_constraints return check constraints where columns are unknown" do
108
+ begin
109
+ @db.create_table(:tmp_dolls) do
110
+ Integer :i
111
+ Integer :j
112
+ end
113
+ @db.run "CREATE OR REPLACE FUNCTION valid_tmp_dolls(t1 tmp_dolls) RETURNS boolean AS 'SELECT false' LANGUAGE SQL;"
114
+ @db.alter_table(:tmp_dolls) do
115
+ add_constraint(:valid_tmp_dolls, Sequel.function(:valid_tmp_dolls, :tmp_dolls))
116
+ end
117
+
118
+ @db.check_constraints(:tmp_dolls).must_equal(:valid_tmp_dolls=>{:definition=>"CHECK (valid_tmp_dolls(tmp_dolls.*))", :columns=>[]})
119
+ ensure
120
+ @db.run "ALTER TABLE tmp_dolls DROP CONSTRAINT IF EXISTS valid_tmp_dolls"
121
+ @db.run "DROP FUNCTION IF EXISTS valid_tmp_dolls(tmp_dolls)"
122
+ end
123
+ end if DB.server_version >= 90000
124
+
107
125
  it "should not allow to pass both :temp and :unlogged" do
108
126
  proc do
109
127
  @db.create_table(:temp_unlogged_dolls, :temp => true, :unlogged => true){text :name}
@@ -3930,6 +3948,14 @@ describe 'PostgreSQL enum types' do
3930
3948
  @db.schema(:test_enumt, :reload=>true).first.last[:enum_values].must_equal @initial_enum_values
3931
3949
  @db.rename_enum(:new_enum, :test_enum)
3932
3950
  end
3951
+
3952
+ it "should rename enum values" do
3953
+ @db.rename_enum_value(:test_enum, :b, :x)
3954
+ new_enum_values = @initial_enum_values
3955
+ new_enum_values[new_enum_values.index('b')] = 'x'
3956
+ @db.schema(:test_enumt, :reload=>true).first.last[:enum_values].must_equal new_enum_values
3957
+ @db.rename_enum_value(:test_enum, :x, :b)
3958
+ end if DB.server_version >= 100000
3933
3959
  end
3934
3960
 
3935
3961
  describe "PostgreSQL stored procedures for datasets" do
@@ -3980,6 +4006,10 @@ describe "pg_auto_constraint_validations plugin" do
3980
4006
  constraint :valid_i, Sequel[:i] < 10
3981
4007
  constraint(:valid_i_id, Sequel[:i] + Sequel[:id] < 20)
3982
4008
  end
4009
+ @db.run "CREATE OR REPLACE FUNCTION valid_test1(t1 test1) RETURNS boolean AS 'SELECT t1.i != -100' LANGUAGE SQL;"
4010
+ @db.alter_table(:test1) do
4011
+ add_constraint(:valid_test1, Sequel.function(:valid_test1, :test1))
4012
+ end
3983
4013
  @db.create_table!(:test2) do
3984
4014
  Integer :test2_id, :primary_key=>true
3985
4015
  foreign_key :test1_id, :test1
@@ -4000,6 +4030,8 @@ describe "pg_auto_constraint_validations plugin" do
4000
4030
  @c2.insert(:test2_id=>3, :test1_id=>1)
4001
4031
  end
4002
4032
  after(:all) do
4033
+ @db.run "ALTER TABLE test1 DROP CONSTRAINT IF EXISTS valid_test1"
4034
+ @db.run "DROP FUNCTION IF EXISTS valid_test1(test1)"
4003
4035
  @db.drop_table?(:test2, :test1)
4004
4036
  end
4005
4037
 
@@ -4009,6 +4041,14 @@ describe "pg_auto_constraint_validations plugin" do
4009
4041
  o.errors.must_equal(:i=>['is invalid'])
4010
4042
  end
4011
4043
 
4044
+ it "should handle check constraint failures where the columns are unknown, if columns are explicitly specified" do
4045
+ o = @c1.new(:id=>5, :i=>-100)
4046
+ proc{o.save}.must_raise Sequel::CheckConstraintViolation
4047
+ @c1.pg_auto_constraint_validation_override(:valid_test1, :i, "should not be -100")
4048
+ proc{o.save}.must_raise Sequel::ValidationFailed
4049
+ o.errors.must_equal(:i=>['should not be -100'])
4050
+ end
4051
+
4012
4052
  it "should handle check constraint failures as validation errors when updating" do
4013
4053
  o = @c1.new(:id=>5, :i=>3)
4014
4054
  o.save
@@ -1140,15 +1140,86 @@ describe "Database#transaction with savepoint support" do
1140
1140
  end
1141
1141
 
1142
1142
  it "should support after_rollback inside savepoints" do
1143
- @db.transaction do
1143
+ @db.transaction(:rollback=>:always) do
1144
1144
  @db.after_rollback{@db.execute('foo')}
1145
1145
  @db.transaction(:savepoint=>true){@db.after_rollback{@db.execute('bar')}}
1146
1146
  @db.after_rollback{@db.execute('baz')}
1147
- raise Sequel::Rollback
1148
1147
  end
1149
1148
  @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'RELEASE SAVEPOINT autopoint_1', 'ROLLBACK', 'foo', 'bar', 'baz']
1150
1149
  end
1151
1150
 
1151
+ it "should run after_commit if savepoint rolled back" do
1152
+ @db.transaction do
1153
+ @db.after_commit{@db.execute('foo')}
1154
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.after_commit{@db.execute('bar')}}
1155
+ end
1156
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'COMMIT', 'foo', 'bar']
1157
+ end
1158
+
1159
+ it "should not run after_commit if savepoint rolled back and :savepoint option used" do
1160
+ @db.transaction do
1161
+ @db.after_commit{@db.execute('foo')}
1162
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.after_commit(:savepoint=>true){@db.execute('bar')}}
1163
+ end
1164
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'COMMIT', 'foo']
1165
+ end
1166
+
1167
+ it "should not run after_commit if higher-level savepoint rolled back and :savepoint option used" do
1168
+ @db.transaction do
1169
+ @db.after_commit{@db.execute('foo')}
1170
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.transaction(:savepoint=>true){@db.after_commit(:savepoint=>true){@db.execute('bar')}}}
1171
+ end
1172
+ @db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "ROLLBACK TO SAVEPOINT autopoint_1", "COMMIT", "foo"]
1173
+ end
1174
+
1175
+ it "should not run after_commit if transaction rolled back and :savepoint option used" do
1176
+ @db.transaction(:rollback=>:always) do
1177
+ @db.after_commit{@db.execute('foo')}
1178
+ @db.transaction(:savepoint=>true){@db.transaction(:savepoint=>true){@db.after_commit(:savepoint=>true){@db.execute('bar')}}}
1179
+ end
1180
+ @db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_1", "ROLLBACK"]
1181
+ end
1182
+
1183
+ it "should run after_rollback if savepoint rolls back" do
1184
+ @db.transaction(:rollback=>:always) do
1185
+ @db.after_rollback{@db.execute('foo')}
1186
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback{@db.execute('bar')}}
1187
+ @db.after_rollback{@db.execute('baz')}
1188
+ end
1189
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK', 'foo', 'bar', 'baz']
1190
+ end
1191
+
1192
+ it "should run after_rollback when savepoint rolls back if :savepoint option used" do
1193
+ @db.transaction(:rollback=>:always) do
1194
+ @db.after_rollback{@db.execute('foo')}
1195
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}
1196
+ @db.after_rollback{@db.execute('baz')}
1197
+ end
1198
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'bar', 'ROLLBACK', 'foo', 'baz']
1199
+ end
1200
+
1201
+ it "should run after_rollback if savepoint rolled back and :savepoint option used, even if transaction commits" do
1202
+ @db.transaction do
1203
+ @db.after_commit{@db.execute('foo')}
1204
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}
1205
+ end
1206
+ @db.sqls.must_equal ['BEGIN', 'SAVEPOINT autopoint_1', 'ROLLBACK TO SAVEPOINT autopoint_1', 'bar', 'COMMIT', 'foo']
1207
+ end
1208
+
1209
+ it "should run after_rollback if higher-level savepoint rolled back and :savepoint option used" do
1210
+ @db.transaction do
1211
+ @db.transaction(:savepoint=>true, :rollback=>:always){@db.transaction(:savepoint=>true){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}}
1212
+ end
1213
+ @db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "ROLLBACK TO SAVEPOINT autopoint_1", "bar", "COMMIT"]
1214
+ end
1215
+
1216
+ it "should run after_rollback if transaction rolled back and :savepoint option used" do
1217
+ @db.transaction(:rollback=>:always) do
1218
+ @db.transaction(:savepoint=>true){@db.transaction(:savepoint=>true){@db.after_rollback(:savepoint=>true){@db.execute('bar')}}}
1219
+ end
1220
+ @db.sqls.must_equal ["BEGIN", "SAVEPOINT autopoint_1", "SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_2", "RELEASE SAVEPOINT autopoint_1", "ROLLBACK", "bar"]
1221
+ end
1222
+
1152
1223
  it "should raise an error if you attempt to use after_commit inside a savepoint in a prepared transaction" do
1153
1224
  @db.define_singleton_method(:supports_prepared_transactions?){true}
1154
1225
  proc{@db.transaction(:prepare=>'XYZ'){@db.transaction(:savepoint=>true){@db.after_commit{@db.execute('foo')}}}}.must_raise(Sequel::Error)
@@ -1679,7 +1679,7 @@ describe "Schema Parser" do
1679
1679
  sch = @db.schema(:x)
1680
1680
  sch.must_equal [[:a, {:db_type=>"x", :ruby_default=>nil}], [:b, {:db_type=>"x", :ruby_default=>nil}]]
1681
1681
  sch[0][1][:db_type].must_be_same_as(sch[1][1][:db_type])
1682
- end if RUBY_VERSION >= '2.5' && !defined?(JRUBY_VERSION)
1682
+ end if RUBY_VERSION >= '2.5'
1683
1683
 
1684
1684
  it "should set :auto_increment to true by default if unset and a single integer primary key is used" do
1685
1685
  @db.define_singleton_method(:schema_parse_table){|*| [[:a, {:primary_key=>true, :db_type=>'integer'}]]}
@@ -1777,17 +1777,21 @@ describe "Schema Parser" do
1777
1777
  @db.schema(:text).first.last[:type].must_equal :string
1778
1778
  @db.schema(:date).first.last[:type].must_equal :date
1779
1779
  @db.schema(:datetime).first.last[:type].must_equal :datetime
1780
+ @db.schema(:smalldatetime).first.last[:type].must_equal :datetime
1780
1781
  @db.schema(:timestamp).first.last[:type].must_equal :datetime
1781
1782
  @db.schema(:"timestamp with time zone").first.last[:type].must_equal :datetime
1782
1783
  @db.schema(:"timestamp without time zone").first.last[:type].must_equal :datetime
1783
1784
  @db.schema(:time).first.last[:type].must_equal :time
1784
1785
  @db.schema(:"time with time zone").first.last[:type].must_equal :time
1785
1786
  @db.schema(:"time without time zone").first.last[:type].must_equal :time
1787
+ @db.schema(:bool).first.last[:type].must_equal :boolean
1786
1788
  @db.schema(:boolean).first.last[:type].must_equal :boolean
1787
1789
  @db.schema(:real).first.last[:type].must_equal :float
1788
1790
  @db.schema(:float).first.last[:type].must_equal :float
1791
+ @db.schema(:"float unsigned").first.last[:type].must_equal :float
1789
1792
  @db.schema(:double).first.last[:type].must_equal :float
1790
1793
  @db.schema(:"double(1,2)").first.last[:type].must_equal :float
1794
+ @db.schema(:"double(1,2) unsigned").first.last[:type].must_equal :float
1791
1795
  @db.schema(:"double precision").first.last[:type].must_equal :float
1792
1796
  @db.schema(:number).first.last[:type].must_equal :decimal
1793
1797
  @db.schema(:numeric).first.last[:type].must_equal :decimal
@@ -1801,6 +1805,8 @@ describe "Schema Parser" do
1801
1805
  @db.schema(:nchar).first.last[:type].must_equal :string
1802
1806
  @db.schema(:nvarchar).first.last[:type].must_equal :string
1803
1807
  @db.schema(:ntext).first.last[:type].must_equal :string
1808
+ @db.schema(:clob).first.last[:type].must_equal :string
1809
+ @db.schema(:ntext).first.last[:type].must_equal :string
1804
1810
  @db.schema(:smalldatetime).first.last[:type].must_equal :datetime
1805
1811
  @db.schema(:binary).first.last[:type].must_equal :blob
1806
1812
  @db.schema(:varbinary).first.last[:type].must_equal :blob
@@ -5,8 +5,8 @@ describe "class_table_inheritance plugin" do
5
5
  @db = Sequel.mock(:numrows=>1, :autoid=>proc{|sql| 1})
6
6
  def @db.supports_schema_parsing?() true end
7
7
  def @db.schema(table, opts={})
8
- {:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]],
9
- :managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer}] ],
8
+ {:employees=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string, :allow_null=>false}], [:kind, {:type=>:string}]],
9
+ :managers=>[[:id, {:type=>:integer}], [:num_staff, {:type=>:integer, :allow_null=>false}] ],
10
10
  :executives=>[[:id, {:type=>:integer}], [:num_managers, {:type=>:integer}]],
11
11
  :staff=>[[:id, {:type=>:integer}], [:manager_id, {:type=>:integer}]],
12
12
  }[table.is_a?(Sequel::Dataset) ? table.first_source_table : table]
@@ -23,7 +23,9 @@ describe "class_table_inheritance plugin" do
23
23
  }[opts[:from] + (opts[:join] || []).map{|x| x.table}]
24
24
  end
25
25
  end
26
- class ::Employee < Sequel::Model(@db)
26
+ base = Sequel::Model(@db)
27
+ base.plugin :auto_validations if @use_auto_validations
28
+ class ::Employee < base
27
29
  def _save_refresh; @values[:id] = 1 end
28
30
  def self.columns
29
31
  dataset.columns || dataset.opts[:from].first.expression.columns
@@ -114,6 +116,26 @@ describe "class_table_inheritance plugin" do
114
116
  "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 WHERE (id = 1) LIMIT 1"]
115
117
  end
116
118
 
119
+ describe "with auto_validations plugin" do
120
+ before(:all) do
121
+ @use_auto_validations = true
122
+ end
123
+
124
+ it "should work" do
125
+ e = Employee.new
126
+ e.valid?.must_equal false
127
+ e.errors.must_equal(:name=>["is not present"])
128
+
129
+ e = Manager.new
130
+ e.valid?.must_equal false
131
+ e.errors.must_equal(:name=>["is not present"], :num_staff=>["is not present"])
132
+
133
+ e = Executive.new
134
+ e.valid?.must_equal false
135
+ e.errors.must_equal(:name=>["is not present"], :num_staff=>["is not present"])
136
+ end
137
+ end
138
+
117
139
  it "should return rows with the current class if sti_key is nil" do
118
140
  Employee.plugin :class_table_inheritance
119
141
  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]
@@ -232,11 +254,11 @@ describe "class_table_inheritance plugin" do
232
254
  end
233
255
 
234
256
  it "should include schema for columns for tables for ancestor classes" do
235
- Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string})
236
- Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer})
237
- 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})
238
- Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string}, :manager_id=>{:type=>:integer})
239
- Intern.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string}, :kind=>{:type=>:string})
257
+ Employee.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string})
258
+ Manager.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer, :allow_null=>false})
259
+ Executive.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string}, :num_staff=>{:type=>:integer, :allow_null=>false}, :num_managers=>{:type=>:integer})
260
+ Staff.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string}, :manager_id=>{:type=>:integer})
261
+ Intern.db_schema.must_equal(:id=>{:primary_key=>true, :type=>:integer}, :name=>{:type=>:string, :allow_null=>false}, :kind=>{:type=>:string})
240
262
  end
241
263
 
242
264
  it "should use the correct primary key (which should have the same name in all subclasses)" do
@@ -20,6 +20,7 @@ describe "constraint_validations extension" do
20
20
  before do
21
21
  @db = Sequel.mock
22
22
  @db.extend(Module.new{attr_writer :schema; def schema(table, *) execute("parse schema for #{table}"); @schema; end})
23
+ def @db.table_exists?(_) true; end
23
24
  @db.extension(:constraint_validations)
24
25
  end
25
26
 
@@ -36,13 +37,13 @@ describe "constraint_validations extension" do
36
37
 
37
38
  it "should allow dropping the sequel_constraint_validations table" do
38
39
  @db.drop_constraint_validations_table
39
- @db.sqls.must_equal ["DROP TABLE sequel_constraint_validations"]
40
+ @db.sqls.must_equal ["DELETE FROM sequel_constraint_validations WHERE (table = 'sequel_constraint_validations')", "DROP TABLE sequel_constraint_validations"]
40
41
  end
41
42
 
42
43
  it "should allow dropping the sequel_constraint_validations table with a non-default table name" do
43
44
  @db.constraint_validations_table = :foo
44
45
  @db.drop_constraint_validations_table
45
- @db.sqls.must_equal ["DROP TABLE foo"]
46
+ @db.sqls.must_equal ["DELETE FROM foo WHERE (table = 'foo')", "DROP TABLE foo"]
46
47
  end
47
48
 
48
49
  it "should allow dropping validations for a given table" do
@@ -50,6 +51,23 @@ describe "constraint_validations extension" do
50
51
  @db.sqls.must_equal ["DELETE FROM sequel_constraint_validations WHERE (table = 'foo')"]
51
52
  end
52
53
 
54
+ it "should drop validations for a given table when dropping the table" do
55
+ @db.drop_table(:foo)
56
+ @db.sqls.must_equal ["DELETE FROM sequel_constraint_validations WHERE (table = 'foo')", "DROP TABLE foo"]
57
+
58
+ @db.drop_table(:foo, :if_exists => true)
59
+ @db.sqls.must_equal ["DELETE FROM sequel_constraint_validations WHERE (table = 'foo')", "DROP TABLE IF EXISTS foo"]
60
+
61
+ @db.drop_table?(:foo)
62
+ @db.sqls.must_equal ["DELETE FROM sequel_constraint_validations WHERE (table = 'foo')", "DROP TABLE foo"]
63
+ end
64
+
65
+ it "should not drop validations for a given table if the constraint validations table does not exist" do
66
+ def @db.table_exists?(_) false; end
67
+ @db.drop_table(:foo)
68
+ @db.sqls.must_equal ["DROP TABLE foo"]
69
+ end
70
+
53
71
  it "should allow dropping validations for a given table and column" do
54
72
  @db.drop_constraint_validations_for(:table=>:foo, :column=>:bar)
55
73
  @db.sqls.must_equal ["DELETE FROM sequel_constraint_validations WHERE ((table = 'foo') AND (column = 'bar'))"]
@@ -1,6 +1,6 @@
1
1
  require_relative "spec_helper"
2
2
 
3
- if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby') # || (RUBY_VERSION >= '2.3.0' && RUBY_ENGINE == 'jruby')
3
+ if (RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby') || (RUBY_ENGINE == 'jruby' && (JRUBY_VERSION >= '9.3' || (JRUBY_VERSION.match(/\A9\.2\.(\d+)/) && $1.to_i >= 7)))
4
4
  Sequel.extension :core_refinements, :pg_array, :pg_hstore, :pg_row, :pg_range, :pg_row_ops, :pg_range_ops, :pg_array_ops, :pg_hstore_ops, :pg_json, :pg_json_ops
5
5
  using Sequel::CoreRefinements
6
6
 
@@ -22,6 +22,28 @@ describe Sequel::Model, "hook_class_methods plugin" do
22
22
  hooks.values.all?(&:frozen?).must_equal true
23
23
  end
24
24
 
25
+ deprecated ".hook_blocks method should yield each hook block" do
26
+ c = model_class.call Sequel::Model
27
+ a = []
28
+ c.hook_blocks(:before_save){|b| a << b}
29
+ a.must_equal []
30
+
31
+ pr = proc{adds << 'hi'}
32
+ c.before_save(&pr)
33
+ a = []
34
+ c.hook_blocks(:before_save){|b| a << b}
35
+ a.must_equal [pr]
36
+
37
+ c.before_save(&pr)
38
+ a = []
39
+ c.hook_blocks(:before_save){|b| a << b}
40
+ a.must_equal [pr, pr]
41
+
42
+ a = []
43
+ c.hook_blocks(:after_save){|b| a << b}
44
+ a.must_equal []
45
+ end
46
+
25
47
  it "should be definable using a block" do
26
48
  adds = []
27
49
  c = model_class.call Sequel::Model do