dm-validations 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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