ramsingla-mongomapper 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +7 -0
  2. data/History +30 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/Rakefile +71 -0
  6. data/VERSION +1 -0
  7. data/lib/mongomapper.rb +60 -0
  8. data/lib/mongomapper/associations.rb +69 -0
  9. data/lib/mongomapper/associations/array_proxy.rb +6 -0
  10. data/lib/mongomapper/associations/base.rb +50 -0
  11. data/lib/mongomapper/associations/belongs_to_proxy.rb +26 -0
  12. data/lib/mongomapper/associations/has_many_embedded_proxy.rb +19 -0
  13. data/lib/mongomapper/associations/has_many_proxy.rb +28 -0
  14. data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +31 -0
  15. data/lib/mongomapper/associations/proxy.rb +60 -0
  16. data/lib/mongomapper/callbacks.rb +106 -0
  17. data/lib/mongomapper/document.rb +263 -0
  18. data/lib/mongomapper/embedded_document.rb +295 -0
  19. data/lib/mongomapper/finder_options.rb +81 -0
  20. data/lib/mongomapper/key.rb +82 -0
  21. data/lib/mongomapper/observing.rb +50 -0
  22. data/lib/mongomapper/pagination.rb +52 -0
  23. data/lib/mongomapper/rails_compatibility.rb +23 -0
  24. data/lib/mongomapper/save_with_validation.rb +19 -0
  25. data/lib/mongomapper/serialization.rb +55 -0
  26. data/lib/mongomapper/serializers/json_serializer.rb +77 -0
  27. data/lib/mongomapper/validations.rb +47 -0
  28. data/mongomapper.gemspec +105 -0
  29. data/test/serializers/test_json_serializer.rb +104 -0
  30. data/test/test_associations.rb +211 -0
  31. data/test/test_callbacks.rb +84 -0
  32. data/test/test_document.rb +995 -0
  33. data/test/test_embedded_document.rb +253 -0
  34. data/test/test_finder_options.rb +148 -0
  35. data/test/test_helper.rb +62 -0
  36. data/test/test_key.rb +200 -0
  37. data/test/test_mongomapper.rb +28 -0
  38. data/test/test_observing.rb +101 -0
  39. data/test/test_rails_compatibility.rb +29 -0
  40. data/test/test_serializations.rb +54 -0
  41. data/test/test_validations.rb +409 -0
  42. metadata +156 -0
