sequel 2.9.0 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/CHANGELOG +56 -0
  2. data/{README → README.rdoc} +85 -57
  3. data/Rakefile +10 -5
  4. data/bin/sequel +7 -16
  5. data/doc/advanced_associations.rdoc +5 -17
  6. data/doc/cheat_sheet.rdoc +18 -20
  7. data/doc/dataset_filtering.rdoc +8 -32
  8. data/doc/schema.rdoc +20 -0
  9. data/lib/sequel_core.rb +35 -1
  10. data/lib/sequel_core/adapters/ado.rb +1 -1
  11. data/lib/sequel_core/adapters/db2.rb +2 -2
  12. data/lib/sequel_core/adapters/dbi.rb +2 -11
  13. data/lib/sequel_core/adapters/do.rb +205 -0
  14. data/lib/sequel_core/adapters/do/mysql.rb +38 -0
  15. data/lib/sequel_core/adapters/do/postgres.rb +92 -0
  16. data/lib/sequel_core/adapters/do/sqlite.rb +31 -0
  17. data/lib/sequel_core/adapters/firebird.rb +298 -0
  18. data/lib/sequel_core/adapters/informix.rb +10 -1
  19. data/lib/sequel_core/adapters/jdbc.rb +78 -19
  20. data/lib/sequel_core/adapters/jdbc/h2.rb +69 -0
  21. data/lib/sequel_core/adapters/jdbc/mysql.rb +10 -0
  22. data/lib/sequel_core/adapters/jdbc/postgresql.rb +7 -3
  23. data/lib/sequel_core/adapters/mysql.rb +53 -77
  24. data/lib/sequel_core/adapters/odbc.rb +1 -1
  25. data/lib/sequel_core/adapters/openbase.rb +1 -1
  26. data/lib/sequel_core/adapters/oracle.rb +2 -2
  27. data/lib/sequel_core/adapters/postgres.rb +16 -14
  28. data/lib/sequel_core/adapters/shared/mysql.rb +44 -9
  29. data/lib/sequel_core/adapters/shared/oracle.rb +4 -5
  30. data/lib/sequel_core/adapters/shared/postgres.rb +86 -82
  31. data/lib/sequel_core/adapters/shared/sqlite.rb +39 -24
  32. data/lib/sequel_core/adapters/sqlite.rb +11 -1
  33. data/lib/sequel_core/connection_pool.rb +10 -2
  34. data/lib/sequel_core/core_sql.rb +13 -3
  35. data/lib/sequel_core/database.rb +131 -30
  36. data/lib/sequel_core/database/schema.rb +5 -5
  37. data/lib/sequel_core/dataset.rb +31 -6
  38. data/lib/sequel_core/dataset/convenience.rb +11 -11
  39. data/lib/sequel_core/dataset/query.rb +2 -2
  40. data/lib/sequel_core/dataset/sql.rb +6 -6
  41. data/lib/sequel_core/exceptions.rb +4 -0
  42. data/lib/sequel_core/migration.rb +4 -4
  43. data/lib/sequel_core/schema/generator.rb +19 -3
  44. data/lib/sequel_core/schema/sql.rb +24 -20
  45. data/lib/sequel_core/sql.rb +13 -16
  46. data/lib/sequel_core/version.rb +11 -0
  47. data/lib/sequel_model.rb +2 -0
  48. data/lib/sequel_model/base.rb +2 -2
  49. data/lib/sequel_model/hooks.rb +46 -7
  50. data/lib/sequel_model/record.rb +11 -9
  51. data/lib/sequel_model/schema.rb +1 -1
  52. data/lib/sequel_model/validations.rb +72 -61
  53. data/spec/adapters/firebird_spec.rb +371 -0
  54. data/spec/adapters/mysql_spec.rb +118 -62
  55. data/spec/adapters/oracle_spec.rb +5 -5
  56. data/spec/adapters/postgres_spec.rb +33 -18
  57. data/spec/adapters/sqlite_spec.rb +2 -2
  58. data/spec/integration/dataset_test.rb +3 -3
  59. data/spec/integration/schema_test.rb +55 -5
  60. data/spec/integration/spec_helper.rb +11 -0
  61. data/spec/integration/type_test.rb +59 -16
  62. data/spec/sequel_core/connection_pool_spec.rb +14 -0
  63. data/spec/sequel_core/core_sql_spec.rb +24 -14
  64. data/spec/sequel_core/database_spec.rb +96 -11
  65. data/spec/sequel_core/dataset_spec.rb +97 -37
  66. data/spec/sequel_core/expression_filters_spec.rb +51 -40
  67. data/spec/sequel_core/object_graph_spec.rb +2 -2
  68. data/spec/sequel_core/schema_generator_spec.rb +31 -6
  69. data/spec/sequel_core/schema_spec.rb +25 -9
  70. data/spec/sequel_core/spec_helper.rb +4 -1
  71. data/spec/sequel_core/version_spec.rb +7 -0
  72. data/spec/sequel_model/associations_spec.rb +1 -1
  73. data/spec/sequel_model/hooks_spec.rb +68 -13
  74. data/spec/sequel_model/model_spec.rb +4 -4
  75. data/spec/sequel_model/record_spec.rb +22 -0
  76. data/spec/sequel_model/spec_helper.rb +2 -1
  77. data/spec/sequel_model/validations_spec.rb +107 -7
  78. metadata +15 -5
