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.
- 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
|