public
Rubygem
Description: Extras for DataMapper, including bridges to DataObjects::Migrations and Merb::DataMapper
Homepage: http://datamapper.org
Clone URL: git://github.com/sam/dm-more.git
Added automatic validations on save

* An optional context argument can be passed into Resource#save that
  will determine which context the object is validated with prior to
  saving.  The context defaults to :default.
Dan Kubb (author)
Sat May 24 22:29:10 -0700 2008
commit  69b9df09621058677f80095d365d4c16f9c247bd
tree    f6c9fe8f58fdfe12916409a6285adb39a59ea511
parent  378929eef072c085e3bb81a64d33ade6dff8d530
...
28
29
30
 
 
 
 
 
 
 
31
32
33
...
178
179
180
181
 
 
 
 
 
 
 
 
182
183
184
...
28
29
30
31
32
33
34
35
36
37
38
39
40
...
185
186
187
 
188
189
190
191
192
193
194
195
196
197
198
0
@@ -28,6 +28,13 @@ require dir / 'support' / 'object'
0
 module DataMapper
0
   module Validate
0
 
0
+    # Validate the resource before saving
0
+    #
0
+    def save(context = :default)
0
+      return false unless valid?(context)
0
+      super()
0
+    end
0
+
0
     # Return the ValidationErrors
0
     #
0
     def errors
0
@@ -178,7 +185,14 @@ module DataMapper
0
   end # module Validate
0
 
0
   module Resource
0
-    include Validate
0
+    class << self
0
+      included = instance_method(:included)
0
+
0
+      define_method(:included) do |model|
0
+        included.bind(self).call(model)
0
+        model.send(:include, Validate)
0
+      end
0
+    end
0
 
0
     module ClassMethods
0
       include Validate::ClassMethods
...
2
3
4
5
 
6
7
8
...
12
13
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
16
17
...
30
31
32
33
34
 
35
36
37
38
39
40
41
42
43
 
44
45
46
...
115
116
117
118
119
 
120
121
122
123
124
125
 
 
 
 
126
127
128
129
130
131
 
 
 
132
133
134
135
136
137
138
 
139
140
141
142
143
 
 
 
144
145
146
147
148
149
 
 
 
150
151
152
153
154
155
156
 
157
158
159
160
161
162
 
 
 
163
164
165
166
167
168
 
 
 
169
170
171
172
173
174
175
 
176
177
178
179
180
 
 
 
181
182
183
184
185
186
 
 
 
187
188
189
190
191
192
193
194
 
195
196
197
...
249
250
251
252
253
254
...
2
3
4
 
5
6
7
8
...
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
...
93
94
95
 
 
96
97
98
99
 
100
101
102
103
104
105
106
107
108
...
177
178
179
 
 
180
181
182
183
184
185
 
186
187
188
189
190
191
192
193
194
 
195
196
197
198
199
200
201
202
 
 
203
204
205
206
207
 
208
209
210
211
212
213
214
215
 
216
217
218
219
220
221
222
223
 
 
224
225
226
227
228
229
 
230
231
232
233
234
235
236
237
 
238
239
240
241
242
243
244
245
 
 
246
247
248
249
250
 
251
252
253
254
255
256
257
258
 
259
260
261
262
263
264
265
266
 
 
 
267
268
269
270
...
322
323
324
 
325
326
0
@@ -2,7 +2,7 @@ require 'pathname'
0
 require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
0
 
0
 describe DataMapper::Validate do
0
-  before(:all) do
0
+  before :all do
0
     class Yacht
0
       include DataMapper::Resource
0
       property :id, Integer, :serial => true
0
@@ -12,6 +12,69 @@ describe DataMapper::Validate do
0
     end
0
   end
0
 
0
+  it 'should respond to save' do
0
+    Yacht.new.should respond_to(:save)
0
+  end
0
+
0
+  describe '#save' do
0
+    before do
0
+      Yacht.auto_migrate!
0
+      @yacht = Yacht.new :name => 'The Gertrude'
0
+    end
0
+
0
+    describe 'without context specified' do
0
+      it 'should validate using the default context' do
0
+        @yacht.should_receive(:valid?).with(:default)
0
+        @yacht.save
0
+      end
0
+
0
+      it 'should save if the object is valid for the default context' do
0
+        @yacht.should be_valid
0
+        @yacht.save.should be_true
0
+        @yacht.should_not be_new_record
0
+      end
0
+
0
+      it 'should not save if the object is not valid for the default context' do
0
+        @yacht.name = 'a'
0
+        @yacht.should be_valid
0
+
0
+        @yacht.name = nil
0
+        @yacht.should_not be_valid
0
+        @yacht.save.should be_false
0
+        @yacht.should be_new_record
0
+      end
0
+    end
0
+
0
+    describe 'with context specified' do
0
+      before :all do
0
+        class Yacht
0
+          validates_length :name, :min => 2, :context => [ :strict_name ]
0
+        end
0
+      end
0
+
0
+      it 'should validate using the specified context' do
0
+        @yacht.should_receive(:valid?).with(:strict_name)
0
+        @yacht.save(:strict_name)
0
+      end
0
+
0
+      it 'should save if the object is valid for the specified context' do
0
+        @yacht.should be_valid(:strict_name)
0
+        @yacht.save(:strict_name).should be_true
0
+        @yacht.should_not be_new_record
0
+      end
0
+
0
+      it 'should not save if the object is not valid for the specified context' do
0
+        @yacht.name = 'aa'
0
+        @yacht.should be_valid(:strict_name)
0
+
0
+        @yacht.name = 'a'
0
+        @yacht.should_not be_valid(:strict_name)
0
+        @yacht.save(:strict_name).should be_false
0
+        @yacht.should be_new_record
0
+      end
0
+    end
0
+  end
0
+
0
   it "should respond to validatable? (for recursing assocations)" do
