sequel 1.4.0 → 1.5.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 +56 -0
- data/README +257 -154
- data/Rakefile +8 -16
- data/lib/sequel_model.rb +15 -257
- data/lib/sequel_model/associations.rb +70 -33
- data/lib/sequel_model/base.rb +80 -35
- data/lib/sequel_model/caching.rb +3 -3
- data/lib/sequel_model/deprecated.rb +81 -0
- data/lib/sequel_model/eager_loading.rb +303 -43
- data/lib/sequel_model/hooks.rb +29 -25
- data/lib/sequel_model/inflections.rb +112 -0
- data/lib/sequel_model/inflector.rb +279 -0
- data/lib/sequel_model/plugins.rb +15 -14
- data/lib/sequel_model/record.rb +87 -75
- data/lib/sequel_model/schema.rb +2 -0
- data/lib/sequel_model/validations.rb +300 -2
- data/spec/associations_spec.rb +175 -9
- data/spec/base_spec.rb +37 -18
- data/spec/caching_spec.rb +7 -4
- data/spec/deprecated_relations_spec.rb +3 -43
- data/spec/eager_loading_spec.rb +295 -7
- data/spec/hooks_spec.rb +7 -4
- data/spec/inflector_spec.rb +34 -0
- data/spec/model_spec.rb +30 -53
- data/spec/record_spec.rb +191 -33
- data/spec/spec_helper.rb +17 -2
- data/spec/validations_spec.rb +414 -15
- metadata +7 -22
- data/lib/sequel_model/pretty_table.rb +0 -73
data/spec/record_spec.rb
CHANGED
@@ -6,9 +6,7 @@ describe "Model#save" do
|
|
6
6
|
MODEL_DB.reset
|
7
7
|
|
8
8
|
@c = Class.new(Sequel::Model(:items)) do
|
9
|
-
|
10
|
-
[:id, :x, :y]
|
11
|
-
end
|
9
|
+
columns :id, :x, :y
|
12
10
|
end
|
13
11
|
end
|
14
12
|
|
@@ -52,9 +50,7 @@ describe "Model#save_changes" do
|
|
52
50
|
MODEL_DB.reset
|
53
51
|
|
54
52
|
@c = Class.new(Sequel::Model(:items)) do
|
55
|
-
|
56
|
-
[:id, :x, :y]
|
57
|
-
end
|
53
|
+
columns :id, :x, :y
|
58
54
|
end
|
59
55
|
end
|
60
56
|
|
@@ -109,47 +105,67 @@ describe "Model#save_changes" do
|
|
109
105
|
end
|
110
106
|
end
|
111
107
|
|
112
|
-
describe "Model#
|
113
|
-
|
108
|
+
describe "Model#update_values" do
|
114
109
|
before(:each) do
|
115
110
|
MODEL_DB.reset
|
116
111
|
|
117
112
|
@c = Class.new(Sequel::Model(:items)) do
|
118
|
-
|
119
|
-
[:id, :x, :y]
|
120
|
-
end
|
113
|
+
columns :id, :x, :y
|
121
114
|
end
|
122
115
|
end
|
123
116
|
|
124
117
|
it "should generate an update statement" do
|
125
118
|
o = @c.new(:id => 1)
|
126
|
-
o.
|
119
|
+
o.update_values(:x => 1)
|
127
120
|
MODEL_DB.sqls.first.should == "UPDATE items SET x = 1 WHERE (id = 1)"
|
128
121
|
end
|
129
122
|
|
130
123
|
it "should update attribute values" do
|
131
124
|
o = @c.new(:id => 1)
|
132
125
|
o.x.should be_nil
|
133
|
-
o.
|
126
|
+
o.update_values(:x => 1)
|
134
127
|
o.x.should == 1
|
135
128
|
end
|
136
129
|
|
137
130
|
it "should support string keys" do
|
138
131
|
o = @c.new(:id => 1)
|
139
132
|
o.x.should be_nil
|
140
|
-
o.
|
133
|
+
o.update_values('x' => 1)
|
141
134
|
o.x.should == 1
|
142
135
|
MODEL_DB.sqls.first.should == "UPDATE items SET x = 1 WHERE (id = 1)"
|
143
136
|
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "Model#set_values" do
|
140
|
+
before(:each) do
|
141
|
+
MODEL_DB.reset
|
142
|
+
|
143
|
+
@c = Class.new(Sequel::Model(:items)) do
|
144
|
+
columns :id, :x, :y
|
145
|
+
end
|
146
|
+
end
|
144
147
|
|
145
|
-
it "should
|
148
|
+
it "should not touch the database" do
|
146
149
|
o = @c.new(:id => 1)
|
147
|
-
o.
|
148
|
-
MODEL_DB.sqls.
|
150
|
+
o.set_values(:x => 1)
|
151
|
+
MODEL_DB.sqls.should == []
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should update attribute values" do
|
155
|
+
o = @c.new(:id => 1)
|
156
|
+
o.x.should be_nil
|
157
|
+
o.set_values(:x => 1)
|
158
|
+
o.x.should == 1
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should support string keys" do
|
162
|
+
o = @c.new(:id => 1)
|
163
|
+
o.x.should be_nil
|
164
|
+
o.set_values('x' => 1)
|
165
|
+
o.x.should == 1
|
149
166
|
end
|
150
167
|
end
|
151
168
|
|
152
|
-
|
153
169
|
describe "Model#new?" do
|
154
170
|
|
155
171
|
before(:each) do
|
@@ -170,6 +186,7 @@ describe "Model#new?" do
|
|
170
186
|
n.should_not be_new
|
171
187
|
end
|
172
188
|
|
189
|
+
### DEPRECATED
|
173
190
|
it "should alias new_record? to new?" do
|
174
191
|
n = @c.new(:x => 1)
|
175
192
|
n.should respond_to(:new_record?)
|
@@ -217,7 +234,7 @@ end
|
|
217
234
|
|
218
235
|
describe Sequel::Model, "with this" do
|
219
236
|
|
220
|
-
before { @example = Class.new Sequel::Model(:examples) }
|
237
|
+
before { @example = Class.new Sequel::Model(:examples); @example.columns :id, :a, :x, :y }
|
221
238
|
|
222
239
|
it "should return a dataset identifying the record" do
|
223
240
|
instance = @example.new :id => 3
|
@@ -250,6 +267,7 @@ end
|
|
250
267
|
describe "Model#pk" do
|
251
268
|
before(:each) do
|
252
269
|
@m = Class.new(Sequel::Model)
|
270
|
+
@m.columns :id, :x, :y
|
253
271
|
end
|
254
272
|
|
255
273
|
it "should be default return the value of the :id column" do
|
@@ -283,6 +301,7 @@ end
|
|
283
301
|
describe "Model#pk_hash" do
|
284
302
|
before(:each) do
|
285
303
|
@m = Class.new(Sequel::Model)
|
304
|
+
@m.columns :id, :x, :y
|
286
305
|
end
|
287
306
|
|
288
307
|
it "should be default return the value of the :id column" do
|
@@ -313,14 +332,59 @@ describe "Model#pk_hash" do
|
|
313
332
|
end
|
314
333
|
end
|
315
334
|
|
335
|
+
describe Sequel::Model, "set_with_params" do
|
336
|
+
|
337
|
+
before(:each) do
|
338
|
+
MODEL_DB.reset
|
339
|
+
|
340
|
+
@c = Class.new(Sequel::Model(:items)) do
|
341
|
+
columns :x, :y, :id
|
342
|
+
end
|
343
|
+
@c.instance_variable_set(:@columns, true)
|
344
|
+
@o1 = @c.new
|
345
|
+
@o2 = @c.load(:id => 5)
|
346
|
+
end
|
347
|
+
|
348
|
+
it "should filter the given params using the model columns" do
|
349
|
+
@o1.set_with_params(:x => 1, :z => 2)
|
350
|
+
@o1.values.should == {:x => 1}
|
351
|
+
MODEL_DB.sqls.should == []
|
352
|
+
|
353
|
+
@o2.set_with_params(:y => 1, :abc => 2)
|
354
|
+
@o2.values.should == {:y => 1, :id=> 5}
|
355
|
+
MODEL_DB.sqls.should == []
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should work with both strings and symbols" do
|
359
|
+
@o1.set_with_params('x'=> 1, 'z'=> 2)
|
360
|
+
@o1.values.should == {:x => 1}
|
361
|
+
MODEL_DB.sqls.should == []
|
362
|
+
|
363
|
+
@o2.set_with_params('y'=> 1, 'abc'=> 2)
|
364
|
+
@o2.values.should == {:y => 1, :id=> 5}
|
365
|
+
MODEL_DB.sqls.should == []
|
366
|
+
end
|
367
|
+
|
368
|
+
it "should support virtual attributes" do
|
369
|
+
@c.class_def(:blah=) {|v| self.x = v}
|
370
|
+
@o1.set_with_params(:blah => 333)
|
371
|
+
@o1.values.should == {:x => 333}
|
372
|
+
MODEL_DB.sqls.should == []
|
373
|
+
@o1.set_with_params('blah'=> 334)
|
374
|
+
@o1.values.should == {:x => 334}
|
375
|
+
MODEL_DB.sqls.should == []
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
316
379
|
describe Sequel::Model, "update_with_params" do
|
317
380
|
|
318
381
|
before(:each) do
|
319
382
|
MODEL_DB.reset
|
320
383
|
|
321
384
|
@c = Class.new(Sequel::Model(:items)) do
|
322
|
-
|
385
|
+
columns :x, :y, :id
|
323
386
|
end
|
387
|
+
@c.instance_variable_set(:@columns, true)
|
324
388
|
@o1 = @c.new
|
325
389
|
@o2 = @c.load(:id => 5)
|
326
390
|
end
|
@@ -334,7 +398,8 @@ describe Sequel::Model, "update_with_params" do
|
|
334
398
|
MODEL_DB.sqls.first.should == "UPDATE items SET y = 1 WHERE (id = 5)"
|
335
399
|
end
|
336
400
|
|
337
|
-
|
401
|
+
### DEPRECATE
|
402
|
+
it "should be aliased by update_with" do
|
338
403
|
@o1.update_with(:x => 1, :z => 2)
|
339
404
|
MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
|
340
405
|
|
@@ -345,19 +410,21 @@ describe Sequel::Model, "update_with_params" do
|
|
345
410
|
|
346
411
|
it "should support virtual attributes" do
|
347
412
|
@c.class_def(:blah=) {|v| self.x = v}
|
348
|
-
@o1.
|
413
|
+
@o1.update_with_params(:blah => 333)
|
349
414
|
MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (333)"
|
350
415
|
end
|
351
416
|
end
|
352
417
|
|
418
|
+
### DEPRECATE
|
353
419
|
describe Sequel::Model, "create_with_params" do
|
354
420
|
|
355
421
|
before(:each) do
|
356
422
|
MODEL_DB.reset
|
357
423
|
|
358
424
|
@c = Class.new(Sequel::Model(:items)) do
|
359
|
-
|
425
|
+
columns :x, :y
|
360
426
|
end
|
427
|
+
@c.instance_variable_set(:@columns, true)
|
361
428
|
end
|
362
429
|
|
363
430
|
it "should filter the given params using the model columns" do
|
@@ -390,12 +457,19 @@ describe Sequel::Model, "#destroy" do
|
|
390
457
|
before(:each) do
|
391
458
|
MODEL_DB.reset
|
392
459
|
@model = Class.new(Sequel::Model(:items))
|
460
|
+
@model.columns :id
|
393
461
|
@model.dataset.meta_def(:delete) {MODEL_DB.execute delete_sql}
|
394
462
|
|
395
463
|
@instance = @model.new(:id => 1234)
|
396
464
|
#@model.stub!(:delete).and_return(:true)
|
397
465
|
end
|
398
466
|
|
467
|
+
it "should return self" do
|
468
|
+
@model.db.should_receive(:transaction)
|
469
|
+
@model.after_destroy{3}
|
470
|
+
@instance.destroy.should == @instance
|
471
|
+
end
|
472
|
+
|
399
473
|
it "should run within a transaction" do
|
400
474
|
@model.db.should_receive(:transaction)
|
401
475
|
@instance.destroy
|
@@ -434,6 +508,7 @@ end
|
|
434
508
|
describe Sequel::Model, "#each" do
|
435
509
|
setup do
|
436
510
|
@model = Class.new(Sequel::Model(:items))
|
511
|
+
@model.columns :a, :b, :id
|
437
512
|
@m = @model.new(:a => 1, :b => 2, :id => 4444)
|
438
513
|
end
|
439
514
|
|
@@ -447,6 +522,7 @@ end
|
|
447
522
|
describe Sequel::Model, "#keys" do
|
448
523
|
setup do
|
449
524
|
@model = Class.new(Sequel::Model(:items))
|
525
|
+
@model.columns :a, :b, :id
|
450
526
|
@m = @model.new(:a => 1, :b => 2, :id => 4444)
|
451
527
|
end
|
452
528
|
|
@@ -459,32 +535,100 @@ describe Sequel::Model, "#keys" do
|
|
459
535
|
end
|
460
536
|
end
|
461
537
|
|
462
|
-
describe Sequel::Model, "
|
538
|
+
describe Sequel::Model, "#==" do
|
463
539
|
specify "should compare instances by values" do
|
464
|
-
|
465
|
-
|
466
|
-
|
540
|
+
z = Class.new(Sequel::Model)
|
541
|
+
z.columns :id, :x
|
542
|
+
a = z.new(:id => 1, :x => 3)
|
543
|
+
b = z.new(:id => 1, :x => 4)
|
544
|
+
c = z.new(:id => 1, :x => 3)
|
467
545
|
|
468
546
|
a.should_not == b
|
469
547
|
a.should == c
|
470
548
|
b.should_not == c
|
471
549
|
end
|
550
|
+
|
551
|
+
specify "should be aliased to #eql?" do
|
552
|
+
z = Class.new(Sequel::Model)
|
553
|
+
z.columns :id, :x
|
554
|
+
a = z.new(:id => 1, :x => 3)
|
555
|
+
b = z.new(:id => 1, :x => 4)
|
556
|
+
c = z.new(:id => 1, :x => 3)
|
557
|
+
|
558
|
+
a.eql?(b).should == false
|
559
|
+
a.eql?(c).should == true
|
560
|
+
b.eql?(c).should == false
|
561
|
+
end
|
472
562
|
end
|
473
563
|
|
474
564
|
describe Sequel::Model, "#===" do
|
475
|
-
specify "should compare instances by pk
|
476
|
-
|
477
|
-
|
478
|
-
|
565
|
+
specify "should compare instances by class and pk if pk is not nil" do
|
566
|
+
z = Class.new(Sequel::Model)
|
567
|
+
z.columns :id, :x
|
568
|
+
y = Class.new(Sequel::Model)
|
569
|
+
y.columns :id, :x
|
570
|
+
a = z.new(:id => 1, :x => 3)
|
571
|
+
b = z.new(:id => 1, :x => 4)
|
572
|
+
c = z.new(:id => 2, :x => 3)
|
573
|
+
d = y.new(:id => 1, :x => 3)
|
479
574
|
|
480
575
|
a.should === b
|
481
576
|
a.should_not === c
|
577
|
+
a.should_not === d
|
578
|
+
end
|
579
|
+
|
580
|
+
specify "should always be false if the primary key is nil" do
|
581
|
+
z = Class.new(Sequel::Model)
|
582
|
+
z.columns :id, :x
|
583
|
+
y = Class.new(Sequel::Model)
|
584
|
+
y.columns :id, :x
|
585
|
+
a = z.new(:x => 3)
|
586
|
+
b = z.new(:x => 4)
|
587
|
+
c = z.new(:x => 3)
|
588
|
+
d = y.new(:x => 3)
|
589
|
+
|
590
|
+
a.should_not === b
|
591
|
+
a.should_not === c
|
592
|
+
a.should_not === d
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
describe Sequel::Model, "#hash" do
|
597
|
+
specify "should be the same only for objects with the same class and pk if the pk is not nil" do
|
598
|
+
z = Class.new(Sequel::Model)
|
599
|
+
z.columns :id, :x
|
600
|
+
y = Class.new(Sequel::Model)
|
601
|
+
y.columns :id, :x
|
602
|
+
a = z.new(:id => 1, :x => 3)
|
603
|
+
b = z.new(:id => 1, :x => 4)
|
604
|
+
c = z.new(:id => 2, :x => 3)
|
605
|
+
d = y.new(:id => 1, :x => 3)
|
606
|
+
|
607
|
+
a.hash.should == b.hash
|
608
|
+
a.hash.should_not == c.hash
|
609
|
+
a.hash.should_not == d.hash
|
610
|
+
end
|
611
|
+
|
612
|
+
specify "should be the same only for objects with the same class and values if the pk is nil" do
|
613
|
+
z = Class.new(Sequel::Model)
|
614
|
+
z.columns :id, :x
|
615
|
+
y = Class.new(Sequel::Model)
|
616
|
+
y.columns :id, :x
|
617
|
+
a = z.new(:x => 3)
|
618
|
+
b = z.new(:x => 4)
|
619
|
+
c = z.new(:x => 3)
|
620
|
+
d = y.new(:x => 3)
|
621
|
+
|
622
|
+
a.hash.should_not == b.hash
|
623
|
+
a.hash.should == c.hash
|
624
|
+
a.hash.should_not == d.hash
|
482
625
|
end
|
483
626
|
end
|
484
627
|
|
485
628
|
describe Sequel::Model, "#initialize" do
|
486
629
|
setup do
|
487
630
|
@c = Class.new(Sequel::Model) do
|
631
|
+
columns :id, :x
|
488
632
|
end
|
489
633
|
end
|
490
634
|
|
@@ -528,7 +672,7 @@ describe Sequel::Model, ".create" do
|
|
528
672
|
before(:each) do
|
529
673
|
MODEL_DB.reset
|
530
674
|
@c = Class.new(Sequel::Model(:items)) do
|
531
|
-
|
675
|
+
columns :x
|
532
676
|
end
|
533
677
|
end
|
534
678
|
|
@@ -566,7 +710,7 @@ describe Sequel::Model, "#refresh" do
|
|
566
710
|
setup do
|
567
711
|
MODEL_DB.reset
|
568
712
|
@c = Class.new(Sequel::Model(:items)) do
|
569
|
-
|
713
|
+
columns :x
|
570
714
|
end
|
571
715
|
end
|
572
716
|
|
@@ -590,4 +734,18 @@ describe Sequel::Model, "#refresh" do
|
|
590
734
|
@m.reload
|
591
735
|
@m[:x].should == 'kaboom'
|
592
736
|
end
|
737
|
+
|
738
|
+
specify "should remove cached associations" do
|
739
|
+
@c.many_to_one :node, :class=>@c
|
740
|
+
@c.one_to_many :attributes, :class=>@c
|
741
|
+
@c.many_to_many :tags, :class=>@c
|
742
|
+
@m = @c.new(:id => 555)
|
743
|
+
@m.instance_variable_set(:@node, 15)
|
744
|
+
@m.instance_variable_set(:@attributes, [15])
|
745
|
+
@m.instance_variable_set(:@tags, [15])
|
746
|
+
@m.reload
|
747
|
+
@m.instance_variable_get(:@node).should == nil
|
748
|
+
@m.instance_variable_get(:@attributes).should == nil
|
749
|
+
@m.instance_variable_get(:@tags).should == nil
|
750
|
+
end
|
593
751
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
unless Object.const_defined?('Sequel')
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), "../../sequel_core/lib/"))
|
3
4
|
require 'sequel_core'
|
4
5
|
end
|
5
|
-
|
6
|
+
unless Sequel.const_defined?('Model')
|
7
|
+
$:.unshift(File.join(File.dirname(__FILE__), "../lib/"))
|
8
|
+
require 'sequel_model'
|
9
|
+
end
|
6
10
|
|
7
11
|
class MockDataset < Sequel::Dataset
|
8
12
|
def insert(*args)
|
@@ -40,4 +44,15 @@ class MockDatabase < Sequel::Database
|
|
40
44
|
def dataset; MockDataset.new(self); end
|
41
45
|
end
|
42
46
|
|
43
|
-
Sequel::Model
|
47
|
+
class << Sequel::Model
|
48
|
+
alias orig_columns columns
|
49
|
+
def columns(*cols)
|
50
|
+
return if cols.empty?
|
51
|
+
define_method(:columns){cols}
|
52
|
+
@dataset.instance_variable_set(:@columns, cols) if @dataset
|
53
|
+
define_method(:str_columns){cols.map{|x|x.to_s.freeze}}
|
54
|
+
def_column_accessor(*cols)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
Sequel::Model.db = MODEL_DB = MockDatabase.new
|
data/spec/validations_spec.rb
CHANGED
@@ -1,33 +1,386 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
2
|
|
3
|
+
describe "Validation::Errors" do
|
4
|
+
setup do
|
5
|
+
@errors = Validation::Errors.new
|
6
|
+
class Validation::Errors
|
7
|
+
attr_accessor :errors
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
specify "should be clearable using #clear" do
|
12
|
+
@errors.errors = {1 => 2, 3 => 4}
|
13
|
+
@errors.clear
|
14
|
+
@errors.errors.should == {}
|
15
|
+
end
|
16
|
+
|
17
|
+
specify "should be empty if no errors are added" do
|
18
|
+
@errors.should be_empty
|
19
|
+
@errors[:blah] << "blah"
|
20
|
+
@errors.should_not be_empty
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "should return errors for a specific attribute using #on or #[]" do
|
24
|
+
@errors[:blah].should == []
|
25
|
+
@errors.on(:blah).should == []
|
26
|
+
|
27
|
+
@errors[:blah] << 'blah'
|
28
|
+
@errors[:blah].should == ['blah']
|
29
|
+
@errors.on(:blah).should == ['blah']
|
30
|
+
|
31
|
+
@errors[:bleu].should == []
|
32
|
+
@errors.on(:bleu).should == []
|
33
|
+
end
|
34
|
+
|
35
|
+
specify "should accept errors using #[] << or #add" do
|
36
|
+
@errors[:blah] << 'blah'
|
37
|
+
@errors[:blah].should == ['blah']
|
38
|
+
|
39
|
+
@errors.add :blah, 'zzzz'
|
40
|
+
@errors[:blah].should == ['blah', 'zzzz']
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "should return full messages using #full_messages" do
|
44
|
+
@errors.full_messages.should == []
|
45
|
+
|
46
|
+
@errors[:blow] << 'blieuh'
|
47
|
+
@errors[:blow] << 'blich'
|
48
|
+
@errors[:blay] << 'bliu'
|
49
|
+
msgs = @errors.full_messages
|
50
|
+
msgs.size.should == 3
|
51
|
+
msgs.should include('blow blieuh', 'blow blich', 'blay bliu')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe Validation do
|
56
|
+
setup do
|
57
|
+
@c = Class.new do
|
58
|
+
include Validation
|
59
|
+
|
60
|
+
def self.validates_coolness_of(attr)
|
61
|
+
validates_each(attr) {|o, a, v| o.errors[a] << 'is not cool' if v != :cool}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
@d = Class.new do
|
66
|
+
attr_accessor :errors
|
67
|
+
def initialize; @errors = Validation::Errors.new; end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
specify "should respond to validates, validations, has_validations?" do
|
72
|
+
@c.should respond_to(:validations)
|
73
|
+
@c.should respond_to(:has_validations?)
|
74
|
+
end
|
75
|
+
|
76
|
+
specify "should acccept validation definitions using validates_each" do
|
77
|
+
@c.validates_each(:xx, :yy) {|o, a, v| o.errors[a] << 'too low' if v < 50}
|
78
|
+
|
79
|
+
@c.validations[:xx].size.should == 1
|
80
|
+
@c.validations[:yy].size.should == 1
|
81
|
+
|
82
|
+
o = @d.new
|
83
|
+
@c.validations[:xx].first.call(o, :aa, 40)
|
84
|
+
@c.validations[:yy].first.call(o, :bb, 60)
|
85
|
+
|
86
|
+
o.errors.full_messages.should == ['aa too low']
|
87
|
+
end
|
88
|
+
|
89
|
+
specify "should return true/false for has_validations?" do
|
90
|
+
@c.has_validations?.should == false
|
91
|
+
@c.validates_each(:xx) {1}
|
92
|
+
@c.has_validations?.should == true
|
93
|
+
end
|
94
|
+
|
95
|
+
specify "should provide a validates method that takes block with validation definitions" do
|
96
|
+
@c.validates do
|
97
|
+
coolness_of :blah
|
98
|
+
end
|
99
|
+
@c.validations[:blah].should_not be_empty
|
100
|
+
|
101
|
+
o = @d.new
|
102
|
+
@c.validations[:blah].first.call(o, :ttt, 40)
|
103
|
+
o.errors.full_messages.should == ['ttt is not cool']
|
104
|
+
o.errors.clear
|
105
|
+
@c.validations[:blah].first.call(o, :ttt, :cool)
|
106
|
+
o.errors.should be_empty
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "A Validation instance" do
|
111
|
+
setup do
|
112
|
+
@c = Class.new do
|
113
|
+
attr_accessor :score
|
114
|
+
|
115
|
+
include Validation
|
116
|
+
|
117
|
+
validates_each :score do |o, a, v|
|
118
|
+
o.errors[a] << 'too low' if v < 87
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
@o = @c.new
|
123
|
+
end
|
124
|
+
|
125
|
+
specify "should supply a #valid? method that returns true if validations pass" do
|
126
|
+
@o.score = 50
|
127
|
+
@o.should_not be_valid
|
128
|
+
@o.score = 100
|
129
|
+
@o.should be_valid
|
130
|
+
end
|
131
|
+
|
132
|
+
specify "should provide an errors object" do
|
133
|
+
@o.score = 100
|
134
|
+
@o.should be_valid
|
135
|
+
@o.errors.should be_empty
|
136
|
+
|
137
|
+
@o.score = 86
|
138
|
+
@o.should_not be_valid
|
139
|
+
@o.errors[:score].should == ['too low']
|
140
|
+
@o.errors[:blah].should be_empty
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe Validation::Generator do
|
145
|
+
setup do
|
146
|
+
$testit = nil
|
147
|
+
|
148
|
+
@c = Class.new do
|
149
|
+
include Validation
|
150
|
+
|
151
|
+
def self.validates_blah
|
152
|
+
$testit = 1324
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
specify "should instance_eval the block, sending everything to its receiver" do
|
158
|
+
Validation::Generator.new(@c) do
|
159
|
+
blah
|
160
|
+
end
|
161
|
+
$testit.should == 1324
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "Validations" do
|
166
|
+
setup do
|
167
|
+
@c = Class.new do
|
168
|
+
attr_accessor :value
|
169
|
+
include Validation
|
170
|
+
end
|
171
|
+
@m = @c.new
|
172
|
+
end
|
173
|
+
|
174
|
+
specify "should validate acceptance_of" do
|
175
|
+
@c.validates_acceptance_of :value
|
176
|
+
@m.should be_valid
|
177
|
+
@m.value = '1'
|
178
|
+
@m.should be_valid
|
179
|
+
end
|
180
|
+
|
181
|
+
specify "should validate acceptance_of with accept" do
|
182
|
+
@c.validates_acceptance_of :value, :accept => 'true'
|
183
|
+
@m.value = '1'
|
184
|
+
@m.should_not be_valid
|
185
|
+
@m.value = 'true'
|
186
|
+
@m.should be_valid
|
187
|
+
end
|
188
|
+
|
189
|
+
specify "should validate acceptance_of with allow_nil => false" do
|
190
|
+
@c.validates_acceptance_of :value, :allow_nil => false
|
191
|
+
@m.should_not be_valid
|
192
|
+
end
|
193
|
+
|
194
|
+
specify "should validate confirmation_of" do
|
195
|
+
@c.send(:attr_accessor, :value_confirmation)
|
196
|
+
@c.validates_confirmation_of :value
|
197
|
+
|
198
|
+
@m.value = 'blah'
|
199
|
+
@m.should_not be_valid
|
200
|
+
|
201
|
+
@m.value_confirmation = 'blah'
|
202
|
+
@m.should be_valid
|
203
|
+
end
|
204
|
+
|
205
|
+
specify "should validate format_of" do
|
206
|
+
@c.validates_format_of :value, :with => /.+_.+/
|
207
|
+
@m.value = 'abc_'
|
208
|
+
@m.should_not be_valid
|
209
|
+
@m.value = 'abc_def'
|
210
|
+
@m.should be_valid
|
211
|
+
end
|
212
|
+
|
213
|
+
specify "should raise for validate_format_of without regexp" do
|
214
|
+
proc {@c.validates_format_of :value}.should raise_error(ArgumentError)
|
215
|
+
proc {@c.validates_format_of :value, :with => :blah}.should raise_error(ArgumentError)
|
216
|
+
end
|
217
|
+
|
218
|
+
specify "should validate length_of with maximum" do
|
219
|
+
@c.validates_length_of :value, :maximum => 5
|
220
|
+
@m.should_not be_valid
|
221
|
+
@m.value = '12345'
|
222
|
+
@m.should be_valid
|
223
|
+
@m.value = '123456'
|
224
|
+
@m.should_not be_valid
|
225
|
+
end
|
226
|
+
|
227
|
+
specify "should validate length_of with minimum" do
|
228
|
+
@c.validates_length_of :value, :minimum => 5
|
229
|
+
@m.should_not be_valid
|
230
|
+
@m.value = '12345'
|
231
|
+
@m.should be_valid
|
232
|
+
@m.value = '1234'
|
233
|
+
@m.should_not be_valid
|
234
|
+
end
|
235
|
+
|
236
|
+
specify "should validate length_of with within" do
|
237
|
+
@c.validates_length_of :value, :within => 2..5
|
238
|
+
@m.should_not be_valid
|
239
|
+
@m.value = '12345'
|
240
|
+
@m.should be_valid
|
241
|
+
@m.value = '1'
|
242
|
+
@m.should_not be_valid
|
243
|
+
@m.value = '123456'
|
244
|
+
@m.should_not be_valid
|
245
|
+
end
|
246
|
+
|
247
|
+
specify "should validate length_of with is" do
|
248
|
+
@c.validates_length_of :value, :is => 3
|
249
|
+
@m.should_not be_valid
|
250
|
+
@m.value = '123'
|
251
|
+
@m.should be_valid
|
252
|
+
@m.value = '12'
|
253
|
+
@m.should_not be_valid
|
254
|
+
@m.value = '1234'
|
255
|
+
@m.should_not be_valid
|
256
|
+
end
|
257
|
+
|
258
|
+
specify "should validate length_of with allow_nil" do
|
259
|
+
@c.validates_length_of :value, :is => 3, :allow_nil => true
|
260
|
+
@m.should be_valid
|
261
|
+
end
|
262
|
+
|
263
|
+
specify "should validate numericality_of" do
|
264
|
+
@c.validates_numericality_of :value
|
265
|
+
@m.value = 'blah'
|
266
|
+
@m.should_not be_valid
|
267
|
+
@m.value = '123'
|
268
|
+
@m.should be_valid
|
269
|
+
@m.value = '123.1231'
|
270
|
+
@m.should be_valid
|
271
|
+
end
|
272
|
+
|
273
|
+
specify "should validate numericality_of with only_integer" do
|
274
|
+
@c.validates_numericality_of :value, :only_integer => true
|
275
|
+
@m.value = 'blah'
|
276
|
+
@m.should_not be_valid
|
277
|
+
@m.value = '123'
|
278
|
+
@m.should be_valid
|
279
|
+
@m.value = '123.1231'
|
280
|
+
@m.should_not be_valid
|
281
|
+
end
|
282
|
+
|
283
|
+
specify "should validate presence_of" do
|
284
|
+
@c.validates_presence_of :value
|
285
|
+
@m.should_not be_valid
|
286
|
+
@m.value = ''
|
287
|
+
@m.should_not be_valid
|
288
|
+
@m.value = 1234
|
289
|
+
@m.should be_valid
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
context "Superclass validations" do
|
294
|
+
setup do
|
295
|
+
@c1 = Class.new do
|
296
|
+
include Validation
|
297
|
+
attr_accessor :value
|
298
|
+
validates_length_of :value, :minimum => 5
|
299
|
+
end
|
300
|
+
|
301
|
+
@c2 = Class.new(@c1) do
|
302
|
+
validates_format_of :value, :with => /^[a-z]+$/
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
specify "should be checked when validating" do
|
307
|
+
o = @c2.new
|
308
|
+
o.value = 'ab'
|
309
|
+
o.valid?.should == false
|
310
|
+
o.errors.full_messages.should == [
|
311
|
+
'value is too short'
|
312
|
+
]
|
313
|
+
|
314
|
+
o.value = '12'
|
315
|
+
o.valid?.should == false
|
316
|
+
o.errors.full_messages.should == [
|
317
|
+
'value is too short',
|
318
|
+
'value is invalid'
|
319
|
+
]
|
320
|
+
|
321
|
+
o.value = 'abcde'
|
322
|
+
o.valid?.should be_true
|
323
|
+
end
|
324
|
+
|
325
|
+
specify "should be skipped if skip_superclass_validations is called" do
|
326
|
+
@c2.skip_superclass_validations
|
327
|
+
|
328
|
+
o = @c2.new
|
329
|
+
o.value = 'ab'
|
330
|
+
o.valid?.should be_true
|
331
|
+
|
332
|
+
o.value = '12'
|
333
|
+
o.valid?.should == false
|
334
|
+
o.errors.full_messages.should == [
|
335
|
+
'value is invalid'
|
336
|
+
]
|
337
|
+
|
338
|
+
o.value = 'abcde'
|
339
|
+
o.valid?.should be_true
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
context ".validates with block" do
|
344
|
+
specify "should support calling .each" do
|
345
|
+
@c = Class.new do
|
346
|
+
attr_accessor :vvv
|
347
|
+
|
348
|
+
include Validation
|
349
|
+
validates do
|
350
|
+
each :vvv do |o, a, v|
|
351
|
+
o.errors[a] << "is less than zero" if v.to_i < 0
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
o = @c.new
|
357
|
+
o.vvv = 1
|
358
|
+
o.should be_valid
|
359
|
+
o.vvv = -1
|
360
|
+
o.should_not be_valid
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
3
364
|
describe Sequel::Model, "Validations" do
|
4
365
|
|
5
366
|
before(:all) do
|
6
367
|
class Person < Sequel::Model
|
7
|
-
|
8
|
-
[:id,:name,:first_name,:last_name,:middle_name,:initials,:age, :terms]
|
9
|
-
end
|
368
|
+
columns :id,:name,:first_name,:last_name,:middle_name,:initials,:age, :terms
|
10
369
|
end
|
11
370
|
|
12
371
|
class Smurf < Person
|
13
372
|
end
|
14
373
|
|
15
374
|
class Cow < Sequel::Model
|
16
|
-
|
17
|
-
[:id, :name, :got_milk]
|
18
|
-
end
|
375
|
+
columns :id, :name, :got_milk
|
19
376
|
end
|
20
377
|
|
21
378
|
class User < Sequel::Model
|
22
|
-
|
23
|
-
[:id, :username, :password]
|
24
|
-
end
|
379
|
+
columns :id, :username, :password
|
25
380
|
end
|
26
381
|
|
27
382
|
class Address < Sequel::Model
|
28
|
-
|
29
|
-
[:id, :zip_code]
|
30
|
-
end
|
383
|
+
columns :id, :zip_code
|
31
384
|
end
|
32
385
|
end
|
33
386
|
|
@@ -137,6 +490,52 @@ describe Sequel::Model, "Validations" do
|
|
137
490
|
@cow.name = "Betsy"
|
138
491
|
@cow.should be_valid
|
139
492
|
end
|
493
|
+
|
494
|
+
it "should validate the uniqueness of a column" do
|
495
|
+
class User < Sequel::Model
|
496
|
+
validations.clear
|
497
|
+
validates do
|
498
|
+
uniqueness_of :username
|
499
|
+
end
|
500
|
+
end
|
501
|
+
User.dataset.extend(Module.new {
|
502
|
+
def fetch_rows(sql)
|
503
|
+
@db << sql
|
504
|
+
|
505
|
+
case sql
|
506
|
+
when /COUNT.*username = '0records'/
|
507
|
+
when /COUNT.*username = '2records'/
|
508
|
+
yield({:v => 2})
|
509
|
+
when /COUNT.*username = '1record'/
|
510
|
+
yield({:v => 1})
|
511
|
+
when /username = '1record'/
|
512
|
+
yield({:id => 3, :username => "1record", :password => "test"})
|
513
|
+
end
|
514
|
+
end
|
515
|
+
})
|
516
|
+
|
517
|
+
@user = User.new(:username => "2records", :password => "anothertest")
|
518
|
+
@user.should_not be_valid
|
519
|
+
@user.errors.full_messages.should == ['username is already taken']
|
520
|
+
|
521
|
+
@user = User.new(:username => "1record", :password => "anothertest")
|
522
|
+
@user.should_not be_valid
|
523
|
+
@user.errors.full_messages.should == ['username is already taken']
|
524
|
+
|
525
|
+
@user = User.new(:id=>4, :username => "1record", :password => "anothertest")
|
526
|
+
@user.instance_variable_set(:@new, false)
|
527
|
+
@user.should_not be_valid
|
528
|
+
@user.errors.full_messages.should == ['username is already taken']
|
529
|
+
|
530
|
+
@user = User.new(:id=>3, :username => "1record", :password => "anothertest")
|
531
|
+
@user.instance_variable_set(:@new, false)
|
532
|
+
@user.should be_valid
|
533
|
+
@user.errors.full_messages.should == []
|
534
|
+
|
535
|
+
@user = User.new(:username => "0records", :password => "anothertest")
|
536
|
+
@user.should be_valid
|
537
|
+
@user.errors.full_messages.should == []
|
538
|
+
end
|
140
539
|
|
141
540
|
it "should have a validates block that contains multiple validations" do
|
142
541
|
class Person < Sequel::Model
|
@@ -185,7 +584,7 @@ describe Sequel::Model, "Validations" do
|
|
185
584
|
|
186
585
|
it "should validate correctly instances initialized with string keys" do
|
187
586
|
class Can < Sequel::Model
|
188
|
-
|
587
|
+
columns :id, :name
|
189
588
|
|
190
589
|
validates_length_of :name, :minimum => 4
|
191
590
|
end
|
@@ -219,7 +618,7 @@ end
|
|
219
618
|
describe "Model#save" do
|
220
619
|
setup do
|
221
620
|
@c = Class.new(Sequel::Model(:people)) do
|
222
|
-
|
621
|
+
columns :id
|
223
622
|
|
224
623
|
validates_each :id do |o, a, v|
|
225
624
|
o.errors[a] << 'blah' unless v == 5
|
@@ -243,4 +642,4 @@ describe "Model#save" do
|
|
243
642
|
specify "should return false if validations fail" do
|
244
643
|
@m.save.should == false
|
245
644
|
end
|
246
|
-
end
|
645
|
+
end
|