sequel 3.38.0 → 3.39.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.
- data/CHANGELOG +62 -0
- data/README.rdoc +2 -2
- data/bin/sequel +12 -2
- data/doc/advanced_associations.rdoc +1 -1
- data/doc/association_basics.rdoc +13 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/schema_modification.rdoc +4 -4
- data/lib/sequel/adapters/jdbc/derby.rb +1 -0
- data/lib/sequel/adapters/mock.rb +5 -0
- data/lib/sequel/adapters/mysql.rb +8 -1
- data/lib/sequel/adapters/mysql2.rb +10 -3
- data/lib/sequel/adapters/postgres.rb +72 -8
- data/lib/sequel/adapters/shared/db2.rb +1 -0
- data/lib/sequel/adapters/shared/mssql.rb +57 -0
- data/lib/sequel/adapters/shared/mysql.rb +95 -19
- data/lib/sequel/adapters/shared/oracle.rb +14 -0
- data/lib/sequel/adapters/shared/postgres.rb +63 -24
- data/lib/sequel/adapters/shared/sqlite.rb +6 -9
- data/lib/sequel/connection_pool/sharded_threaded.rb +8 -3
- data/lib/sequel/connection_pool/threaded.rb +9 -4
- data/lib/sequel/database/query.rb +60 -48
- data/lib/sequel/database/schema_generator.rb +13 -6
- data/lib/sequel/database/schema_methods.rb +65 -12
- data/lib/sequel/dataset/actions.rb +22 -4
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/graph.rb +2 -3
- data/lib/sequel/dataset/misc.rb +2 -2
- data/lib/sequel/dataset/query.rb +0 -2
- data/lib/sequel/dataset/sql.rb +33 -12
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/extensions/eval_inspect.rb +17 -2
- data/lib/sequel/extensions/pg_array_ops.rb +15 -5
- data/lib/sequel/extensions/pg_interval.rb +2 -2
- data/lib/sequel/extensions/pg_row_ops.rb +18 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -11
- data/lib/sequel/model/associations.rb +3 -2
- data/lib/sequel/model/base.rb +57 -13
- data/lib/sequel/model/exceptions.rb +20 -2
- data/lib/sequel/plugins/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/defaults_setter.rb +15 -1
- data/lib/sequel/plugins/dirty.rb +2 -2
- data/lib/sequel/plugins/identity_map.rb +12 -8
- data/lib/sequel/plugins/subclasses.rb +19 -1
- data/lib/sequel/plugins/tree.rb +3 -3
- data/lib/sequel/plugins/validation_helpers.rb +24 -4
- data/lib/sequel/sql.rb +64 -24
- data/lib/sequel/timezones.rb +10 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +26 -25
- data/spec/adapters/mysql_spec.rb +57 -23
- data/spec/adapters/oracle_spec.rb +34 -49
- data/spec/adapters/postgres_spec.rb +226 -128
- data/spec/adapters/sqlite_spec.rb +50 -49
- data/spec/core/connection_pool_spec.rb +22 -0
- data/spec/core/database_spec.rb +53 -47
- data/spec/core/dataset_spec.rb +36 -32
- data/spec/core/expression_filters_spec.rb +14 -2
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/core/object_graph_spec.rb +0 -13
- data/spec/core/schema_spec.rb +64 -5
- data/spec/core_extensions_spec.rb +1 -0
- data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/defaults_setter_spec.rb +24 -0
- data/spec/extensions/eval_inspect_spec.rb +9 -0
- data/spec/extensions/identity_map_spec.rb +11 -2
- data/spec/extensions/pg_array_ops_spec.rb +9 -0
- data/spec/extensions/pg_row_ops_spec.rb +11 -1
- data/spec/extensions/pg_row_plugin_spec.rb +4 -0
- data/spec/extensions/schema_dumper_spec.rb +8 -5
- data/spec/extensions/subclasses_spec.rb +14 -0
- data/spec/extensions/validation_helpers_spec.rb +15 -2
- data/spec/integration/dataset_test.rb +75 -1
- data/spec/integration/plugin_test.rb +146 -0
- data/spec/integration/schema_test.rb +34 -0
- data/spec/model/dataset_methods_spec.rb +38 -0
- data/spec/model/hooks_spec.rb +6 -0
- data/spec/model/validations_spec.rb +27 -2
- metadata +8 -2
|
@@ -60,10 +60,12 @@ describe "Blockless Ruby Filters" do
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
it "should support ~ via Hash and Regexp (if supported by database)" do
|
|
63
|
+
def @d.supports_regexp?; true end
|
|
63
64
|
@d.l(:x => /blah/).should == '(x ~ \'blah\')'
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
it "should support !~ via inverted Hash and Regexp" do
|
|
68
|
+
def @d.supports_regexp?; true end
|
|
67
69
|
@d.l(~Sequel.expr(:x => /blah/)).should == '(x !~ \'blah\')'
|
|
68
70
|
end
|
|
69
71
|
|
|
@@ -141,7 +143,7 @@ describe "Blockless Ruby Filters" do
|
|
|
141
143
|
@d.l(Sequel.expr(Sequel.lit('y') => Sequel.lit('z')) & Sequel.lit('x')).should == '((y = z) AND x)'
|
|
142
144
|
@d.l((Sequel.lit('x') > 200) & (Sequel.lit('y') < 200)).should == '((x > 200) AND (y < 200))'
|
|
143
145
|
@d.l(~(Sequel.lit('x') + 1 > 100)).should == '((x + 1) <= 100)'
|
|
144
|
-
@d.l(Sequel.lit('x').like(
|
|
146
|
+
@d.l(Sequel.lit('x').like('a')).should == '(x LIKE \'a\')'
|
|
145
147
|
@d.l(Sequel.lit('x') + 1 > 100).should == '((x + 1) > 100)'
|
|
146
148
|
@d.l((Sequel.lit('x') * :y) < 100.01).should == '((x * y) < 100.01)'
|
|
147
149
|
@d.l((Sequel.lit('x') - Sequel.expr(:y)/2) >= 100000000000000000000000000000000000).should == '((x - (y / 2)) >= 100000000000000000000000000000000000)'
|
|
@@ -381,6 +383,14 @@ describe "Blockless Ruby Filters" do
|
|
|
381
383
|
@d.lit(d.like(:b)).should == '((SELECT a FROM items) LIKE b)'
|
|
382
384
|
@d.lit(d.ilike(:b)).should == '((SELECT a FROM items) ILIKE b)'
|
|
383
385
|
end
|
|
386
|
+
|
|
387
|
+
it "should handled emulated char_length function" do
|
|
388
|
+
@d.lit(Sequel.char_length(:a)).should == 'char_length(a)'
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
it "should handled emulated trim function" do
|
|
392
|
+
@d.lit(Sequel.trim(:a)).should == 'trim(a)'
|
|
393
|
+
end
|
|
384
394
|
end
|
|
385
395
|
|
|
386
396
|
describe Sequel::SQL::VirtualRow do
|
|
@@ -523,12 +533,14 @@ end
|
|
|
523
533
|
describe "Sequel core extension replacements" do
|
|
524
534
|
before do
|
|
525
535
|
@db = Sequel::Database.new
|
|
536
|
+
@ds = @db.dataset
|
|
537
|
+
def @ds.supports_regexp?; true end
|
|
526
538
|
@o = Object.new
|
|
527
539
|
def @o.sql_literal(ds) 'foo' end
|
|
528
540
|
end
|
|
529
541
|
|
|
530
542
|
def l(arg, should)
|
|
531
|
-
@
|
|
543
|
+
@ds.literal(arg).should == should
|
|
532
544
|
end
|
|
533
545
|
|
|
534
546
|
it "Sequel.expr should return items wrapped in Sequel objects" do
|
|
@@ -446,4 +446,8 @@ describe "Sequel Mock Adapter" do
|
|
|
446
446
|
specify "should stub out the primary_key method for postgres" do
|
|
447
447
|
Sequel.mock(:host=>'postgres').primary_key(:t).should == :id
|
|
448
448
|
end
|
|
449
|
+
|
|
450
|
+
specify "should handle creating tables on oracle" do
|
|
451
|
+
proc{Sequel.mock(:host=>'oracle').create_table(:a){String :b}}.should_not raise_error
|
|
452
|
+
end
|
|
449
453
|
end
|
|
@@ -92,19 +92,6 @@ describe Sequel::Dataset, " graphing" do
|
|
|
92
92
|
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
-
it "#graph should accept an object that responds to dataset as the dataset" do
|
|
96
|
-
oc = Class.new
|
|
97
|
-
o = oc.new
|
|
98
|
-
ds = @ds2
|
|
99
|
-
oc.send(:define_method, :dataset){ds}
|
|
100
|
-
ds = @ds1.graph(o, :x=>:id)
|
|
101
|
-
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
|
102
|
-
ds = :lines
|
|
103
|
-
oc.send(:define_method, :dataset){ds}
|
|
104
|
-
ds = @ds1.graph(o, :x=>:id)
|
|
105
|
-
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
|
106
|
-
end
|
|
107
|
-
|
|
108
95
|
it "#graph should raise an error if a symbol, dataset, or model is not used" do
|
|
109
96
|
proc{@ds1.graph(Object.new, :x=>:id)}.should raise_error(Sequel::Error)
|
|
110
97
|
end
|
data/spec/core/schema_spec.rb
CHANGED
|
@@ -757,20 +757,34 @@ describe "DB#alter_table" do
|
|
|
757
757
|
@db = Sequel.mock
|
|
758
758
|
end
|
|
759
759
|
|
|
760
|
-
specify "should allow adding not null constraint" do
|
|
760
|
+
specify "should allow adding not null constraint via set_column_allow_null with false argument" do
|
|
761
761
|
@db.alter_table(:cats) do
|
|
762
762
|
set_column_allow_null :score, false
|
|
763
763
|
end
|
|
764
764
|
@db.sqls.should == ["ALTER TABLE cats ALTER COLUMN score SET NOT NULL"]
|
|
765
765
|
end
|
|
766
766
|
|
|
767
|
-
specify "should allow
|
|
767
|
+
specify "should allow removing not null constraint via set_column_allow_null with true argument" do
|
|
768
768
|
@db.alter_table(:cats) do
|
|
769
769
|
set_column_allow_null :score, true
|
|
770
770
|
end
|
|
771
771
|
@db.sqls.should == ["ALTER TABLE cats ALTER COLUMN score DROP NOT NULL"]
|
|
772
772
|
end
|
|
773
773
|
|
|
774
|
+
specify "should allow adding not null constraint via set_column_not_null" do
|
|
775
|
+
@db.alter_table(:cats) do
|
|
776
|
+
set_column_not_null :score
|
|
777
|
+
end
|
|
778
|
+
@db.sqls.should == ["ALTER TABLE cats ALTER COLUMN score SET NOT NULL"]
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
specify "should allow removing not null constraint via set_column_allow_null without argument" do
|
|
782
|
+
@db.alter_table(:cats) do
|
|
783
|
+
set_column_allow_null :score
|
|
784
|
+
end
|
|
785
|
+
@db.sqls.should == ["ALTER TABLE cats ALTER COLUMN score DROP NOT NULL"]
|
|
786
|
+
end
|
|
787
|
+
|
|
774
788
|
specify "should support add_column" do
|
|
775
789
|
@db.alter_table(:cats) do
|
|
776
790
|
add_column :score, :integer
|
|
@@ -939,6 +953,40 @@ describe "DB#alter_table" do
|
|
|
939
953
|
"ALTER TABLE cats ALTER COLUMN score TYPE varchar(30)",
|
|
940
954
|
"ALTER TABLE cats ALTER COLUMN score TYPE enum('a', 'b')"]
|
|
941
955
|
end
|
|
956
|
+
|
|
957
|
+
specify "should combine operations into a single query if the database supports it" do
|
|
958
|
+
@db.meta_def(:supports_combining_alter_table_ops?){true}
|
|
959
|
+
@db.alter_table(:cats) do
|
|
960
|
+
add_column :a, Integer
|
|
961
|
+
drop_column :b
|
|
962
|
+
set_column_not_null :c
|
|
963
|
+
rename_column :d, :e
|
|
964
|
+
set_column_default :f, 'g'
|
|
965
|
+
set_column_type :h, Integer
|
|
966
|
+
add_constraint(:i){a > 1}
|
|
967
|
+
drop_constraint :j
|
|
968
|
+
end
|
|
969
|
+
@db.sqls.should == ["ALTER TABLE cats ADD COLUMN a integer, DROP COLUMN b, ALTER COLUMN c SET NOT NULL, RENAME COLUMN d TO e, ALTER COLUMN f SET DEFAULT 'g', ALTER COLUMN h TYPE integer, ADD CONSTRAINT i CHECK (a > 1), DROP CONSTRAINT j"]
|
|
970
|
+
end
|
|
971
|
+
|
|
972
|
+
specify "should combine operations into consecutive groups of combinable operations if the database supports combining operations" do
|
|
973
|
+
@db.meta_def(:supports_combining_alter_table_ops?){true}
|
|
974
|
+
@db.alter_table(:cats) do
|
|
975
|
+
add_column :a, Integer
|
|
976
|
+
drop_column :b
|
|
977
|
+
set_column_not_null :c
|
|
978
|
+
rename_column :d, :e
|
|
979
|
+
add_index :e
|
|
980
|
+
set_column_default :f, 'g'
|
|
981
|
+
set_column_type :h, Integer
|
|
982
|
+
add_constraint(:i){a > 1}
|
|
983
|
+
drop_constraint :j
|
|
984
|
+
end
|
|
985
|
+
@db.sqls.should == ["ALTER TABLE cats ADD COLUMN a integer, DROP COLUMN b, ALTER COLUMN c SET NOT NULL, RENAME COLUMN d TO e",
|
|
986
|
+
"CREATE INDEX cats_e_index ON cats (e)",
|
|
987
|
+
"ALTER TABLE cats ALTER COLUMN f SET DEFAULT 'g', ALTER COLUMN h TYPE integer, ADD CONSTRAINT i CHECK (a > 1), DROP CONSTRAINT j"]
|
|
988
|
+
end
|
|
989
|
+
|
|
942
990
|
end
|
|
943
991
|
|
|
944
992
|
describe "Database#create_table" do
|
|
@@ -1251,11 +1299,13 @@ describe "Schema Parser" do
|
|
|
1251
1299
|
end
|
|
1252
1300
|
|
|
1253
1301
|
specify "should correctly parse all supported data types" do
|
|
1254
|
-
|
|
1255
|
-
|
|
1302
|
+
sm = Module.new do
|
|
1303
|
+
def schema_parse_table(t, opts)
|
|
1304
|
+
[[:x, {:type=>schema_column_type(t.to_s)}]]
|
|
1305
|
+
end
|
|
1256
1306
|
end
|
|
1307
|
+
@db.extend(sm)
|
|
1257
1308
|
@db.schema(:tinyint).first.last[:type].should == :integer
|
|
1258
|
-
@db.schema(:interval).first.last[:type].should == :interval
|
|
1259
1309
|
@db.schema(:int).first.last[:type].should == :integer
|
|
1260
1310
|
@db.schema(:integer).first.last[:type].should == :integer
|
|
1261
1311
|
@db.schema(:bigint).first.last[:type].should == :integer
|
|
@@ -1277,6 +1327,7 @@ describe "Schema Parser" do
|
|
|
1277
1327
|
@db.schema(:real).first.last[:type].should == :float
|
|
1278
1328
|
@db.schema(:float).first.last[:type].should == :float
|
|
1279
1329
|
@db.schema(:double).first.last[:type].should == :float
|
|
1330
|
+
@db.schema(:"double(1,2)").first.last[:type].should == :float
|
|
1280
1331
|
@db.schema(:"double precision").first.last[:type].should == :float
|
|
1281
1332
|
@db.schema(:number).first.last[:type].should == :decimal
|
|
1282
1333
|
@db.schema(:numeric).first.last[:type].should == :decimal
|
|
@@ -1296,5 +1347,13 @@ describe "Schema Parser" do
|
|
|
1296
1347
|
@db.schema(:binary).first.last[:type].should == :blob
|
|
1297
1348
|
@db.schema(:varbinary).first.last[:type].should == :blob
|
|
1298
1349
|
@db.schema(:enum).first.last[:type].should == :enum
|
|
1350
|
+
|
|
1351
|
+
@db = Sequel.mock(:host=>'postgres')
|
|
1352
|
+
@db.extend(sm)
|
|
1353
|
+
@db.schema(:interval).first.last[:type].should == :interval
|
|
1354
|
+
|
|
1355
|
+
@db = Sequel.mock(:host=>'mysql')
|
|
1356
|
+
@db.extend(sm)
|
|
1357
|
+
@db.schema(:set).first.last[:type].should == :set
|
|
1299
1358
|
end
|
|
1300
1359
|
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "Sequel::Plugins::ConstraintValidations" do
|
|
4
|
+
def model_class(opts={})
|
|
5
|
+
return @c if @c
|
|
6
|
+
@c = Class.new(Sequel::Model(@db[:items]))
|
|
7
|
+
@c.columns :name
|
|
8
|
+
@db.sqls
|
|
9
|
+
set_fetch(opts)
|
|
10
|
+
@c.plugin :constraint_validations
|
|
11
|
+
@c
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def set_fetch(opts)
|
|
15
|
+
@db.fetch = {:table=>'items', :message=>nil, :allow_nil=>nil, :constraint_name=>nil, :validation_type=>'presence', :argument=>nil, :column=>'name'}.merge(opts)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
before do
|
|
19
|
+
@db = Sequel.mock
|
|
20
|
+
set_fetch({})
|
|
21
|
+
@ds = @db[:items]
|
|
22
|
+
@ds.instance_variable_set(:@columns, [:name])
|
|
23
|
+
@ds2 = @db.dup[:items2]
|
|
24
|
+
@ds2.instance_variable_set(:@columns, [:name])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should load the validation_helpers plugin into the class" do
|
|
28
|
+
model_class.new.should respond_to(:validates_presence)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should parse constraint validations when loading plugin" do
|
|
32
|
+
@c = model_class
|
|
33
|
+
@db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
|
|
34
|
+
@db.constraint_validations.should == {'items'=>[[:validates_presence, :name]]}
|
|
35
|
+
@c.constraint_validations.should == [[:validates_presence, :name]]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "should parse constraint validations with a custom constraint validations table" do
|
|
39
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
40
|
+
@db.sqls
|
|
41
|
+
c.plugin :constraint_validations, :constraint_validations_table=>:foo
|
|
42
|
+
@db.sqls.should == ["SELECT * FROM foo"]
|
|
43
|
+
@db.constraint_validations.should == {'items'=>[[:validates_presence, :name]]}
|
|
44
|
+
c.constraint_validations.should == [[:validates_presence, :name]]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should populate constraint_validations when subclassing" do
|
|
48
|
+
c = Class.new(Sequel::Model(@db))
|
|
49
|
+
c.plugin :constraint_validations
|
|
50
|
+
@db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
|
|
51
|
+
sc = Class.new(c)
|
|
52
|
+
sc.set_dataset @ds
|
|
53
|
+
@db.sqls.should == []
|
|
54
|
+
sc.constraint_validations.should == [[:validates_presence, :name]]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should populate constraint_validations when changing the model's dataset" do
|
|
58
|
+
c = Class.new(Sequel::Model(@db[:foo]))
|
|
59
|
+
c.columns :name
|
|
60
|
+
@db.sqls
|
|
61
|
+
c.plugin :constraint_validations
|
|
62
|
+
@db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
|
|
63
|
+
sc = Class.new(c)
|
|
64
|
+
sc.set_dataset @ds
|
|
65
|
+
@db.sqls.should == []
|
|
66
|
+
sc.constraint_validations.should == [[:validates_presence, :name]]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "should reparse constraint validations when changing the model's database" do
|
|
70
|
+
c = Class.new(Sequel::Model(@ds2))
|
|
71
|
+
c.plugin :constraint_validations
|
|
72
|
+
@db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
|
|
73
|
+
sc = Class.new(c)
|
|
74
|
+
sc.set_dataset @ds
|
|
75
|
+
@db.sqls.should == ["SELECT * FROM sequel_constraint_validations"]
|
|
76
|
+
sc.constraint_validations.should == [[:validates_presence, :name]]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "should reparse constraint validations when changing the model's database with a custom constraint validations table" do
|
|
80
|
+
c = Class.new(Sequel::Model(@ds2))
|
|
81
|
+
c.plugin :constraint_validations, :constraint_validations_table=>:foo
|
|
82
|
+
@db.sqls.should == ["SELECT * FROM foo"]
|
|
83
|
+
sc = Class.new(c)
|
|
84
|
+
sc.set_dataset @ds
|
|
85
|
+
@db.sqls.should == ["SELECT * FROM foo"]
|
|
86
|
+
sc.constraint_validations.should == [[:validates_presence, :name]]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should correctly retrieve :message option from constraint validations table" do
|
|
90
|
+
model_class(:message=>'foo').constraint_validations.should == [[:validates_presence, :name, {:message=>'foo'}]]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "should correctly retrieve :allow_nil option from constraint validations table" do
|
|
94
|
+
model_class(:allow_nil=>true).constraint_validations.should == [[:validates_presence, :name, {:allow_nil=>true}]]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "should handle presence validation" do
|
|
98
|
+
model_class(:validation_type=>'presence').constraint_validations.should == [[:validates_presence, :name]]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "should handle exact_length validation" do
|
|
102
|
+
model_class(:validation_type=>'exact_length', :argument=>'5').constraint_validations.should == [[:validates_exact_length, 5, :name]]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "should handle min_length validation" do
|
|
106
|
+
model_class(:validation_type=>'min_length', :argument=>'5').constraint_validations.should == [[:validates_min_length, 5, :name]]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "should handle max_length validation" do
|
|
110
|
+
model_class(:validation_type=>'max_length', :argument=>'5').constraint_validations.should == [[:validates_max_length, 5, :name]]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "should handle length_range validation" do
|
|
114
|
+
model_class(:validation_type=>'length_range', :argument=>'3..5').constraint_validations.should == [[:validates_length_range, 3..5, :name]]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "should handle length_range validation with an exclusive end" do
|
|
118
|
+
model_class(:validation_type=>'length_range', :argument=>'3...5').constraint_validations.should == [[:validates_length_range, 3...5, :name]]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "should handle format validation" do
|
|
122
|
+
model_class(:validation_type=>'format', :argument=>'^foo.*').constraint_validations.should == [[:validates_format, /^foo.*/, :name]]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should handle format validation with case insensitive format" do
|
|
126
|
+
model_class(:validation_type=>'iformat', :argument=>'^foo.*').constraint_validations.should == [[:validates_format, /^foo.*/i, :name]]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "should handle includes validation with array of strings" do
|
|
130
|
+
model_class(:validation_type=>'includes_str_array', :argument=>'a,b,c').constraint_validations.should == [[:validates_includes, %w'a b c', :name]]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "should handle includes validation with array of integers" do
|
|
134
|
+
model_class(:validation_type=>'includes_int_array', :argument=>'1,2,3').constraint_validations.should == [[:validates_includes, [1, 2, 3], :name]]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "should handle includes validation with inclusive range of integers" do
|
|
138
|
+
model_class(:validation_type=>'includes_int_range', :argument=>'3..5').constraint_validations.should == [[:validates_includes, 3..5, :name]]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "should handle includes validation with exclusive range of integers" do
|
|
142
|
+
model_class(:validation_type=>'includes_int_range', :argument=>'3...5').constraint_validations.should == [[:validates_includes, 3...5, :name]]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "should handle like validation" do
|
|
146
|
+
model_class(:validation_type=>'like', :argument=>'foo').constraint_validations.should == [[:validates_format, /\Afoo\z/, :name]]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "should handle ilike validation" do
|
|
150
|
+
model_class(:validation_type=>'ilike', :argument=>'foo').constraint_validations.should == [[:validates_format, /\Afoo\z/i, :name]]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "should handle like validation with % metacharacter" do
|
|
154
|
+
model_class(:validation_type=>'like', :argument=>'%foo%').constraint_validations.should == [[:validates_format, /\A.*foo.*\z/, :name]]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "should handle like validation with %% metacharacter" do
|
|
158
|
+
model_class(:validation_type=>'like', :argument=>'%%foo%%').constraint_validations.should == [[:validates_format, /\A%foo%\z/, :name]]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should handle like validation with _ metacharacter" do
|
|
162
|
+
model_class(:validation_type=>'like', :argument=>'f_o').constraint_validations.should == [[:validates_format, /\Af.o\z/, :name]]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "should handle like validation with Regexp metacharacter" do
|
|
166
|
+
model_class(:validation_type=>'like', :argument=>'\wfoo\d').constraint_validations.should == [[:validates_format, /\A\\wfoo\\d\z/, :name]]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it "should handle unique validation" do
|
|
170
|
+
model_class(:validation_type=>'unique').constraint_validations.should == [[:validates_unique, [:name]]]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "should handle unique validation with multiple columns" do
|
|
174
|
+
model_class(:validation_type=>'unique', :column=>'name,id').constraint_validations.should == [[:validates_unique, [:name, :id]]]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "should used parsed constraint validations when validating" do
|
|
178
|
+
o = model_class.new
|
|
179
|
+
o.valid?.should == false
|
|
180
|
+
o.errors.full_messages.should == ['name is not present']
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it "should handle a table name specified as SQL::Identifier" do
|
|
184
|
+
set_fetch(:table=>'sch__items')
|
|
185
|
+
c = Class.new(Sequel::Model(@db[Sequel.identifier(:sch__items)]))
|
|
186
|
+
c.plugin :constraint_validations
|
|
187
|
+
c.constraint_validations.should == [[:validates_presence, :name]]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it "should handle a table name specified as SQL::QualifiedIdentifier" do
|
|
191
|
+
set_fetch(:table=>'sch.items')
|
|
192
|
+
c = Class.new(Sequel::Model(@db[Sequel.qualify(:sch, :items)]))
|
|
193
|
+
c.plugin :constraint_validations
|
|
194
|
+
c.constraint_validations.should == [[:validates_presence, :name]]
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "constraint_validations extension" do
|
|
4
|
+
def parse_insert(s)
|
|
5
|
+
m = /\AINSERT INTO sequel_constraint_validations \((.*)\) VALUES \((.*)\)\z/.match(s)
|
|
6
|
+
Hash[*m[1].split(', ').map{|v| v.to_sym}.zip(m[2].split(', ').map{|v| parse_insert_value(v)}).reject{|k, v| v.nil?}.flatten]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def parse_insert_value(s)
|
|
10
|
+
case s
|
|
11
|
+
when 'NULL'
|
|
12
|
+
nil
|
|
13
|
+
when /\A'(.*)'\z/
|
|
14
|
+
$1
|
|
15
|
+
else
|
|
16
|
+
raise Sequel::Error, "unhandled insert value: #{s.inspect}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
before do
|
|
21
|
+
@db = Sequel.mock
|
|
22
|
+
@db.extend(Module.new{attr_writer :schema; def schema(table, *) execute("parse schema for #{table}"); @schema; end})
|
|
23
|
+
@db.extension(:constraint_validations)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should allow creating the sequel_constraint_validations table" do
|
|
27
|
+
@db.create_constraint_validations_table
|
|
28
|
+
@db.sqls.should == ["CREATE TABLE sequel_constraint_validations (table varchar(255) NOT NULL, constraint_name varchar(255), validation_type varchar(255) NOT NULL, column varchar(255) NOT NULL, argument varchar(255), message varchar(255), allow_nil boolean)"]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should allow creating the sequel_constraint_validations table with a non-default table name" do
|
|
32
|
+
@db.constraint_validations_table = :foo
|
|
33
|
+
@db.create_constraint_validations_table
|
|
34
|
+
@db.sqls.should == ["CREATE TABLE foo (table varchar(255) NOT NULL, constraint_name varchar(255), validation_type varchar(255) NOT NULL, column varchar(255) NOT NULL, argument varchar(255), message varchar(255), allow_nil boolean)"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should allow dropping the sequel_constraint_validations table" do
|
|
38
|
+
@db.drop_constraint_validations_table
|
|
39
|
+
@db.sqls.should == ["DROP TABLE sequel_constraint_validations"]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should allow dropping the sequel_constraint_validations table with a non-default table name" do
|
|
43
|
+
@db.constraint_validations_table = :foo
|
|
44
|
+
@db.drop_constraint_validations_table
|
|
45
|
+
@db.sqls.should == ["DROP TABLE foo"]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should allow dropping validations for a given table" do
|
|
49
|
+
@db.drop_constraint_validations_for(:table=>:foo)
|
|
50
|
+
@db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE (table = 'foo')"]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should allow dropping validations for a given table and column" do
|
|
54
|
+
@db.drop_constraint_validations_for(:table=>:foo, :column=>:bar)
|
|
55
|
+
@db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table = 'foo') AND (column = 'bar'))"]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should allow dropping validations for a given table and constraint" do
|
|
59
|
+
@db.drop_constraint_validations_for(:table=>:foo, :constraint=>:bar)
|
|
60
|
+
@db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table = 'foo') AND (constraint_name = 'bar'))"]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "should allow dropping validations for a non-default constraint_validations table" do
|
|
64
|
+
@db.constraint_validations_table = :cv
|
|
65
|
+
@db.drop_constraint_validations_for(:table=>:foo)
|
|
66
|
+
@db.sqls.should == ["DELETE FROM cv WHERE (table = 'foo')"]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "should raise an error without deleting if attempting to drop validations with table, column, or constraint" do
|
|
70
|
+
proc{@db.drop_constraint_validations_for({})}.should raise_error(Sequel::Error)
|
|
71
|
+
@db.sqls.should == []
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "should allow adding constraint validations via create_table validate" do
|
|
75
|
+
@db.create_table(:foo){String :name; validate{presence :name}}
|
|
76
|
+
sqls = @db.sqls
|
|
77
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
|
78
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should allow adding constraint validations via alter_table validate" do
|
|
82
|
+
@db.schema = [[:name, {:type=>:string}]]
|
|
83
|
+
@db.alter_table(:foo){validate{presence :name}}
|
|
84
|
+
sqls = @db.sqls
|
|
85
|
+
parse_insert(sqls.slice!(2)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
|
86
|
+
sqls.should == ["parse schema for foo", "BEGIN", "COMMIT", "ALTER TABLE foo ADD CHECK ((name IS NOT NULL) AND (trim(name) != ''))"]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should handle :message option when adding validations" do
|
|
90
|
+
@db.create_table(:foo){String :name; validate{presence :name, :message=>'not there'}}
|
|
91
|
+
sqls = @db.sqls
|
|
92
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :message=>'not there'}
|
|
93
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "should handle :allow_nil option when adding validations" do
|
|
97
|
+
@db.create_table(:foo){String :name; validate{presence :name, :allow_nil=>true}}
|
|
98
|
+
sqls = @db.sqls
|
|
99
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t'}
|
|
100
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK (trim(name) != ''))"]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should handle :name option when adding validations" do
|
|
104
|
+
@db.create_table(:foo){String :name; validate{presence :name, :name=>'cons'}}
|
|
105
|
+
sqls = @db.sqls
|
|
106
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :constraint_name=>'cons'}
|
|
107
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CONSTRAINT cons CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should handle multiple columns when adding validations" do
|
|
111
|
+
@db.create_table(:foo){String :name; String :bar; validate{presence [:name, :bar]}}
|
|
112
|
+
sqls = @db.sqls
|
|
113
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
|
114
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"bar", :table=>"foo"}
|
|
115
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), bar varchar(255), CHECK ((name IS NOT NULL) AND (bar IS NOT NULL) AND (trim(name) != '') AND (trim(bar) != '')))"]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "should handle presence validation on non-String columns" do
|
|
119
|
+
@db.create_table(:foo){Integer :name; validate{presence :name}}
|
|
120
|
+
sqls = @db.sqls
|
|
121
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
|
122
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name integer, CHECK (name IS NOT NULL))"]
|
|
123
|
+
|
|
124
|
+
@db.schema = [[:name, {:type=>:integer}]]
|
|
125
|
+
@db.alter_table(:foo){validate{presence :name}}
|
|
126
|
+
sqls = @db.sqls
|
|
127
|
+
parse_insert(sqls.slice!(2)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
|
128
|
+
sqls.should == ["parse schema for foo", "BEGIN", "COMMIT", "ALTER TABLE foo ADD CHECK (name IS NOT NULL)"]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "should handle presence validation on Oracle with IS NOT NULL instead of != ''" do
|
|
132
|
+
@db = Sequel.mock(:host=>'oracle')
|
|
133
|
+
@db.extension(:constraint_validations)
|
|
134
|
+
@db.create_table(:foo){String :name; validate{presence :name}}
|
|
135
|
+
sqls = @db.sqls
|
|
136
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo"}
|
|
137
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) IS NOT NULL)))"]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "should assume column is not a String if it can't determine the type" do
|
|
141
|
+
@db.create_table(:foo){Integer :name; validate{presence :bar}}
|
|
142
|
+
sqls = @db.sqls
|
|
143
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"bar", :table=>"foo"}
|
|
144
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name integer, CHECK (bar IS NOT NULL))"]
|
|
145
|
+
|
|
146
|
+
@db.schema = [[:name, {:type=>:integer}]]
|
|
147
|
+
@db.alter_table(:foo){validate{presence :bar}}
|
|
148
|
+
sqls = @db.sqls
|
|
149
|
+
parse_insert(sqls.slice!(2)).should == {:validation_type=>"presence", :column=>"bar", :table=>"foo"}
|
|
150
|
+
sqls.should == ["parse schema for foo", "BEGIN", "COMMIT", "ALTER TABLE foo ADD CHECK (bar IS NOT NULL)"]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "should handle presence validation on non-String columns with :allow_nil option" do
|
|
154
|
+
@db.create_table(:foo){Integer :name; validate{presence :name, :allow_nil=>true}}
|
|
155
|
+
sqls = @db.sqls
|
|
156
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"foo", :allow_nil=>'t'}
|
|
157
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name integer)"]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "should support :exact_length constraint validation" do
|
|
161
|
+
@db.create_table(:foo){String :name; validate{exact_length 5, :name}}
|
|
162
|
+
sqls = @db.sqls
|
|
163
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"exact_length", :column=>"name", :table=>"foo", :argument=>'5'}
|
|
164
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) = 5)))"]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "should support :min_length constraint validation" do
|
|
168
|
+
@db.create_table(:foo){String :name; validate{min_length 5, :name}}
|
|
169
|
+
sqls = @db.sqls
|
|
170
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"min_length", :column=>"name", :table=>"foo", :argument=>'5'}
|
|
171
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) >= 5)))"]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "should support :max_length constraint validation" do
|
|
175
|
+
@db.create_table(:foo){String :name; validate{max_length 5, :name}}
|
|
176
|
+
sqls = @db.sqls
|
|
177
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"max_length", :column=>"name", :table=>"foo", :argument=>'5'}
|
|
178
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) <= 5)))"]
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "should support :length_range constraint validation" do
|
|
182
|
+
@db.create_table(:foo){String :name; validate{length_range 3..5, :name}}
|
|
183
|
+
sqls = @db.sqls
|
|
184
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"length_range", :column=>"name", :table=>"foo", :argument=>'3..5'}
|
|
185
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) >= 3) AND (char_length(name) <= 5)))"]
|
|
186
|
+
|
|
187
|
+
@db.create_table(:foo){String :name; validate{length_range 3...5, :name}}
|
|
188
|
+
sqls = @db.sqls
|
|
189
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"length_range", :column=>"name", :table=>"foo", :argument=>'3...5'}
|
|
190
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (char_length(name) >= 3) AND (char_length(name) < 5)))"]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "should support :format constraint validation" do
|
|
194
|
+
@db = Sequel.mock(:host=>'postgres')
|
|
195
|
+
@db.extension(:constraint_validations)
|
|
196
|
+
@db.create_table(:foo){String :name; validate{format /^foo.*/, :name}}
|
|
197
|
+
sqls = @db.sqls
|
|
198
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"format", :column=>"name", :table=>"foo", :argument=>'^foo.*'}
|
|
199
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name text, CHECK ((name IS NOT NULL) AND (name ~ '^foo.*')))"]
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it "should support :format constraint validation with case insensitive format" do
|
|
203
|
+
@db = Sequel.mock(:host=>'postgres')
|
|
204
|
+
@db.extension(:constraint_validations)
|
|
205
|
+
@db.create_table(:foo){String :name; validate{format /^foo.*/i, :name}}
|
|
206
|
+
sqls = @db.sqls
|
|
207
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"iformat", :column=>"name", :table=>"foo", :argument=>'^foo.*'}
|
|
208
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name text, CHECK ((name IS NOT NULL) AND (name ~* '^foo.*')))"]
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "should support :includes constraint validation with an array of strings" do
|
|
212
|
+
@db.create_table(:foo){String :name; validate{includes %w'a b c', :name}}
|
|
213
|
+
sqls = @db.sqls
|
|
214
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_str_array", :column=>"name", :table=>"foo", :argument=>'a,b,c'}
|
|
215
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name IN ('a', 'b', 'c'))))"]
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
it "should support :includes constraint validation with an array of integers" do
|
|
219
|
+
@db.create_table(:foo){String :name; validate{includes [1, 2, 3], :name}}
|
|
220
|
+
sqls = @db.sqls
|
|
221
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_int_array", :column=>"name", :table=>"foo", :argument=>'1,2,3'}
|
|
222
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name IN (1, 2, 3))))"]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it "should support :includes constraint validation with a inclusive range of integers" do
|
|
226
|
+
@db.create_table(:foo){String :name; validate{includes 3..5, :name}}
|
|
227
|
+
sqls = @db.sqls
|
|
228
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_int_range", :column=>"name", :table=>"foo", :argument=>'3..5'}
|
|
229
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name >= 3) AND (name <= 5)))"]
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it "should support :includes constraint validation with a exclusive range of integers" do
|
|
233
|
+
@db.create_table(:foo){String :name; validate{includes 3...5, :name}}
|
|
234
|
+
sqls = @db.sqls
|
|
235
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"includes_int_range", :column=>"name", :table=>"foo", :argument=>'3...5'}
|
|
236
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name >= 3) AND (name < 5)))"]
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it "should support :like constraint validation" do
|
|
240
|
+
@db.create_table(:foo){String :name; validate{like 'foo%', :name}}
|
|
241
|
+
sqls = @db.sqls
|
|
242
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"like", :column=>"name", :table=>"foo", :argument=>'foo%'}
|
|
243
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name LIKE 'foo%')))"]
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
it "should support :ilike constraint validation" do
|
|
247
|
+
@db.create_table(:foo){String :name; validate{ilike 'foo%', :name}}
|
|
248
|
+
sqls = @db.sqls
|
|
249
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"ilike", :column=>"name", :table=>"foo", :argument=>'foo%'}
|
|
250
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), CHECK ((name IS NOT NULL) AND (name ILIKE 'foo%')))"]
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it "should support :unique constraint validation" do
|
|
254
|
+
@db.create_table(:foo){String :name; validate{unique :name}}
|
|
255
|
+
sqls = @db.sqls
|
|
256
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"unique", :column=>"name", :table=>"foo"}
|
|
257
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), UNIQUE (name))"]
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
it "should support :unique constraint validation with multiple columns" do
|
|
261
|
+
@db.create_table(:foo){String :name; Integer :id; validate{unique [:name, :id]}}
|
|
262
|
+
sqls = @db.sqls
|
|
263
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"unique", :column=>"name,id", :table=>"foo"}
|
|
264
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE foo (name varchar(255), id integer, UNIQUE (name, id))"]
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
it "should support :unique constraint validation in alter_table" do
|
|
268
|
+
@db.alter_table(:foo){validate{unique :name}}
|
|
269
|
+
sqls = @db.sqls
|
|
270
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"unique", :column=>"name", :table=>"foo"}
|
|
271
|
+
sqls.should == ["BEGIN", "COMMIT", "ALTER TABLE foo ADD UNIQUE (name)"]
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
it "should drop constraints and validations when dropping a constraint validation" do
|
|
275
|
+
@db.alter_table(:foo){String :name; validate{drop :bar}}
|
|
276
|
+
@db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table, constraint_name) IN (('foo', 'bar')))", "ALTER TABLE foo DROP CONSTRAINT bar"]
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
it "should raise an error if attempting to validate inclusion with a range of non-integers" do
|
|
280
|
+
proc{@db.create_table(:foo){String :name; validate{includes 'a'..'z', :name}}}.should raise_error(Sequel::Error)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
it "should raise an error if attempting to validate inclusion with a range of non-integers or strings" do
|
|
284
|
+
proc{@db.create_table(:foo){String :name; validate{includes [1.0, 2.0], :name}}}.should raise_error(Sequel::Error)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
it "should raise an error if attempting to validate inclusion with a unsupported object" do
|
|
288
|
+
proc{@db.create_table(:foo){String :name; validate{includes 'a', :name}}}.should raise_error(Sequel::Error)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
it "should raise an error if attempting to drop a constraint validation in a create_table generator" do
|
|
292
|
+
proc{@db.create_table(:foo){String :name; validate{drop :foo}}}.should raise_error(Sequel::Error)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
it "should raise an error if attempting to drop a constraint validation without a name" do
|
|
296
|
+
proc{@db.alter_table(:foo){String :name; validate{drop nil}}}.should raise_error(Sequel::Error)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
it "should raise an error if attempting attempting to process a constraint validation with an unsupported type" do
|
|
300
|
+
proc{@db.alter_table(:foo){String :name; validations << {:type=>:foo}}}.should raise_error(Sequel::Error)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
it "should allow adding constraint validations for tables specified as a SQL::Identifier" do
|
|
304
|
+
@db.create_table(Sequel.identifier(:sch__foo)){String :name; validate{presence :name}}
|
|
305
|
+
sqls = @db.sqls
|
|
306
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"sch__foo"}
|
|
307
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE sch__foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
it "should allow adding constraint validations for tables specified as a SQL::QualifiedIdentifier" do
|
|
311
|
+
@db.create_table(Sequel.qualify(:sch, :foo)){String :name; validate{presence :name}}
|
|
312
|
+
sqls = @db.sqls
|
|
313
|
+
parse_insert(sqls.slice!(1)).should == {:validation_type=>"presence", :column=>"name", :table=>"sch.foo"}
|
|
314
|
+
sqls.should == ["BEGIN", "COMMIT", "CREATE TABLE sch.foo (name varchar(255), CHECK ((name IS NOT NULL) AND (trim(name) != '')))"]
|
|
315
|
+
end
|
|
316
|
+
end
|