0
     Yacht.new.should be_validatable
0
     Class.new.new.should_not be_validatable
0
@@ -30,17 +93,16 @@ describe DataMapper::Validate do
0
     Yacht.validators.should respond_to(:execute)
0
   end
0
 
0
-  it "should place a validator in the :default context if a named context is
0
-      not provided" do
0
+  it "should place a validator in the :default context if a named context is not provided" do
0
     Yacht.validators.context(:default).length.should == 2
0
   end
0
 
0
-
0
   it "should allow multiple user defined contexts for a validator" do
0
     class Yacht
0
       property :port, String, :auto_validation => false
0
       validates_present :port, :context => [:at_sea, :in_harbor]
0
     end
0
+
0
     Yacht.validators.context(:at_sea).length.should == 1
0
     Yacht.validators.context(:in_harbor).length.should == 1
0
     Yacht.validators.context(:no_such_context).length.should == 0
0
@@ -115,83 +177,94 @@ describe DataMapper::Validate do
0
     sea.errors.full_messages.first.should == 'Year built is a must enter field'
0
   end
0
 
0
-  it "should execute a Proc when provided in an :if clause and run validation
0
-      if the Proc returns true" do
0
+  it "should execute a Proc when provided in an :if clause and run validation if the Proc returns true" do
0
     class Dingy
0
       include DataMapper::Resource
0
       property :id, Integer, :serial => true
0
       property :owner, String, :auto_validation => false
0
       validates_present :owner, :if => Proc.new{|resource| resource.owned?}
0
-      def owned?; false; end
0
+
0
+      def owned?
0
+        false
0
+      end
0
     end
0
 
0
     Dingy.new.valid?.should == true
0
 
0
     class Dingy
0
-      def owned?; true; end
0
+      def owned?
0
+        true
0
+      end
0
     end
0
 
0
     Dingy.new.valid?.should_not == true
0
   end
0
 
0
-  it "should execute a symbol or method name provided in an :if clause and run
0
-      validation if the method returns true" do
0
+  it "should execute a symbol or method name provided in an :if clause and run validation if the method returns true" do
0
     class Dingy
0
       validators.clear!
0
       validates_present :owner, :if => :owned?
0
 
0
-      def owned?; false; end
0
+      def owned?
0
+        false
0
+      end
0
     end
0
 
0
     Dingy.new.valid?.should == true
0
 
0
     class Dingy
0
-      def owned?; true; end
0
+      def owned?
0
+        true
0
+      end
0
     end
0
 
0
     Dingy.new.valid?.should_not == true
0
   end
0
 
0
-  it "should execute a Proc when provided in an :unless clause and not run
0
-      validation if the Proc returns true" do
0
+  it "should execute a Proc when provided in an :unless clause and not run validation if the Proc returns true" do
0
     class RowBoat
0
       include DataMapper::Resource
0
       property :id, Integer, :serial => true
0
       validates_present :salesman, :unless => Proc.new{|resource| resource.sold?}
0
 
0
-      def sold?; false; end
0
+      def sold?
0
+        false
0
+      end
0
     end
0
 
0
     RowBoat.new.valid?.should_not == true
0
 
0
     class RowBoat
0
-      def sold?; true; end
0
+      def sold?
0
+        true
0
+      end
0
     end
0
 
0
     RowBoat.new.valid?.should == true
0
   end
0
 
0
-  it "should execute a symbol or method name provided in an :unless clause and
0
-      not run validation if the method returns true" do
0
+  it "should execute a symbol or method name provided in an :unless clause and not run validation if the method returns true" do
0
     class Dingy
0
       validators.clear!
0
       validates_present :salesman, :unless => :sold?
0
 
0
-      def sold?; false; end
0
+      def sold?
0
+        false
0
+      end
0
     end
0
 
0
     Dingy.new.valid?.should_not == true  #not sold and no salesman
0
 
0
     class Dingy
0
-      def sold?; true; end
0
+      def sold?
0
+        true
0
+      end
0
     end
0
 
0
     Dingy.new.valid?.should == true    # sold and no salesman
0
   end
0
 
0
-
0
-  it "should perform automatic recursive validation #all_valid? checking all
0
-      instance variables (and ivar.each items if valid)" do
0
+  it "should perform automatic recursive validation #all_valid? checking all instance variables (and ivar.each items if valid)" do
0
     class Invoice
0
       include DataMapper::Resource
0
       property :id, Integer, :serial => true
0
@@ -249,6 +322,5 @@ describe DataMapper::Validate do
0
     invoice.line_items[1].price = '23.44'
0
 
0
     invoice.all_valid?.should == true
0
-
0
   end
0
 end

Comments