@@ -138,7 +138,7 @@ describe Sequel::Dataset, " graphing" do
138
138
  end
139
139
 
140
140
  it "#set_graph_aliases should allow a third entry to specify an expression to use other than the default" do
141
- ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :x, 1], :y=>[:lines, :y, :random[]])
141
+ ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :x, 1], :y=>[:lines, :y, :random.sql_function])
142
142
  ['SELECT 1 AS x, random() AS y FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)',
143
143
  'SELECT random() AS y, 1 AS x FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
144
144
  ].should(include(ds.sql))
@@ -241,7 +241,7 @@ describe Sequel::Dataset, " graphing" do
241
241
  end
242
242
 
243
243
  it "#graph_each should correctly map values when #set_graph_aliases is used with a third argument for each entry" do
244
- ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :z1, 2], :y=>[:lines, :z2, :random[]])
244
+ ds = @ds1.graph(:lines, :x=>:id).set_graph_aliases(:x=>[:points, :z1, 2], :y=>[:lines, :z2, :random.sql_function])
245
245
  def ds.fetch_rows(sql, &block)
246
246
  yield({:x=>2,:y=>3})
247
247
  end
@@ -47,9 +47,9 @@ describe Sequel::Schema::Generator do
47
47
 
48
48
  it "creates foreign key column" do
49
49
  @columns[3][:name].should == :parent_id
50
- @columns[3][:type].should == :integer
50
+ @columns[3][:type].should == Integer
51
51
  @columns[6][:name].should == :node_id
52
- @columns[6][:type].should == :integer
52
+ @columns[6][:type].should == Integer
53
53
  end
54
54
 
55
55
  it "uses table for foreign key columns, if specified" do
@@ -107,7 +107,7 @@ describe Sequel::Schema::AlterTableGenerator do
107
107
  add_spatial_index :geom
108
108
  add_index :blah, :type => :hash
109
109
  add_index :blah, :where => {:something => true}
110
- add_constraint :con1, ':fred > 100'
110
+ add_constraint :con1, 'fred > 100'
111
111
  drop_constraint :con2
112
112
  add_unique_constraint [:aaa, :bbb, :ccc], :name => :con3
113
113
  add_primary_key :id
@@ -130,13 +130,38 @@ describe Sequel::Schema::AlterTableGenerator do
130
130
  {:op => :add_index, :columns => [:geom], :type => :spatial},
131
131
  {:op => :add_index, :columns => [:blah], :type => :hash},
132
132
  {:op => :add_index, :columns => [:blah], :where => {:something => true}},
133
- {:op => :add_constraint, :type => :check, :constraint_type => :check, :name => :con1, :check => [':fred > 100']},
133
+ {:op => :add_constraint, :type => :check, :constraint_type => :check, :name => :con1, :check => ['fred > 100']},
134
134
  {:op => :drop_constraint, :name => :con2},
135
135
  {:op => :add_constraint, :type => :check, :constraint_type => :unique, :name => :con3, :columns => [:aaa, :bbb, :ccc]},
136
- {:op => :add_column, :name => :id, :type => :integer, :primary_key=>true, :auto_increment=>true},
137
- {:op => :add_column, :name => :node_id, :type => :integer, :table=>:nodes},
136
+ {:op => :add_column, :name => :id, :type => Integer, :primary_key=>true, :auto_increment=>true},
137
+ {:op => :add_column, :name => :node_id, :type => Integer, :table=>:nodes},
138
138
  {:op => :add_constraint, :type => :check, :constraint_type => :primary_key, :columns => [:aaa, :bbb]},
