dm-validations 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/LICENSE +20 -0
  2. data/README +72 -0
  3. data/Rakefile +74 -0
  4. data/TODO +16 -0
  5. data/lib/dm-validations.rb +208 -0
  6. data/lib/dm-validations/absent_field_validator.rb +60 -0
  7. data/lib/dm-validations/acceptance_validator.rb +76 -0
  8. data/lib/dm-validations/auto_validate.rb +98 -0
  9. data/lib/dm-validations/confirmation_validator.rb +76 -0
  10. data/lib/dm-validations/contextual_validators.rb +56 -0
  11. data/lib/dm-validations/custom_validator.rb +72 -0
  12. data/lib/dm-validations/format_validator.rb +94 -0
  13. data/lib/dm-validations/formats/email.rb +40 -0
  14. data/lib/dm-validations/generic_validator.rb +92 -0
  15. data/lib/dm-validations/length_validator.rb +113 -0
  16. data/lib/dm-validations/method_validator.rb +58 -0
  17. data/lib/dm-validations/numeric_validator.rb +66 -0
  18. data/lib/dm-validations/primitive_validator.rb +60 -0
  19. data/lib/dm-validations/required_field_validator.rb +88 -0
  20. data/lib/dm-validations/support/object.rb +5 -0
  21. data/lib/dm-validations/uniqueness_validator.rb +61 -0
  22. data/lib/dm-validations/validation_errors.rb +63 -0
  23. data/lib/dm-validations/within_validator.rb +41 -0
  24. data/spec/integration/absent_field_validator_spec.rb +34 -0
  25. data/spec/integration/acceptance_validator_spec.rb +87 -0
  26. data/spec/integration/auto_validate_spec.rb +262 -0
  27. data/spec/integration/confirmation_validator_spec.rb +66 -0
  28. data/spec/integration/contextual_validators_spec.rb +28 -0
  29. data/spec/integration/custom_validator_spec.rb +9 -0
  30. data/spec/integration/format_validator_spec.rb +118 -0
  31. data/spec/integration/generic_validator_spec.rb +9 -0
  32. data/spec/integration/length_validator_spec.rb +113 -0
  33. data/spec/integration/method_validator_spec.rb +31 -0
  34. data/spec/integration/numeric_validator_spec.rb +192 -0
  35. data/spec/integration/primitive_validator_spec.rb +25 -0
  36. data/spec/integration/required_field_validator_spec.rb +93 -0
  37. data/spec/integration/uniqueness_validator_spec.rb +81 -0
  38. data/spec/integration/validation_errors_spec.rb +18 -0
  39. data/spec/integration/validation_spec.rb +339 -0
  40. data/spec/integration/within_validator_spec.rb +35 -0
  41. data/spec/spec.opts +2 -0
  42. data/spec/spec_helper.rb +26 -0
  43. metadata +104 -0