@@ -0,0 +1,200 @@
1
+ require 'test_helper'
2
+
3
+ class Address
4
+ include MongoMapper::EmbeddedDocument
5
+
6
+ key :address, String
7
+ key :city, String
8
+ key :state, String
9
+ key :zip, Integer
10
+ end
11
+
12
+ class KeyTest < Test::Unit::TestCase
13
+ include MongoMapper
14
+
15
+ context "The Key Class" do
16
+ should "have the native types defined" do
17
+ Key::NativeTypes.should == [String, Float, Time, Integer, Boolean, Array, Hash, Ref]
18
+ end
19
+ end
20
+
21
+ context "Initializing a new key" do
22
+ should "allow setting the name" do
23
+ Key.new(:foo, String).name.should == 'foo'
24
+ end
25
+
26
+ should "allow setting the type" do
27
+ Key.new(:foo, Integer).type.should be(Integer)
28
+ end
29
+
30
+ should "allow setting options" do
31
+ Key.new(:foo, Integer, :required => true).options[:required].should be(true)
32
+ end
33
+
34
+ should "symbolize option keys" do
35
+ Key.new(:foo, Integer, 'required' => true).options[:required].should be(true)
36
+ end
37
+ end
38
+
39
+ context "A key" do
40
+ should "be equal to another key with same name and type" do
41
+ Key.new(:name, String).should == Key.new(:name, String)
42
+ end
43
+
44
+ should "not be equal to another key with different name" do
45
+ Key.new(:name, String).should_not == Key.new(:foo, String)
46
+ end
47
+
48
+ should "not be equal to another key with different type" do
49
+ Key.new(:name, String).should_not == Key.new(:name, Integer)
50
+ end
51
+
52
+ should "know if it is native" do
53
+ Key.new(:name, String).native?.should be_true
54
+ end
55
+
56
+ should "know if it is not native" do
57
+ klass = Class.new
58
+ Key.new(:name, klass).native?.should be_false
59
+ end
60
+
61
+ should "know if it is a embedded_document" do
62
+ klass = Class.new do
63
+ include MongoMapper::EmbeddedDocument
64
+ end
65
+ Key.new(:name, klass).embedded_document?.should be_true
66
+ end
67
+
68
+ should "know if it is not a embedded_document" do
69
+ Key.new(:name, String).embedded_document?.should be_false
70
+ end
71
+ end
72
+
73
+ context "setting a value" do
74
+ should "correctly typecast Strings" do
75
+ key = Key.new(:foo, String)
76
+ [21, '21'].each do |a|
77
+ key.set(a).should == '21'
78
+ end
79
+ end
80
+
81
+ should "correctly typecast Integers" do
82
+ key = Key.new(:foo, Integer)
83
+ [21, 21.0, '21'].each do |a|
84
+ key.set(a).should == 21
85
+ end
86
+ end
87
+
88
+ should "correctly typecast Floats" do
89
+ key = Key.new(:foo, Float)
90
+ [21, 21.0, '21'].each do |a|
91
+ key.set(a).should == 21.0
92
+ end
93
+ end
94
+
95
+ should "correctly typecast Times" do
96
+ key = Key.new(:foo, Time)
97
+ key.set('2000-01-01 01:01:01.123456').should == Time.local(2000, 1, 1, 1, 1, 1, 123456)
98
+ end
99
+
100
+ should_eventually "correctly typecast Dates" do
101
+ key = Key.new(:foo, Date)
102
+ key.set('2000-01-01').should == Date.new(2000, 1, 1)
103
+ end
104
+
105
+ should "correctly typecast Boolean" do
106
+ key = Key.new(:foo, Boolean)
107
+ ['false', false, 'f', '0', 0].each do |b|
108
+ key.set(b).should == false
109
+ end
110
+
111
+ ['true', true, 't', '1', 1].each do |b|
112
+ key.set(b).should == true
113
+ end
114
+ end
115
+
116
+ should "correctly typecast Array" do
117
+ key = Key.new(:foo, Array)
118
+ key.set([1,2,3,4]).should == [1,2,3,4]
119
+ key.set({'1' => '2', '3' => '4'}).should == [['1', '2'], ['3', '4']]
120
+ key.set('1').should == ['1']
121
+ end
122
+
123
+ should "correctly typecast Hash using indifferent access" do
124
+ key = Key.new(:foo, Hash)
125
+ key.set(:foo => 'bar')[:foo].should == 'bar'
126
+ key.set(:foo => 'bar')['foo'].should == 'bar'
127
+ key.set(:foo => {:bar => 'baz'})[:foo][:bar].should == 'baz'
128
+ key.set(:foo => {:bar => 'baz'})['foo']['bar'].should == 'baz'
129
+ end
130
+ end
131
+
132
+ context "getting a value" do
133
+ should "work" do
134
+ key = Key.new(:foo, String)
135
+ key.get('bar').should == 'bar'
136
+ end
137
+
138
+ context "for a key with a default value set" do
139
+ setup do
140
+ @key = Key.new(:foo, String, :default => 'baz')
141
+ end
142
+
143
+ should "return default value if value nil" do
144
+ @key.get(nil).should == 'baz'
145
+ end
146
+
147
+ should "return value if not blank" do
148
+ @key.get('foobar').should == 'foobar'
149
+ end
150
+ end
151
+
152
+ context "for a boolean key" do
153
+ should "allow setting default to false" do
154
+ Key.new(:active, Boolean, :default => false).get(nil).should be_false
155
+ end
156
+
157
+ should "allow setting default to true" do
158
+ Key.new(:active, Boolean, :default => true).get(nil).should be_true
159
+ end
160
+ end
161
+
162
+ context "for an array" do
163
+ should "return array" do
164
+ key = Key.new(:foo, Array)
165
+ key.get([1,2]).should == [1,2]
166
+ end
167
+
168
+ should "default to empty array" do
169
+ key = Key.new(:foo, Array)
170
+ key.get(nil).should == []
171
+ end
172
+ end
173
+
174
+ context "for a hash" do
175
+ should "default to empty hash" do
176
+ key = Key.new(:foo, Hash)
177
+ key.get(nil).should == {}
178
+ end
179
+
180
+ should "use hash with indifferent access" do
181
+ key = Key.new(:foo, Hash)
182
+ key.get({:foo => 'bar'})['foo'].should == 'bar'
183
+ key.get({:foo => 'bar'})[:foo].should == 'bar'
184
+ end
185
+ end
186
+
187
+ context "for a embedded_document" do
188
+ should "default to nil" do
189
+ key = Key.new(:foo, Address)
190
+ key.get(nil).should be_nil
191
+ end
192
+
193
+ should "return instance if instance" do
194
+ address = Address.new(:city => 'South Bend', :state => 'IN', :zip => 46544)
195
+ key = Key.new(:foo, Address)
196
+ key.get(address).should == address
197
+ end
198
+ end
199
+ end
200
+ end # KeyTest
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ class Address; end
4
+
5
+ class MongoMapperTest < Test::Unit::TestCase
6
+ should "be able to write and read connection" do
7
+ conn = XGen::Mongo::Driver::Mongo.new
8
+ MongoMapper.connection = conn
9
+ MongoMapper.connection.should == conn
10
+ end
11
+
12
+ should "default connection to new mongo ruby driver" do
13
+ MongoMapper.connection = nil
14
+ MongoMapper.connection.should be_instance_of(XGen::Mongo::Driver::Mongo)
15
+ end
16
+
17
+ should "be able to write and read default database" do
18
+ MongoMapper.database = DefaultDatabase
19
+ MongoMapper.database.should be_instance_of(XGen::Mongo::Driver::DB)
20
+ MongoMapper.database.name.should == DefaultDatabase
21
+ end
22
+
23
+ should "have document not found error" do
24
+ lambda {
25
+ MongoMapper::DocumentNotFound
26
+ }.should_not raise_error
27
+ end
28
+ end
@@ -0,0 +1,101 @@
1
+ require 'test_helper'
2
+
3
+ class Comment
4
+ include MongoMapper::Document
5
+
6
+ key :name, String
7
+ key :body, String
8
+
9
+ attr_accessor :callers
10
+ before_validation :record_callers
11
+
12
+ def after_validation
13
+ record_callers
14
+ end
15
+
16
+ def record_callers
17
+ callers << self.class if callers
18
+ end
19
+ end
20
+
21
+ class Article
22
+ include MongoMapper::Document
23
+
24
+ key :title, String
25
+ key :body, String
26
+ end
27
+
28
+ class CommentObserver < MongoMapper::Observer
29
+ attr_accessor :callers
30
+
31
+ def after_validation(model)
32
+ callers << self.class if callers
33
+ end
34
+ end
35
+
36
+ class AuditObserver < MongoMapper::Observer
37
+ observe Article, Comment
38
+ attr_reader :document
39
+
40
+ def after_validation(document)
41
+ @document = document
42
+ end
43
+ end
44
+
45
+ class GlobalObserver < MongoMapper::Observer
46
+ observe Article, Comment
47
+ attr_reader :document
48
+
49
+ def before_save(document)
50
+ @document = document
51
+ end
52
+ end
53
+
54
+ class NonAutomaticObserver < MongoMapper::Observer
55
+ observe Comment
56
+ attr_reader :comment
57
+
58
+ def after_validation(comment)
59
+ @comment = comment
60
+ end
61
+ end
62
+
63
+ class ObserverTest < Test::Unit::TestCase
64
+ should "fire model callbacks before observer" do
65
+ callers = []
66
+ comment = Comment.new
67
+ comment.callers = callers
68
+
69
+ CommentObserver.instance.callers = callers
70
+
71
+ comment.valid?
72
+ callers.should == [Comment, Comment, CommentObserver]
73
+ end
74
+
75
+ should "automatically observe model based on name when possible" do
76
+ CommentObserver.observed_class.should == Comment
77
+ end
78
+
79
+ should "be able to observe other models using observe" do
80
+ obs = NonAutomaticObserver.instance
81
+ comment = Comment.new(:name => 'John Nunemaker', :body => 'is awesome')
82
+ comment.valid?
83
+ obs.comment.name.should == 'John Nunemaker'
84
+ obs.comment.body.should == 'is awesome'
85
+ end
86
+
87
+ should "be able to observe multiple models" do
88
+ obs = AuditObserver.instance
89
+ comment = Comment.new(:name => 'Steve Smith', :body => 'is awesome')
90
+ comment.valid?
91
+
92
+ obs.document.name.should == 'Steve Smith'
93
+ obs.document.body.should == 'is awesome'
94
+
95
+ article = Article.new(:title => 'Ordered List Is Awesome', :body => 'Learn to accept it!')
96
+ article.valid?
97
+
98
+ obs.document.title.should == 'Ordered List Is Awesome'
99
+ obs.document.body.should == 'Learn to accept it!'
100
+ end
101
+ end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ class TestRailsCompatibility < Test::Unit::TestCase
4
+ def setup
5
+ @document = Class.new do
6
+ include MongoMapper::Document
7
+ end
8
+ end
9
+
10
+ should "have to_param that returns id" do
11
+ instance = @document.create('_id' => '1234')
12
+ instance.to_param.should == '1234'
13
+ end
14
+
15
+ should "alias new to new_record?" do
16
+ instance = @document.new
17
+ instance.new_record?.should == instance.new?
18
+ end
19
+
20
+ should "alias many to has_many" do
21
+ @document.should respond_to(:has_many)
22
+ @document.method(:has_many).should == @document.method(:many)
23
+ end
24
+
25
+ should "have column names" do
26
+ @document.key :fname, String
27
+ @document.column_names.sort.should == ['_id', 'created_at', 'fname', 'updated_at']
28
+ end
29
+ end
@@ -0,0 +1,54 @@
1
+ require 'test_helper'
2
+
3
+ class SerializationTest < Test::Unit::TestCase
4
+ def setup
5
+ @document = Class.new do
6
+ include MongoMapper::EmbeddedDocument
7
+ key :name, String
8
+ key :age, Integer
9
+ key :awesome, Boolean
10
+ key :preferences, Hash
11
+ key :created_at, Time
12
+ end
13
+
14
+ @instance = @document.new(
15
+ :name => 'John Doe',
16
+ :age => 25,
17
+ :awesome => true,
18
+ :preferences => {:language => 'Ruby'},
19
+ :created_at => Time.now.change(:usec => 0)
20
+ )
21
+ end
22
+
23
+ # [:xml, :json].each do |format|
24
+ [:json].each do |format|
25
+ context format do
26
+ should "be reversable" do
27
+ serialized = @instance.send("to_#{format}")
28
+ unserialized = @document.new.send("from_#{format}", serialized)
29
+
30
+ assert_equal @instance, unserialized
31
+ end
32
+
33
+ should "allow attribute only filtering" do
34
+ serialized = @instance.send("to_#{format}", :only => [ :age, :name ])
35
+ unserialized = @document.new.send("from_#{format}", serialized)
36
+
37
+ assert_equal @instance.name, unserialized.name
38
+ assert_equal @instance.age, unserialized.age
39
+ assert_nil unserialized.awesome
40
+ assert_nil unserialized.created_at
41
+ end
42
+
43
+ should "allow attribute except filtering" do
44
+ serialized = @instance.send("to_#{format}", :except => [ :age, :name ])
45
+ unserialized = @document.new.send("from_#{format}", serialized)
46
+
47
+ assert_nil unserialized.name
48
+ assert_nil unserialized.age
49
+ assert_equal @instance.awesome, unserialized.awesome
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,409 @@
1
+ require 'test_helper'
2
+
3
+ class ValidationsTest < Test::Unit::TestCase
4
+ context "Validations" do
5
+ setup do
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ end
9
+ end
10
+
11
+ context "Validating acceptance of" do
12
+ should "work with validates_acceptance_of macro" do
13
+ @document.key :terms, String
14
+ @document.validates_acceptance_of :terms
15
+ doc = @document.new(:terms => '')
16
+ doc.should have_error_on(:terms)
17
+ doc.terms = '1'
18
+ doc.should_not have_error_on(:terms)
19
+ end
20
+ end
21
+
22
+ context "validating confirmation of" do
23
+ should "work with validates_confirmation_of macro" do
24
+ @document.key :password, String
25
+ @document.validates_confirmation_of :password
26
+ doc = @document.new
27
+ doc.password = 'foobar'
28
+ doc.should have_error_on(:password)
29
+ doc.password_confirmation = 'foobar'
30
+ doc.should_not have_error_on(:password)
31
+ end
32
+ end
33
+
34
+ context "validating format of" do
35
+ should "work with validates_format_of macro" do
36
+ @document.key :name, String
37
+ @document.validates_format_of :name, :with => /.+/
38
+ doc = @document.new
39
+ doc.should have_error_on(:name)
40
+ doc.name = 'John'
41
+ doc.should_not have_error_on(:name)
42
+ end
43
+
44
+ should "work with :format shorcut key" do
45
+ @document.key :name, String, :format => /.+/
46
+ doc = @document.new
47
+ doc.should have_error_on(:name)
48
+ doc.name = 'John'
49
+ doc.should_not have_error_on(:name)
50
+ end
51
+ end
52
+
53
+ context "validating length of" do
54
+ should "work with validates_length_of macro" do
55
+ @document.key :name, String
56
+ @document.validates_length_of :name, :minimum => 5
57
+ doc = @document.new
58
+ doc.should have_error_on(:name)
59
+ end
60
+
61
+ context "with :length => integer shortcut" do
62
+ should "set maximum of integer provided" do
63
+ @document.key :name, String, :length => 5
64
+ doc = @document.new
65
+ doc.name = '123456'
66
+ doc.should have_error_on(:name)
67
+ doc.name = '12345'
68
+ doc.should_not have_error_on(:name)
69
+ end
70
+ end
71
+
72
+ context "with :length => range shortcut" do
73
+ setup do
74
+ @document.key :name, String, :length => 5..7
75
+ end
76
+
77
+ should "set minimum of range min" do
78
+ doc = @document.new
79
+ doc.should have_error_on(:name)
80
+ doc.name = '123456'
81
+ doc.should_not have_error_on(:name)
82
+ end
83
+
84
+ should "set maximum of range max" do
85
+ doc = @document.new
86
+ doc.should have_error_on(:name)
87
+ doc.name = '12345678'
88
+ doc.should have_error_on(:name)
89
+ doc.name = '123456'
90
+ doc.should_not have_error_on(:name)
91
+ end
92
+ end
93
+
94
+ context "with :length => hash shortcut" do
95
+ should "pass options through" do
96
+ @document.key :name, String, :length => {:minimum => 2}
97
+ doc = @document.new
98
+ doc.should have_error_on(:name)
99
+ doc.name = '12'
100
+ doc.should_not have_error_on(:name)
101
+ end
102
+ end
103
+ end # validates_length_of
104
+
105
+ context "Validating numericality of" do
106
+ should "work with validates_numericality_of macro" do
107
+ @document.key :age, Integer
108
+ @document.validates_numericality_of :age
109
+ doc = @document.new
110
+ doc.age = 'String'
111
+ doc.should have_error_on(:age)
112
+ doc.age = 23
113
+ doc.should_not have_error_on(:age)
114
+ end
115
+
116
+ context "with :numeric shortcut" do
117
+ should "work with integer or float" do
118
+ @document.key :weight, Float, :numeric => true
119
+ doc = @document.new
120
+ doc.weight = 'String'
121
+ doc.should have_error_on(:weight)
122
+ doc.weight = 23.0
123
+ doc.should_not have_error_on(:weight)
124
+ doc.weight = 23
125
+ doc.should_not have_error_on(:weight)
126
+ end
127
+ end
128
+
129
+ context "with :numeric shortcut on Integer key" do
130
+ should "only work with integers" do
131
+ @document.key :age, Integer, :numeric => true
132
+ doc = @document.new
133
+ doc.age = 'String'
134
+ doc.should have_error_on(:age)
135
+ doc.age = 23.1
136
+ doc.should have_error_on(:age)
137
+ doc.age = 23
138
+ doc.should_not have_error_on(:age)
139
+ end
140
+ end
141
+ end # numericality of
142
+
143
+ context "validating presence of" do
144
+ should "work with validates_presence_of macro" do
145
+ @document.key :name, String
146
+ @document.validates_presence_of :name
147
+ doc = @document.new
148
+ doc.should have_error_on(:name)
149
+ end
150
+
151
+ should "work with :required shortcut on key definition" do
152
+ @document.key :name, String, :required => true
153
+ doc = @document.new
154
+ doc.should have_error_on(:name)
155
+ end
156
+ end
157
+
158
+ context "validating uniqueness of" do
159
+ setup do
160
+ @document.key :name, String
161
+ @document.validates_uniqueness_of :name
162
+ end
163
+
164
+ should "not fail if object is new" do
165
+ doc = @document.new
166
+ doc.should_not have_error_on(:name)
167
+ end
168
+
169
+ should "allow to update an object" do
170
+ doc = @document.new("name" => "joe")
171
+ doc.save
172
+
173
+ @document \
174
+ .stubs(:find) \
175
+ .with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
176
+ .returns(doc)
177
+
178
+ doc.name = "joe"
179
+ doc.valid?.should be_true
180
+ doc.should_not have_error_on(:name)
181
+ end
182
+
183
+ should "fail if object name is not unique" do
184
+ doc = @document.new("name" => "joe")
185
+ doc.save.should be_true
186
+
187
+ @document \
188
+ .stubs(:find) \
189
+ .with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
190
+ .returns(doc)
191
+
192
+ doc2 = @document.new("name" => "joe")
193
+ doc2.should have_error_on(:name)
194
+ end
195
+ end
196
+
197
+ context "validates uniqueness of with :unique shortcut" do
198
+ should "work" do
199
+ @document.key :name, String, :unique => true
200
+
201
+ doc = @document.create(:name => 'John')
202
+ doc.should_not have_error_on(:name)
203
+
204
+ @document \
205
+ .stubs(:find) \
206
+ .with(:first, :conditions => {:name => 'John'}, :limit => 1) \
207
+ .returns(doc)
208
+
209
+ second_john = @document.create(:name => 'John')
210
+ second_john.should have_error_on(:name, 'has already been taken')
211
+ end
212
+ end
213
+
214
+ context "validating exclusion of" do
215
+ should "throw error if enumerator not provided" do
216
+ @document.key :action, String
217
+ lambda {
218
+ @document.validates_exclusion_of :action
219
+ }.should raise_error(ArgumentError)
220
+ end
221
+
222
+ should "work with validates_exclusion_of macro" do
223
+ @document.key :action, String
224
+ @document.validates_exclusion_of :action, :within => %w(kick run)
225
+
226
+ doc = @document.new
227
+ doc.should_not have_error_on(:action)
228
+
229
+ doc.action = 'fart'
230
+ doc.should_not have_error_on(:action)
231
+
232
+ doc.action = 'kick'
233
+ doc.should have_error_on(:action, 'is reserved')
234
+ end
235
+
236
+ should "not have error if allow nil is true and value is nil" do
237
+ @document.key :action, String
238
+ @document.validates_exclusion_of :action, :within => %w(kick run), :allow_nil => true
239
+
240
+ doc = @document.new
241
+ doc.should_not have_error_on(:action)
242
+ end
243
+
244
+ should "not have error if allow blank is true and value is blank" do
245
+ @document.key :action, String
246
+ @document.validates_exclusion_of :action, :within => %w(kick run), :allow_nil => true
247
+
248
+ doc = @document.new(:action => '')
249
+ doc.should_not have_error_on(:action)
250
+ end
251
+ end
252
+
253
+ context "validating inclusion of" do
254
+ should "throw error if enumerator not provided" do
255
+ @document.key :action, String
256
+ lambda {
257
+ @document.validates_inclusion_of :action
258
+ }.should raise_error(ArgumentError)
259
+ end
260
+
261
+ should "work with validates_inclusion_of macro" do
262
+ @document.key :action, String
263
+ @document.validates_inclusion_of :action, :within => %w(kick run)
264
+
265
+ doc = @document.new
266
+ doc.should have_error_on(:action, 'is not in the list')
267
+
268
+ doc.action = 'fart'
269
+ doc.should have_error_on(:action, 'is not in the list')
270
+
271
+ doc.action = 'kick'
272
+ doc.should_not have_error_on(:action)
273
+ end
274
+
275
+ should "not have error if allow nil is true and value is nil" do
276
+ @document.key :action, String
277
+ @document.validates_inclusion_of :action, :within => %w(kick run), :allow_nil => true
278
+
279
+ doc = @document.new
280
+ doc.should_not have_error_on(:action)
281
+ end
282
+
283
+ should "not have error if allow blank is true and value is blank" do
284
+ @document.key :action, String
285
+ @document.validates_inclusion_of :action, :within => %w(kick run), :allow_blank => true
286
+
287
+ doc = @document.new(:action => '')
288
+ doc.should_not have_error_on(:action)
289
+ end
290
+ end
291
+ end # Validations
292
+
293
+ context "Saving a new document that is invalid" do
294
+ setup do
295
+ @document = Class.new do
296
+ include MongoMapper::Document
297
+ key :name, String, :required => true
298
+ end
299
+
300
+ @document.collection.clear
301
+ end
302
+
303
+ should "not insert document" do
304
+ doc = @document.new
305
+ doc.save
306
+ @document.count.should == 0
307
+ end
308
+
309
+ should "populate document's errors" do
310
+ doc = @document.new
311
+ doc.errors.size.should == 0
312
+ doc.save
313
+ doc.errors.full_messages.should == ["Name can't be empty"]
314
+ end
315
+ end
316
+
317
+ context "Saving a document that is invalid (destructive)" do
318
+ setup do
319
+ @document = Class.new do
320
+ include MongoMapper::Document
321
+ key :name, String, :required => true
322
+ end
323
+
324
+ @document.collection.clear
325
+ end
326
+
327
+ should "raise error" do
328
+ doc = @document.new
329
+ lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
330
+ end
331
+ end
332
+
333
+ context "Saving an existing document that is invalid" do
334
+ setup do
335
+ @document = Class.new do
336
+ include MongoMapper::Document
337
+ key :name, String, :required => true
338
+ end
339
+
340
+ @document.collection.clear
341
+ @doc = @document.create(:name => 'John Nunemaker')
342
+ end
343
+
344
+ should "not update document" do
345
+ @doc.name = nil
346
+ @doc.save
347
+ @document.find(@doc.id).name.should == 'John Nunemaker'
348
+ end
349
+
350
+ should "populate document's errors" do
351
+ @doc.name = nil
352
+ @doc.save
353
+ @doc.errors.full_messages.should == ["Name can't be empty"]
354
+ end
355
+ end
356
+
357
+ context "Adding validation errors" do
358
+ setup do
359
+ @document = Class.new do
360
+ include MongoMapper::Document
361
+ key :action, String
362
+ def action_present
363
+ errors.add(:action, 'is invalid') if action.blank?
364
+ end
365
+ end
366
+ end
367
+
368
+ should "work with validate callback" do
369
+ @document.validate :action_present
370
+
371
+ doc = @document.new
372
+ doc.action = nil
373
+ doc.should have_error_on(:action)
374
+
375
+ doc.action = 'kick'
376
+ doc.should_not have_error_on(:action)
377
+ end
378
+
379
+ should "work with validate_on_create callback" do
380
+ @document.validate_on_create :action_present
381
+
382
+ doc = @document.new
383
+ doc.action = nil
384
+ doc.should have_error_on(:action)
385
+
386
+ doc.action = 'kick'
387
+ doc.should_not have_error_on(:action)
388
+ doc.save
389
+
390
+ doc.action = nil
391
+ doc.should_not have_error_on(:action)
392
+ end
393
+
394
+ should "work with validate_on_update callback" do
395
+ @document.validate_on_update :action_present
396
+
397
+ doc = @document.new
398
+ doc.action = nil
399
+ doc.should_not have_error_on(:action)
400
+ doc.save
401
+
402
+ doc.action = nil
403
+ doc.should have_error_on(:action)
404
+
405
+ doc.action = 'kick'
406
+ doc.should_not have_error_on(:action)
407
+ end
408
+ end
409
+ end