139
139
  {:op => :add_constraint, :type => :check, :constraint_type => :foreign_key, :columns => [:node_id, :prop_id], :table => :nodes_props}
140
140
  ]
141
141
  end
142
142
  end
143
+
144
+ describe "Sequel::Schema::Generator generic type methods" do
145
+ before do
146
+ @generator = Sequel::Schema::Generator.new(SchemaDummyDatabase.new) do
147
+ String :a
148
+ Integer :b
149
+ Fixnum :c
150
+ Bignum :d
151
+ Float :e
152
+ BigDecimal :f
153
+ Date :g
154
+ DateTime :h
155
+ Time :i
156
+ Numeric :j
157
+ File :k
158
+ TrueClass :l
159
+ FalseClass :m
160
+ end
161
+ @columns, @indexes = @generator.create_info
162
+ end
163
+
164
+ it "should store the type class in :type for each column" do
165
+ @columns.map{|c| c[:type]}.should == [String, Integer, Fixnum, Bignum, Float, BigDecimal, Date, DateTime, Time, Numeric, File, TrueClass, FalseClass]
166
+ end
167
+ end
@@ -38,6 +38,28 @@ context "DB#create_table" do
38
38
  end
39
39
  @db.sqls.should == ['CREATE TABLE cats (id integer, name text)']
40
40
  end
41
+
42
+ specify "should transform types given as ruby classes to database-specific types" do
43
+ @db.create_table(:cats) do
44
+ String :a
45
+ Integer :b
46
+ Fixnum :c
47
+ Bignum :d
48
+ Float :e
49
+ BigDecimal :f
50
+ Date :g
51
+ DateTime :h
52
+ Time :i
53
+ Numeric :j
54
+ File :k
55
+ TrueClass :l
56
+ FalseClass :m
57
+ column :n, Fixnum
58
+ primary_key :o, :type=>String
59
+ foreign_key :p, :f, :type=>Date
60
+ end
61
+ @db.sqls.should == ['CREATE TABLE cats (o varchar(255) PRIMARY KEY AUTOINCREMENT, a varchar(255), b integer, c integer, d bigint, e double precision, f numeric, g date, h timestamp, i timestamp, j numeric, k blob, l boolean, m boolean, n integer, p date REFERENCES f)']
62
+ end
41
63
 
42
64
  specify "should accept primary key definition" do
43
65
  @db.create_table(:cats) do
@@ -332,7 +354,7 @@ context "DB#create_table" do
332
354
  specify "should accept unnamed constraint definitions with blocks" do
333
355
  @db.create_table(:cats) do
334
356
  integer :score
335
- check {(:x > 0) & (:y < 1)}
357
+ check {(:x.sql_number > 0) & (:y.sql_number < 1)}
336
358
  end
337
359
  @db.sqls.should == ["CREATE TABLE cats (score integer, CHECK ((x > 0) AND (y < 1)))"]
338
360
  end
@@ -361,7 +383,7 @@ context "DB#create_table" do
361
383
 
362
384
  specify "should accept named constraint definitions with block" do
363
385
  @db.create_table(:cats) do
364
- constraint(:blah_blah) {(:x > 0) & (:y < 1)}
386
+ constraint(:blah_blah) {(:x.sql_number > 0) & (:y.sql_number < 1)}
365
387
  end
366
388
  @db.sqls.should == ["CREATE TABLE cats (CONSTRAINT blah_blah CHECK ((x > 0) AND (y < 1)))"]
367
389
  end
@@ -510,7 +532,7 @@ context "DB#alter_table" do
510
532
 
511
533
  specify "should support add_constraint with block" do
512
534
  @db.alter_table(:cats) do
513
- add_constraint(:blah_blah) {(:x > 0) & (:y < 1)}
535
+ add_constraint(:blah_blah) {(:x.sql_number > 0) & (:y.sql_number < 1)}
514
536
  end
515
537
  @db.sqls.should == ["ALTER TABLE cats ADD CONSTRAINT blah_blah CHECK ((x > 0) AND (y < 1))"]
516
538
  end
@@ -686,12 +708,6 @@ context "Schema Parser" do
686
708
  @sqls.should == ['x']
