dm-validations 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README +72 -0
- data/Rakefile +74 -0
- data/TODO +16 -0
- data/lib/dm-validations.rb +208 -0
- data/lib/dm-validations/absent_field_validator.rb +60 -0
- data/lib/dm-validations/acceptance_validator.rb +76 -0
- data/lib/dm-validations/auto_validate.rb +98 -0
- data/lib/dm-validations/confirmation_validator.rb +76 -0
- data/lib/dm-validations/contextual_validators.rb +56 -0
- data/lib/dm-validations/custom_validator.rb +72 -0
- data/lib/dm-validations/format_validator.rb +94 -0
- data/lib/dm-validations/formats/email.rb +40 -0
- data/lib/dm-validations/generic_validator.rb +92 -0
- data/lib/dm-validations/length_validator.rb +113 -0
- data/lib/dm-validations/method_validator.rb +58 -0
- data/lib/dm-validations/numeric_validator.rb +66 -0
- data/lib/dm-validations/primitive_validator.rb +60 -0
- data/lib/dm-validations/required_field_validator.rb +88 -0
- data/lib/dm-validations/support/object.rb +5 -0
- data/lib/dm-validations/uniqueness_validator.rb +61 -0
- data/lib/dm-validations/validation_errors.rb +63 -0
- data/lib/dm-validations/within_validator.rb +41 -0
- data/spec/integration/absent_field_validator_spec.rb +34 -0
- data/spec/integration/acceptance_validator_spec.rb +87 -0
- data/spec/integration/auto_validate_spec.rb +262 -0
- data/spec/integration/confirmation_validator_spec.rb +66 -0
- data/spec/integration/contextual_validators_spec.rb +28 -0
- data/spec/integration/custom_validator_spec.rb +9 -0
- data/spec/integration/format_validator_spec.rb +118 -0
- data/spec/integration/generic_validator_spec.rb +9 -0
- data/spec/integration/length_validator_spec.rb +113 -0
- data/spec/integration/method_validator_spec.rb +31 -0
- data/spec/integration/numeric_validator_spec.rb +192 -0
- data/spec/integration/primitive_validator_spec.rb +25 -0
- data/spec/integration/required_field_validator_spec.rb +93 -0
- data/spec/integration/uniqueness_validator_spec.rb +81 -0
- data/spec/integration/validation_errors_spec.rb +18 -0
- data/spec/integration/validation_spec.rb +339 -0
- data/spec/integration/within_validator_spec.rb +35 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +26 -0
- 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
|