@@ -0,0 +1,25 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ class Monica # :nodoc:
5
+ include DataMapper::Resource
6
+ property :id, Integer, :serial => true
7
+ property :birth_date, Date, :auto_validation => false
8
+ validates_is_primitive :birth_date
9
+ end
10
+
11
+ describe DataMapper::Validate::PrimitiveValidator do
12
+ it "should validate a property to check for the type" do
13
+ b = Monica.new
14
+ p b.valid?
15
+ p b.errors
16
+ b.should be_valid
17
+
18
+ b.birth_date = 'ABC'
19
+ b.should_not be_valid
20
+ b.birth_date.should eql('ABC')
21
+ b.birth_date = '2008-01-01'
22
+ b.should be_valid
23
+ b.birth_date.should eql(Date.civil(2008,1,1))
24
+ end
25
+ end
@@ -0,0 +1,93 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
5
+ describe DataMapper::Validate::RequiredFieldValidator do
6
+
7
+ describe "Resources" do
8
+ before do
9
+ class Landscaper
10
+ include DataMapper::Resource
11
+ property :id, Integer, :key => true
12
+ property :name, String
13
+ end
14
+
15
+ class Garden
16
+ include DataMapper::Resource
17
+ property :id, Integer, :key => true
18
+ property :landscaper_id, Integer
19
+ property :name, String, :auto_validation => false
20
+
21
+ belongs_to :landscaper #has :landscaper, 1..n
22
+
23
+ validates_present :name, :when => :property_test
24
+ validates_present :landscaper, :when => :association_test
25
+ end
26
+
27
+ class Fertilizer
28
+ include DataMapper::Resource
29
+ property :id, Integer, :serial => true
30
+ property :brand, String, :auto_validation => false, :default => 'Scotts'
31
+ validates_present :brand, :when => :property_test
32
+ end
33
+
34
+ Landscaper.auto_migrate!
35
+ Garden.auto_migrate!
36
+ Fertilizer.auto_migrate!
37
+ end
38
+
39
+ it "should validate the presence of a property value on an instance of a resource" do
40
+ garden = Garden.new
41
+ garden.should_not be_valid_for_property_test
42
+ garden.errors.on(:name).should include('Name must not be blank')
43
+
44
+ garden.name = 'The Wilds'
45
+ garden.should be_valid_for_property_test
46
+ end
47
+
48
+ it "should validate the presence of an association value on an instance of a resource when dirty"
49
+ #do
50
+ # garden = Garden.new
51
+ # landscaper = garden.landscaper
52
+ # puts landscaper.children.length
53
+ # #puts "Gardens landscaper is #{garden.landscaper.child_key}"
54
+ #end
55
+
56
+ it "should pass when a default is available" do
57
+ fert = Fertilizer.new
58
+ fert.should be_valid_for_property_test
59
+ end
60
+ end
61
+
62
+ describe "A plain class (not a DM resource)" do
63
+
64
+ before do
65
+ class PlainClass
66
+ extend DataMapper::Validate::ClassMethods
67
+ include DataMapper::Validate
68
+ attr_accessor :accessor
69
+ validates_present :here, :empty, :nil, :accessor
70
+ def here; "here" end
71
+ def empty; "" end
72
+ def nil; nil end
73
+ end
74
+
75
+ @pc = PlainClass.new
76
+ end
77
+
78
+ it "should fail validation with empty, nil, or blank fields" do
79
+ @pc.should_not be_valid
80
+ @pc.errors.on(:empty).should include("Empty must not be blank")
81
+ @pc.errors.on(:nil).should include("Nil must not be blank")
82
+ @pc.errors.on(:accessor).should include("Accessor must not be blank")
83
+ end
84
+
85
+ it "giving accessor a value should remove validation error" do
86
+ @pc.accessor = "full"
87
+ @pc.valid?
88
+ @pc.errors.on(:accessor).should be_nil
89
+ end
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,81 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
5
+ describe DataMapper::Validate::UniquenessValidator do
6
+
7
+ before do
8
+ class Organisation
9
+ include DataMapper::Resource
10
+ property :id, Serial
11
+ property :name, String
12
+ property :domain, String #, :unique => true
13
+
14
+ validates_is_unique :domain, :allow_nil => true
15
+ end
16
+
17
+ class User
18
+ include DataMapper::Resource
19
+ property :id, Serial
20
+ property :organisation_id, Integer
21
+ property :user_name, String
22
+
23
+ belongs_to :organisation #has :organisation, n..1
24
+
25
+ validates_is_unique :user_name, :when => :testing_association, :scope => [:organisation]
26
+ validates_is_unique :user_name, :when => :testing_property, :scope => [:organisation_id]
27
+ end
28
+
29
+ Organisation.auto_migrate!
30
+ User.auto_migrate!
31
+
32
+ repository do
33
+ Organisation.new(:id=>1, :name=>'Org One', :domain=>'taken').save
34
+ Organisation.new(:id=>2, :name=>'Org Two', :domain=>'two').save
35
+
36
+ User.new(:id=>1,:organisation_id=>1,:user_name=>'guy').save
37
+ end
38
+ end
39
+
40
+ it 'should validate the uniqueness of a value on a resource' do
41
+ repository do
42
+ o = Organisation.get!(1)
43
+ o.should be_valid
44
+
45
+ o = Organisation.new(:id=>20,:name=>"Org Twenty", :domain=>nil)
46
+ o.should be_valid
47
+ o.save
48
+
49
+ o = Organisation.new(:id=>30,:name=>"Org Thirty", :domain=>nil)
50
+ o.should be_valid
51
+ end
52
+ end
53
+
54
+ it "should not even check if :allow_nil is true" do
55
+ repository do
56
+ o = Organisation.get!(1)
57
+ o.should be_valid
58
+
59
+ o = Organisation.new(:id=>2,:name=>"Org Two", :domain=>"taken")
60
+ o.should_not be_valid
61
+ o.errors.on(:domain).should include('Domain is already taken')
62
+
63
+ o = Organisation.new(:id=>2,:name=>"Org Two", :domain=>"not_taken")
64
+ o.should be_valid
65
+ end
66
+ end
67
+
68
+ it 'should validate the uniqueness of a value with scope' do
69
+ repository do
70
+ u = User.new(:id => 2, :organisation_id=>1, :user_name => 'guy')
71
+ u.should_not be_valid_for_testing_property
72
+ u.should_not be_valid_for_testing_association
73
+
74
+
75
+ u = User.new(:id => 2, :organisation_id => 2, :user_name => 'guy')
76
+ u.should be_valid_for_testing_property
77
+ u.should be_valid_for_testing_association
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,18 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe DataMapper::Validate::ValidationErrors do
5
+
6
+ before(:each) do
7
+ @errors = DataMapper::Validate::ValidationErrors.new
8
+ end
9
+
10
+ it "should report that it is empty on first creation" do
11
+ @errors.empty?.should == true
12
+ end
13
+
14
+ it "should continue to report that it is empty even after being checked" do
15
+ @errors.on(:foo)
16
+ @errors.empty?.should == true
17
+ end
18
+ end
@@ -0,0 +1,339 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe DataMapper::Validate do
5
+ before :all do
6
+ class Yacht
7
+ include DataMapper::Resource
8
+ property :id, Integer, :serial => true
9
+ property :name, String, :auto_validation => false
10
+
11
+ validates_present :name
12
+ end
13
+ end
14
+
15
+ it 'should respond to save' do
16
+ Yacht.new.should respond_to(:save)
17
+ end
18
+
19
+ describe '#save' do
20
+ before do
21
+ Yacht.auto_migrate!
22
+ @yacht = Yacht.new :name => 'The Gertrude'
23
+ end
24
+
25
+ describe 'without context specified' do
26
+ it 'should validate using the default context' do
27
+ @yacht.should_receive(:valid?).with(:default)
28
+ @yacht.save
29
+ end
30
+
31
+ it 'should save if the object is valid for the default context' do
32
+ @yacht.should be_valid
33
+ @yacht.save.should be_true
34
+ @yacht.should_not be_new_record
35
+ end
36
+
37
+ it 'should not save if the object is not valid for the default context' do
38
+ @yacht.name = 'a'
39
+ @yacht.should be_valid
40
+
41
+ @yacht.name = nil
42
+ @yacht.should_not be_valid
43
+ @yacht.save.should be_false
44
+ @yacht.should be_new_record
45
+ end
46
+ end
47
+
48
+ describe 'with context specified' do
49
+ before :all do
50
+ class Yacht
51
+ validates_length :name, :min => 2, :context => [ :strict_name ]
52
+ end
53
+ end
54
+
55
+ it 'should validate using the specified context' do
56
+ @yacht.should_receive(:valid?).with(:strict_name)
57
+ @yacht.save(:strict_name)
58
+ end
59
+
60
+ it 'should save if the object is valid for the specified context' do
61
+ @yacht.should be_valid(:strict_name)
62
+ @yacht.save(:strict_name).should be_true
63
+ @yacht.should_not be_new_record
64
+ end
65
+
66
+ it 'should not save if the object is not valid for the specified context' do
67
+ @yacht.name = 'aa'
68
+ @yacht.should be_valid(:strict_name)
69
+
70
+ @yacht.name = 'a'
71
+ @yacht.should_not be_valid(:strict_name)
72
+ @yacht.save(:strict_name).should be_false
73
+ @yacht.should be_new_record
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '#save!' do
79
+ before do
80
+ Yacht.auto_migrate!
81
+ @yacht = Yacht.new
82
+ end
83
+
84
+ it "should save object without running validations" do
85
+ @yacht.should_not_receive(:valid?)
86
+ @yacht.save!
87
+ @yacht.should_not be_new_record
88
+ end
89
+ end
90
+
91
+ it "should respond to validatable? (for recursing assocations)" do
92
+ Yacht.new.should be_validatable
93
+ Class.new.new.should_not be_validatable
94
+ end
95
+
96
+ it "should have a set of errors on the instance of the resource" do
97
+ shamrock = Yacht.new
98
+ shamrock.should respond_to(:errors)
99
+ end
100
+
101
+ it "should have a set of contextual validations on the class of the resource" do
102
+ Yacht.should respond_to(:validators)
103
+ end
104
+
105
+ it "should execute all validators for a given context against the resource" do
106
+ Yacht.validators.should respond_to(:execute)
107
+ end
108
+
109
+ it "should place a validator in the :default context if a named context is not provided" do
110
+ Yacht.validators.context(:default).length.should == 2
111
+ end
112
+
113
+ it "should allow multiple user defined contexts for a validator" do
114
+ class Yacht
115
+ property :port, String, :auto_validation => false
116
+ validates_present :port, :context => [:at_sea, :in_harbor]
117
+ end
118
+
119
+ Yacht.validators.context(:at_sea).length.should == 1
120
+ Yacht.validators.context(:in_harbor).length.should == 1
121
+ Yacht.validators.context(:no_such_context).length.should == 0
122
+ end
123
+
124
+ it "should alias :on and :when for :context" do
125
+ class Yacht
126
+ property :owner, String, :auto_validation => false
127
+ property :bosun, String, :auto_validation => false
128
+
129
+ validates_present :owner, :on => :owned_vessel
130
+ validates_present :bosun, :when => [:under_way]
131
+ end
132
+ Yacht.validators.context(:owned_vessel).length.should == 1
133
+ Yacht.validators.context(:under_way).length.should == 1
134
+ end
135
+
136
+ it "should alias :group for :context (backward compat with Validatable??)" do
137
+ class Yacht
138
+ property :captain, String, :auto_validation => false
139
+ validates_present :captain, :group => [:captained_vessel]
140
+ end
141
+ Yacht.validators.context(:captained_vessel).length.should == 1
142
+ end
143
+
144
+ it "should add a method valid_for_<context_name>? for each context" do
145
+ class Yacht
146
+ property :engine_size, String, :auto_validation => false
147
+ validates_present :engine_size, :when => :power_boat
148
+ end
149
+
150
+ cigaret = Yacht.new
151
+ cigaret.valid_for_default?.should_not == true
152
+ cigaret.should respond_to(:valid_for_power_boat?)
153
+ cigaret.valid_for_power_boat?.should_not == true
154
+
155
+ cigaret.engine_size = '4 liter V8'
156
+ cigaret.valid_for_power_boat?.should == true
157
+ end
158
+
159
+ it "should add a method all_valid_for_<context_name>? for each context" do
160
+ class Yacht
161
+ property :mast_height, String, :auto_validation => false
162
+ validates_present :mast_height, :when => :sailing_vessel
163
+ end
164
+ swift = Yacht.new
165
+ swift.should respond_to(:all_valid_for_sailing_vessel?)
166
+ end
167
+
168
+ it "should be able to translate the error message" # needs String::translations
169
+
170
+ it "should be able to get the error message for a given field" do
171
+ class Yacht
172
+ property :wood_type, String, :auto_validation => false
173
+ validates_present :wood_type, :on => :wooden_boats
174
+ end
175
+ fantasy = Yacht.new
176
+ fantasy.valid_for_wooden_boats?.should == false
177
+ fantasy.errors.on(:wood_type).first.should == 'Wood type must not be blank'
178
+ fantasy.wood_type = 'birch'
179
+ fantasy.valid_for_wooden_boats?.should == true
180
+ end
181
+
182
+ it "should be able to specify a custom error message" do
183
+ class Yacht
184
+ property :year_built, String, :auto_validation => false
185
+ validates_present :year_built, :when => :built, :message => 'Year built is a must enter field'
186
+ end
187
+
188
+ sea = Yacht.new
189
+ sea.valid_for_built?.should == false
190
+ sea.errors.full_messages.first.should == 'Year built is a must enter field'
191
+ end
192
+
193
+ it "should execute a Proc when provided in an :if clause and run validation if the Proc returns true" do
194
+ class Dingy
195
+ include DataMapper::Resource
196
+ property :id, Integer, :serial => true
197
+ property :owner, String, :auto_validation => false
198
+ validates_present :owner, :if => Proc.new{|resource| resource.owned?}
199
+
200
+ def owned?
201
+ false
202
+ end
203
+ end
204
+
205
+ Dingy.new.valid?.should == true
206
+
207
+ class Dingy
208
+ def owned?
209
+ true
210
+ end
211
+ end
212
+
213
+ Dingy.new.valid?.should_not == true
214
+ end
215
+
216
+ it "should execute a symbol or method name provided in an :if clause and run validation if the method returns true" do
217
+ class Dingy
218
+ validators.clear!
219
+ validates_present :owner, :if => :owned?
220
+
221
+ def owned?
222
+ false
223
+ end
224
+ end
225
+
226
+ Dingy.new.valid?.should == true
227
+
228
+ class Dingy
229
+ def owned?
230
+ true
231
+ end
232
+ end
233
+
234
+ Dingy.new.valid?.should_not == true
235
+ end
236
+
237
+ it "should execute a Proc when provided in an :unless clause and not run validation if the Proc returns true" do
238
+ class RowBoat
239
+ include DataMapper::Resource
240
+ property :id, Integer, :serial => true
241
+ validates_present :salesman, :unless => Proc.new{|resource| resource.sold?}
242
+
243
+ def sold?
244
+ false
245
+ end
246
+ end
247
+
248
+ RowBoat.new.valid?.should_not == true
249
+
250
+ class RowBoat
251
+ def sold?
252
+ true
253
+ end
254
+ end
255
+
256
+ RowBoat.new.valid?.should == true
257
+ end
258
+
259
+ it "should execute a symbol or method name provided in an :unless clause and not run validation if the method returns true" do
260
+ class Dingy
261
+ validators.clear!
262
+ validates_present :salesman, :unless => :sold?
263
+
264
+ def sold?
265
+ false
266
+ end
267
+ end
268
+
269
+ Dingy.new.valid?.should_not == true #not sold and no salesman
270
+
271
+ class Dingy
272
+ def sold?
273
+ true
274
+ end
275
+ end
276
+
277
+ Dingy.new.valid?.should == true # sold and no salesman
278
+ end
279
+
280
+ it "should perform automatic recursive validation #all_valid? checking all instance variables (and ivar.each items if valid)" do
281
+ class Invoice
282
+ include DataMapper::Resource
283
+ property :id, Integer, :serial => true
284
+ property :customer, String, :auto_validation => false
285
+ validates_present :customer
286
+
287
+ def line_items
288
+ @line_items || @line_items = []
289
+ end
290
+
291
+ def comment
292
+ @comment || nil
293
+ end
294
+
295
+ def comment=(value)
296
+ @comment = value
297
+ end
298
+ end
299
+
300
+ class LineItem
301
+ include DataMapper::Resource
302
+ property :id, Integer, :serial => true
303
+ property :price, String, :auto_validation => false
304
+ validates_is_number :price
305
+
306
+ def initialize(price)
307
+ @price = price
308
+ end
309
+ end
310
+
311
+ class Comment
312
+ include DataMapper::Resource
313
+ property :id, Integer, :serial => true
314
+ property :note, String, :auto_validation => false
315
+
316
+ validates_present :note
317
+ end
318
+
319
+ invoice = Invoice.new(:customer => 'Billy Bob')
320
+ invoice.valid?.should == true
321
+
322
+ for i in 1..6 do
323
+ invoice.line_items << LineItem.new(i.to_s)
324
+ end
325
+ invoice.line_items[1].price = 'BAD VALUE'
326
+ invoice.comment = Comment.new
327
+
328
+ invoice.comment.valid?.should == false
329
+ invoice.line_items[1].valid?.should == false
330
+
331
+ invoice.all_valid?.should == false
332
+ invoice.comment.note = 'This is a note'
333
+
334
+ invoice.all_valid?.should == false
335
+ invoice.line_items[1].price = '23.44'
336
+
337
+ invoice.all_valid?.should == true
338
+ end
339
+ end