687
709
  @db.schema(nil, :reload=>true).should == {'x'=>[[:x, {:db_type=>"x"}]]}
688
710
  @sqls.should == ['x', 'x']
689
- @db.meta_def(:schema_parse_tables) do |opts|
690
- sqls << 1
691
- {'x'=>[[:a, {:db_type=>"1"}]]}
692
- end
693
- @db.schema(nil, :reload=>true).should == {'x'=>[[:a, {:db_type=>"1"}]]}
694
- @sqls.should == ['x', 'x', 1]
695
711
  end
696
712
 
697
713
  specify "should convert various types of table name arguments" do
@@ -25,7 +25,8 @@ end
25
25
 
26
26
  class MockDatabase < Sequel::Database
27
27
  @@quote_identifiers = false
28
- @@upcase_identifiers = false
28
+ self.identifier_input_method = nil
29
+ self.identifier_output_method = nil
29
30
  attr_reader :sqls
30
31
 
31
32
  def execute(sql, opts={})
@@ -44,6 +45,8 @@ end
44
45
 
45
46
  class SchemaDummyDatabase < Sequel::Database
46
47
  attr_reader :sqls
48
+ self.identifier_input_method = nil
49
+ self.identifier_output_method = nil
47
50
 
48
51
  def execute(sql, opts={})
49
52
  @sqls ||= []
@@ -0,0 +1,7 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ context "Sequel.version" do
4
+ specify "should be in the form X.Y.Z with all being numbers" do
5
+ Sequel.version.should =~ /\A\d+\.\d+\.\d+\z/
6
+ end
7
+ end
@@ -120,7 +120,7 @@ describe Sequel::Model, "many_to_one" do
120
120
  it "should support :order, :limit (only for offset), and :dataset options, as well as a block" do
121
121
  c2 = @c2
122
122
  @c2.many_to_one :child_20, :class => @c2, :key=>:id, :dataset=>proc{c2.filter(:parent_id=>pk)}, :limit=>[10,20], :order=>:name do |ds|
123
- ds.filter(:x > 1)
123
+ ds.filter(:x.sql_number > 1)
124
124
  end
125
125
  @c2.load(:id => 100).child_20
126
126
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((parent_id = 100) AND (x > 1)) ORDER BY name LIMIT 1 OFFSET 20"]
@@ -37,8 +37,20 @@ describe "Model hooks" do
37
37
  c.new.before_save
38
38
  $adds.should == ['bye']
39
39
  end
40
-
40
+
41
41
  specify "should be additive" do
42
+ $adds = []
43
+ c = Class.new(Sequel::Model)
44
+ c.class_eval do
45
+ after_save {$adds << 'hyiyie'}
46
+ after_save {$adds << 'byiyie'}
47
+ end
48
+
49
+ c.new.after_save
50
+ $adds.should == ['hyiyie', 'byiyie']
51
+ end
52
+
53
+ specify "before hooks should run in reverse order" do
42
54
  $adds = []
43
55
  c = Class.new(Sequel::Model)
44
56
  c.class_eval do
@@ -47,7 +59,7 @@ describe "Model hooks" do
47
59
  end
48
60
 
49
61
  c.new.before_save
50
- $adds.should == ['hyiyie', 'byiyie']
62
+ $adds.should == ['byiyie', 'hyiyie']
51
63
  end
52
64
 
53
65
  specify "should not be additive if the method or tag already exists" do
@@ -101,16 +113,16 @@ describe "Model hooks" do
101
113
  $adds = []
102
114
  a = Class.new(Sequel::Model)
103
115
  a.class_eval do
104
- before_save {$adds << '123'}
116
+ after_save {$adds << '123'}
105
117
  end
106
118
 
107
119
  b = Class.new(a)
108
120
  b.class_eval do
109
- before_save {$adds << '456'}
110
- before_save {$adds << '789'}
121
+ after_save {$adds << '456'}
122
+ after_save {$adds << '789'}
111
123
  end
112
124
 
113
- b.new.before_save
125
+ b.new.after_save
114
126
  $adds.should == ['123', '456', '789']
115
127
  end
116
128
 
@@ -139,31 +151,31 @@ describe "Model hooks" do
139
151
 
140
152
  a = Class.new(Sequel::Model)
141
153
  a.class_eval do
142
- before_save {$adds << 'blah'; $flag}
143
- before_save {$adds << 'cruel'}
154
+ after_save {$adds << 'blah'; $flag}
155
+ after_save {$adds << 'cruel'}
144
156
  end
145
157
 
146
- a.new.before_save
158
+ a.new.after_save
147
159
  $adds.should == ['blah', 'cruel']
148
160
 
149
161
  # chain should not break on nil
150
162
  $adds = []
151
163
  $flag = nil
152
- a.new.before_save
164
+ a.new.after_save
153
165
  $adds.should == ['blah', 'cruel']
154
166
 
155
167
  $adds = []
156
168
  $flag = false
157
- a.new.before_save
169
+ a.new.after_save
158
170
  $adds.should == ['blah']
159
171
 
160
172
  b = Class.new(a)
161
173
  b.class_eval do
162
- before_save {$adds << 'mau'}
174
+ after_save {$adds << 'mau'}
163
175
  end
164
176
 
165
177
  $adds = []
166
- b.new.before_save
178
+ b.new.after_save
167
179
  $adds.should == ['blah']
168
180
  end
169
181
  end
@@ -428,3 +440,46 @@ describe "Model.has_hooks?" do
428
440
  @d.has_hooks?(:before_save).should be_false
429
441
  end
430
442
  end
443
+
444
+ describe "Model#add_hook_type" do
445
+ setup do
446
+ class Foo < Sequel::Model(:items)
447
+ add_hook_type :before_bar, :after_bar
448
+
449
+ def bar
450
+ return :b if before_bar == false
451
+ return :a if after_bar == false
452
+ true
453
+ end
454
+ end
455
+ @f = Class.new(Foo)
456
+ end
457
+
458
+ specify "should have before_bar and after_bar class methods" do
459
+ @f.should respond_to(:before_bar)
460
+ @f.should respond_to(:before_bar)
461
+ end
462
+
463
+ specify "should have before_bar and after_bar instance methods" do
464
+ @f.new.should respond_to(:before_bar)
465
+ @f.new.should respond_to(:before_bar)
466
+ end
467
+
468
+ specify "it should return true for bar when before_bar and after_bar hooks are returing true" do
469
+ a = 1
470
+ @f.before_bar { a += 1}
471
+ @f.new.bar.should be_true
472
+ a.should == 2
473
+ @f.after_bar { a *= 2}
474
+ @f.new.bar.should be_true
475
+ a.should == 6
476
+ end
477
+
478
+ specify "it should return nil for bar when before_bar and after_bar hooks are returing false" do
479
+ @f.new.bar.should be_true
480
+ @f.after_bar { false }
481
+ @f.new.bar.should == :a
482
+ @f.before_bar { false }
483
+ @f.new.bar.should == :b
484
+ end
485
+ end
@@ -219,12 +219,12 @@ describe Sequel::Model, ".subset" do
219
219
  specify "should create a filter on the underlying dataset" do
220
220
  proc {@c.new_only}.should raise_error(NoMethodError)
221
221
 
222
- @c.subset(:new_only) {:age < 'new'}
222
+ @c.subset(:new_only) {:age.sql_number < 'new'}
223
223
 
224
224
  @c.new_only.sql.should == "SELECT * FROM items WHERE (age < 'new')"
225
225
  @c.dataset.new_only.sql.should == "SELECT * FROM items WHERE (age < 'new')"
226
226
 
227
- @c.subset(:pricey) {:price > 100}
227
+ @c.subset(:pricey) {:price.sql_number > 100}
228
228
 
229
229
  @c.pricey.sql.should == "SELECT * FROM items WHERE (price > 100)"
230
230
  @c.dataset.pricey.sql.should == "SELECT * FROM items WHERE (price > 100)"
@@ -262,10 +262,10 @@ describe Sequel::Model, ".find" do
262
262
  end
263
263
 
264
264
  specify "should accept filter blocks" do
265
- @c.find{:id > 1}.should be_a_kind_of(@c)
265
+ @c.find{:id.sql_number > 1}.should be_a_kind_of(@c)
266
266
  $sqls.last.should == "SELECT * FROM items WHERE (id > 1) LIMIT 1"
267
267
 
268
- @c.find {(:x > 1) & (:y < 2)}.should be_a_kind_of(@c)
268
+ @c.find {(:x.sql_number > 1) & (:y.sql_number < 2)}.should be_a_kind_of(@c)
269
269
  $sqls.last.should == "SELECT * FROM items WHERE ((x > 1) AND (y < 2)) LIMIT 1"
270
270
  end
271
271
 
@@ -54,6 +54,28 @@ describe "Model#save" do
54
54
  o.save(:y)
55
55
  o.changed_columns.should == []
56
56
  end
57
+
58
+ it "should preserve changed_columns' and @new's value until all hook finish running" do
59
+ res = nil
60
+ @c.after_save { res = [changed_columns, @new].flatten}
61
+ o = @c.new(:x => 1, :y => nil)
62
+ o[:x] = 2
63
+ o.save
64
+ res.should == [:x,true]
65
+
66
+ res = nil
67
+ o = @c.load(:id => 23,:x => 1, :y => nil)
68
+ o[:x] = 2
69
+ o.save
70
+ res.should == [:x,false]
71
+
72
+ res = nil
73
+ o = @c.load(:id => 23,:x => 1, :y => nil)
74
+ o[:x] = 2
75
+ o[:y] = 22
76
+ o.save(:x)
77
+ res.should == [:x,:y,false]
78
+ end
57
79
 
58
80
  end
59
81
 
@@ -34,7 +34,8 @@ end
34
34
 
35
35
  class MockDatabase < Sequel::Database
36
36
  @@quote_identifiers = false
37
- @@upcase_identifiers = false
37
+ self.identifier_input_method = nil
38
+ self.identifier_output_method = nil
38
39
  attr_reader :sqls
39
40
 
40
41
  def execute(sql, opts={})
@@ -120,6 +120,39 @@ describe Sequel::Model do
120
120
  atts.should == [:xx, :yy]
121
121
  end
122
122
 
123
+ specify "should respect allow_missing option when using multiple attributes" do
124
+ o = @c.new
125
+ def o.xx
126
+ self[:xx]
127
+ end
128
+ def o.yy
129
+ self[:yy]
130
+ end
131
+ vals = nil
132
+ atts = nil
133
+ @c.validates_each([:xx, :yy], :allow_missing=>true){|obj,a,v| atts=a; vals=v}
134
+
135
+ o.values[:xx] = 1
136
+ o.valid?
137
+ vals.should == [1,nil]
138
+ atts.should == [:xx, :yy]
139
+
140
+ vals = nil
141
+ atts = nil
142
+ o.values.clear
143
+ o.values[:yy] = 2
144
+ o.valid?
145
+ vals.should == [nil, 2]
146
+ atts.should == [:xx, :yy]
147
+
148
+ vals = nil
149
+ atts = nil
150
+ o.values.clear
151
+ o.valid?.should == true
152
+ vals.should == nil
153
+ atts.should == nil
154
+ end
155
+
123
156
  specify "should overwrite existing validation with the same tag and attribute" do
124
157
  @c.validates_each(:xx, :xx, :tag=>:low) {|o, a, v| o.xxx; o.errors[a] << 'too low' if v < 50}
125
158
  @c.validates_each(:yy, :yy) {|o, a, v| o.yyy; o.errors[a] << 'too low' if v < 50}
@@ -243,6 +276,18 @@ describe Sequel::Model do
243
276
  @m.should_not be_valid
244
277
  end
245
278
 
279
+ specify "should validate acceptance_of with allow_missing => true" do
280
+ @c.validates_acceptance_of :value, :allow_missing => true
281
+ @m.should be_valid
282
+ end
283
+
284
+ specify "should validate acceptance_of with allow_missing => true and allow_nil => false" do
285
+ @c.validates_acceptance_of :value, :allow_missing => true, :allow_nil => false
286
+ @m.should be_valid
287
+ @m.value = nil
288
+ @m.should_not be_valid
289
+ end
290
+
246
291
  specify "should validate acceptance_of with if => true" do
247
292
  @c.validates_acceptance_of :value, :if => :dont_skip
248
293
  @m.value = '0'
@@ -300,6 +345,16 @@ describe Sequel::Model do
300
345
  @m.should be_valid
301
346
  end
302
347
 
348
+ specify "should validate confirmation_of with allow_missing => true" do
349
+ @c.send(:attr_accessor, :value_confirmation)
350
+ @c.validates_acceptance_of :value, :allow_missing => true
351
+ @m.should be_valid
352
+ @m.value_confirmation = 'blah'
353
+ @m.should be_valid
354
+ @m.value = nil
355
+ @m.should_not be_valid
356
+ end
357
+
303
358
  specify "should validate format_of" do
304
359
  @c.validates_format_of :value, :with => /.+_.+/
305
360
  @m.value = 'abc_'
@@ -327,6 +382,13 @@ describe Sequel::Model do
327
382
  @m.should be_valid
328
383
  end
329
384
 
385
+ specify "should validate format_of with allow_missing => true" do
386
+ @c.validates_format_of :value, :allow_missing => true, :with=>/./
387
+ @m.should be_valid
388
+ @m.value = nil
389
+ @m.should_not be_valid
390
+ end
391
+
330
392
  specify "should validate length_of with maximum" do
331
393
  @c.validates_length_of :value, :maximum => 5
332
394
  @m.should_not be_valid
@@ -386,6 +448,13 @@ describe Sequel::Model do
386
448
  @m.should be_valid
387
449
  end
388
450
 
451
+ specify "should validate length_of with allow_missing => true" do
452
+ @c.validates_length_of :value, :allow_missing => true, :minimum => 5
453
+ @m.should be_valid
454
+ @m.value = nil
455
+ @m.should_not be_valid
456
+ end
457
+
389
458
  specify "should allow multiple calls to validates_length_of with different options without overwriting" do
390
459
  @c.validates_length_of :value, :maximum => 5
391
460
  @c.validates_length_of :value, :minimum => 5
@@ -450,6 +519,13 @@ describe Sequel::Model do
450
519
  @m.should be_valid
451
520
  end
452
521
 
522
+ specify "should validate numericality_of with allow_missing => true" do
523
+ @c.validates_numericality_of :value, :allow_missing => true
524
+ @m.should be_valid
525
+ @m.value = nil
526
+ @m.should_not be_valid
527
+ end
528
+
453
529
  specify "should validate presence_of" do
454
530
  @c.validates_presence_of :value
455
531
  @m.should_not be_valid
@@ -475,6 +551,13 @@ describe Sequel::Model do
475
551
  @m.should be_valid
476
552
  end
477
553
 
554
+ specify "should validate presence_of with allow_missing => true" do
555
+ @c.validates_presence_of :value, :allow_missing => true
556
+ @m.should be_valid
557
+ @m.value = nil
558
+ @m.should_not be_valid
559
+ end
560
+
478
561
  specify "should validate uniqueness_of with if => true" do
479
562
  @c.validates_uniqueness_of :value, :if => :dont_skip
480
563
 
@@ -489,10 +572,11 @@ describe Sequel::Model do
489
572
  @m.should be_valid
490
573
  end
491
574
 
492
- specify "should validate with :if => block" do
493
- @c.validates_presence_of :value, :if => proc {false}
494
-
575
+ specify "should validate uniqueness_of with allow_missing => true" do
576
+ @c.validates_uniqueness_of :value, :allow_missing => true
495
577
  @m.should be_valid
578
+ @m.value = nil
579
+ @m.should_not be_valid
496
580
  end
497
581
  end
498
582
 
@@ -631,10 +715,6 @@ describe Sequel::Model, "Validations" do
631
715
  @person.valid?.should be_true
632
716
  end
633
717
 
634
- # it "should allow for :with_exactly => /[a-zA-Z]/, which wraps the supplied regex with ^<regex>$" do
635
- # pending("TODO: Add this option to Validatable#validates_format_of")
636
- # end
637
-
638
718
  it "should validate length of column" do
639
719
  class ::Person < Sequel::Model
640
720
  validations.clear
@@ -667,6 +747,26 @@ describe Sequel::Model, "Validations" do
667
747
  @person.should be_valid
668
748
  end
669
749
 
750
+ it "should validate that a column doesn't have a string value" do
751
+ p = Class.new(Sequel::Model)
752
+ p.class_eval do
753
+ columns :age
754
+ self.raise_on_typecast_failure = false
755
+ validates_not_string :age
756
+ @db_schema = {:age=>{:type=>:integer}}
757
+ end
758
+
759
+ @person = p.new :age => "a"
760
+ @person.should_not be_valid
761
+ @person.errors.full_messages.should == ['age is not a valid integer']
762
+ @person.db_schema[:age][:type] = :datetime
763
+ @person.should_not be_valid
764
+ @person.errors.full_messages.should == ['age is not a valid datetime']
765
+
766
+ @person.age = 20
767
+ @person.should be_valid
768
+ end
769
+
670
770
  it "should validate numericality of column" do
671
771
  class ::Person < Sequel::Model
672
772
  validations.clear