tpitale-mongo_mapper 0.6.9
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/.gitignore +10 -0
- data/LICENSE +20 -0
- data/README.rdoc +53 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/bin/mmconsole +60 -0
- data/lib/mongo_mapper/associations/base.rb +110 -0
- data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +26 -0
- data/lib/mongo_mapper/associations/belongs_to_proxy.rb +21 -0
- data/lib/mongo_mapper/associations/collection.rb +19 -0
- data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +26 -0
- data/lib/mongo_mapper/associations/many_documents_proxy.rb +115 -0
- data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +31 -0
- data/lib/mongo_mapper/associations/many_embedded_proxy.rb +54 -0
- data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
- data/lib/mongo_mapper/associations/proxy.rb +113 -0
- data/lib/mongo_mapper/associations.rb +70 -0
- data/lib/mongo_mapper/callbacks.rb +109 -0
- data/lib/mongo_mapper/dirty.rb +136 -0
- data/lib/mongo_mapper/document.rb +472 -0
- data/lib/mongo_mapper/dynamic_finder.rb +74 -0
- data/lib/mongo_mapper/embedded_document.rb +384 -0
- data/lib/mongo_mapper/finder_options.rb +133 -0
- data/lib/mongo_mapper/key.rb +36 -0
- data/lib/mongo_mapper/observing.rb +50 -0
- data/lib/mongo_mapper/pagination.rb +55 -0
- data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
- data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
- data/lib/mongo_mapper/serialization.rb +54 -0
- data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
- data/lib/mongo_mapper/support.rb +206 -0
- data/lib/mongo_mapper/validations.rb +41 -0
- data/lib/mongo_mapper.rb +120 -0
- data/mongo_mapper.gemspec +173 -0
- data/specs.watchr +32 -0
- data/test/NOTE_ON_TESTING +1 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +48 -0
- data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
- data/test/functional/associations/test_many_documents_proxy.rb +387 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +192 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
- data/test/functional/test_associations.rb +44 -0
- data/test/functional/test_binary.rb +18 -0
- data/test/functional/test_callbacks.rb +85 -0
- data/test/functional/test_dirty.rb +159 -0
- data/test/functional/test_document.rb +1235 -0
- data/test/functional/test_embedded_document.rb +135 -0
- data/test/functional/test_logger.rb +20 -0
- data/test/functional/test_pagination.rb +95 -0
- data/test/functional/test_rails_compatibility.rb +25 -0
- data/test/functional/test_string_id_compatibility.rb +72 -0
- data/test/functional/test_validations.rb +378 -0
- data/test/models.rb +271 -0
- data/test/support/custom_matchers.rb +55 -0
- data/test/support/timing.rb +16 -0
- data/test/test_helper.rb +27 -0
- data/test/unit/associations/test_base.rb +166 -0
- data/test/unit/associations/test_proxy.rb +91 -0
- data/test/unit/serializers/test_json_serializer.rb +189 -0
- data/test/unit/test_document.rb +204 -0
- data/test/unit/test_dynamic_finder.rb +125 -0
- data/test/unit/test_embedded_document.rb +718 -0
- data/test/unit/test_finder_options.rb +296 -0
- data/test/unit/test_key.rb +172 -0
- data/test/unit/test_mongo_mapper.rb +65 -0
- data/test/unit/test_observing.rb +101 -0
- data/test/unit/test_pagination.rb +113 -0
- data/test/unit/test_rails_compatibility.rb +49 -0
- data/test/unit/test_serializations.rb +52 -0
- data/test/unit/test_support.rb +342 -0
- data/test/unit/test_time_zones.rb +40 -0
- data/test/unit/test_validations.rb +503 -0
- metadata +235 -0
@@ -0,0 +1,378 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ValidationsTest < Test::Unit::TestCase
|
4
|
+
context "Saving a new document that is invalid" do
|
5
|
+
setup do
|
6
|
+
@document = Class.new do
|
7
|
+
include MongoMapper::Document
|
8
|
+
set_collection_name 'test'
|
9
|
+
key :name, String, :required => true
|
10
|
+
end
|
11
|
+
@document.collection.remove
|
12
|
+
end
|
13
|
+
|
14
|
+
should "not insert document" do
|
15
|
+
doc = @document.new
|
16
|
+
doc.save
|
17
|
+
@document.count.should == 0
|
18
|
+
end
|
19
|
+
|
20
|
+
should "populate document's errors" do
|
21
|
+
doc = @document.new
|
22
|
+
doc.errors.size.should == 0
|
23
|
+
doc.save
|
24
|
+
doc.errors.full_messages.should == ["Name can't be empty"]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "Skipping validations when saving" do
|
29
|
+
setup do
|
30
|
+
@document = Class.new do
|
31
|
+
include MongoMapper::Document
|
32
|
+
set_collection_name 'test'
|
33
|
+
key :name, String, :required => true
|
34
|
+
end
|
35
|
+
@document.collection.remove
|
36
|
+
end
|
37
|
+
|
38
|
+
should "insert document" do
|
39
|
+
doc = @document.new
|
40
|
+
doc.save(false)
|
41
|
+
@document.count.should == 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "Saving a document that is invalid (destructive)" do
|
46
|
+
setup do
|
47
|
+
@document = Class.new do
|
48
|
+
include MongoMapper::Document
|
49
|
+
set_collection_name 'test'
|
50
|
+
key :name, String, :required => true
|
51
|
+
end
|
52
|
+
@document.collection.remove
|
53
|
+
end
|
54
|
+
|
55
|
+
should "raise error" do
|
56
|
+
doc = @document.new
|
57
|
+
lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "Creating a document that is invalid (destructive)" do
|
62
|
+
setup do
|
63
|
+
@document = Class.new do
|
64
|
+
include MongoMapper::Document
|
65
|
+
set_collection_name 'test'
|
66
|
+
key :name, String, :required => true
|
67
|
+
end
|
68
|
+
@document.collection.remove
|
69
|
+
end
|
70
|
+
|
71
|
+
should "raise error" do
|
72
|
+
lambda { @document.create! }.should raise_error(MongoMapper::DocumentNotValid)
|
73
|
+
end
|
74
|
+
|
75
|
+
should "create a new document" do
|
76
|
+
instance = @document.create!(:name => "James")
|
77
|
+
instance.new_record?.should be_false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "Saving an existing document that is invalid" do
|
82
|
+
setup do
|
83
|
+
@document = Class.new do
|
84
|
+
include MongoMapper::Document
|
85
|
+
set_collection_name 'test'
|
86
|
+
key :name, String, :required => true
|
87
|
+
end
|
88
|
+
@document.collection.remove
|
89
|
+
|
90
|
+
@doc = @document.create(:name => 'John Nunemaker')
|
91
|
+
end
|
92
|
+
|
93
|
+
should "not update document" do
|
94
|
+
@doc.name = nil
|
95
|
+
@doc.save
|
96
|
+
@doc.reload.name.should == 'John Nunemaker'
|
97
|
+
end
|
98
|
+
|
99
|
+
should "populate document's errors" do
|
100
|
+
@doc.name = nil
|
101
|
+
@doc.save
|
102
|
+
@doc.errors.full_messages.should == ["Name can't be empty"]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "Adding validation errors" do
|
107
|
+
setup do
|
108
|
+
@document = Class.new do
|
109
|
+
include MongoMapper::Document
|
110
|
+
set_collection_name 'test'
|
111
|
+
|
112
|
+
key :action, String
|
113
|
+
def action_present
|
114
|
+
errors.add(:action, 'is invalid') if action.blank?
|
115
|
+
end
|
116
|
+
end
|
117
|
+
@document.collection.remove
|
118
|
+
end
|
119
|
+
|
120
|
+
should "work with validate_on_create callback" do
|
121
|
+
@document.validate_on_create :action_present
|
122
|
+
|
123
|
+
doc = @document.new
|
124
|
+
doc.action = nil
|
125
|
+
doc.should have_error_on(:action)
|
126
|
+
|
127
|
+
doc.action = 'kick'
|
128
|
+
doc.should_not have_error_on(:action)
|
129
|
+
doc.save
|
130
|
+
|
131
|
+
doc.action = nil
|
132
|
+
doc.should_not have_error_on(:action)
|
133
|
+
end
|
134
|
+
|
135
|
+
should "work with validate_on_update callback" do
|
136
|
+
@document.validate_on_update :action_present
|
137
|
+
|
138
|
+
doc = @document.new
|
139
|
+
doc.action = nil
|
140
|
+
doc.should_not have_error_on(:action)
|
141
|
+
doc.save
|
142
|
+
|
143
|
+
doc.action = nil
|
144
|
+
doc.should have_error_on(:action)
|
145
|
+
|
146
|
+
doc.action = 'kick'
|
147
|
+
doc.should_not have_error_on(:action)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "validating uniqueness of" do
|
152
|
+
setup do
|
153
|
+
@document = Class.new do
|
154
|
+
include MongoMapper::Document
|
155
|
+
set_collection_name 'test'
|
156
|
+
|
157
|
+
key :name, String
|
158
|
+
validates_uniqueness_of :name
|
159
|
+
end
|
160
|
+
@document.collection.remove
|
161
|
+
end
|
162
|
+
|
163
|
+
should "not fail if object is new" do
|
164
|
+
doc = @document.new
|
165
|
+
doc.should_not have_error_on(:name)
|
166
|
+
end
|
167
|
+
|
168
|
+
should "not fail when new object is out of scope" do
|
169
|
+
document = Class.new do
|
170
|
+
include MongoMapper::Document
|
171
|
+
set_collection_name 'test'
|
172
|
+
|
173
|
+
key :name
|
174
|
+
key :adult
|
175
|
+
validates_uniqueness_of :name, :scope => :adult
|
176
|
+
end
|
177
|
+
doc = document.new("name" => "joe", :adult => true)
|
178
|
+
doc.save.should be_true
|
179
|
+
|
180
|
+
doc2 = document.new("name" => "joe", :adult => false)
|
181
|
+
doc2.should be_valid
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
should "allow to update an object" do
|
186
|
+
doc = @document.new("name" => "joe")
|
187
|
+
doc.save.should be_true
|
188
|
+
|
189
|
+
@document \
|
190
|
+
.stubs(:first) \
|
191
|
+
.with(:name => 'joe') \
|
192
|
+
.returns(doc)
|
193
|
+
|
194
|
+
doc.name = "joe"
|
195
|
+
doc.valid?.should be_true
|
196
|
+
doc.should_not have_error_on(:name)
|
197
|
+
end
|
198
|
+
|
199
|
+
should "fail if object name is not unique" do
|
200
|
+
doc = @document.new("name" => "joe")
|
201
|
+
doc.save.should be_true
|
202
|
+
|
203
|
+
@document \
|
204
|
+
.stubs(:first) \
|
205
|
+
.with(:name => 'joe') \
|
206
|
+
.returns(doc)
|
207
|
+
|
208
|
+
doc2 = @document.new("name" => "joe")
|
209
|
+
doc2.should have_error_on(:name)
|
210
|
+
end
|
211
|
+
|
212
|
+
should "allow multiple blank entries if :allow_blank => true" do
|
213
|
+
document = Class.new do
|
214
|
+
include MongoMapper::Document
|
215
|
+
set_collection_name 'test'
|
216
|
+
|
217
|
+
key :name
|
218
|
+
validates_uniqueness_of :name, :allow_blank => :true
|
219
|
+
end
|
220
|
+
|
221
|
+
doc = document.new("name" => "")
|
222
|
+
doc.save.should be_true
|
223
|
+
|
224
|
+
document \
|
225
|
+
.stubs(:first) \
|
226
|
+
.with(:name => '') \
|
227
|
+
.returns(doc)
|
228
|
+
|
229
|
+
doc2 = document.new("name" => "")
|
230
|
+
doc2.should_not have_error_on(:name)
|
231
|
+
end
|
232
|
+
|
233
|
+
should "allow entries that differ only in case by default" do
|
234
|
+
document = Class.new do
|
235
|
+
include MongoMapper::Document
|
236
|
+
set_collection_name 'test'
|
237
|
+
|
238
|
+
key :name
|
239
|
+
validates_uniqueness_of :name
|
240
|
+
end
|
241
|
+
|
242
|
+
doc = document.new("name" => "BLAMMO")
|
243
|
+
doc.save.should be_true
|
244
|
+
|
245
|
+
doc2 = document.new("name" => "blammo")
|
246
|
+
doc2.should_not have_error_on(:name)
|
247
|
+
end
|
248
|
+
|
249
|
+
context "with :case_sensitive => false" do
|
250
|
+
setup do
|
251
|
+
@document = Class.new do
|
252
|
+
include MongoMapper::Document
|
253
|
+
set_collection_name 'test'
|
254
|
+
|
255
|
+
key :name
|
256
|
+
validates_uniqueness_of :name, :case_sensitive => false
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
should "fail on entries that differ only in case" do
|
261
|
+
doc = @document.new("name" => "BLAMMO")
|
262
|
+
doc.save.should be_true
|
263
|
+
|
264
|
+
doc2 = @document.new("name" => "blammo")
|
265
|
+
doc2.should have_error_on(:name)
|
266
|
+
end
|
267
|
+
|
268
|
+
should "not raise an error if value is nil" do
|
269
|
+
doc = @document.new("name" => nil)
|
270
|
+
lambda { doc.valid? }.should_not raise_error
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
context "scoped by a single attribute" do
|
275
|
+
setup do
|
276
|
+
@document = Class.new do
|
277
|
+
include MongoMapper::Document
|
278
|
+
set_collection_name 'test'
|
279
|
+
|
280
|
+
key :name, String
|
281
|
+
key :scope, String
|
282
|
+
validates_uniqueness_of :name, :scope => :scope
|
283
|
+
end
|
284
|
+
@document.collection.remove
|
285
|
+
end
|
286
|
+
|
287
|
+
should "fail if the same name exists in the scope" do
|
288
|
+
doc = @document.new("name" => "joe", "scope" => "one")
|
289
|
+
doc.save.should be_true
|
290
|
+
|
291
|
+
@document \
|
292
|
+
.stubs(:first) \
|
293
|
+
.with(:name => 'joe', :scope => "one") \
|
294
|
+
.returns(doc)
|
295
|
+
|
296
|
+
doc2 = @document.new("name" => "joe", "scope" => "one")
|
297
|
+
doc2.should have_error_on(:name)
|
298
|
+
end
|
299
|
+
|
300
|
+
should "pass if the same name exists in a different scope" do
|
301
|
+
doc = @document.new("name" => "joe", "scope" => "one")
|
302
|
+
doc.save.should be_true
|
303
|
+
|
304
|
+
@document \
|
305
|
+
.stubs(:first) \
|
306
|
+
.with(:name => 'joe', :scope => 'two') \
|
307
|
+
.returns(nil)
|
308
|
+
|
309
|
+
doc2 = @document.new("name" => "joe", "scope" => "two")
|
310
|
+
doc2.should_not have_error_on(:name)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
context "scoped by a multiple attributes" do
|
315
|
+
setup do
|
316
|
+
@document = Class.new do
|
317
|
+
include MongoMapper::Document
|
318
|
+
set_collection_name 'test'
|
319
|
+
|
320
|
+
key :name, String
|
321
|
+
key :first_scope, String
|
322
|
+
key :second_scope, String
|
323
|
+
validates_uniqueness_of :name, :scope => [:first_scope, :second_scope]
|
324
|
+
end
|
325
|
+
@document.collection.remove
|
326
|
+
end
|
327
|
+
|
328
|
+
should "fail if the same name exists in the scope" do
|
329
|
+
doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
|
330
|
+
doc.save.should be_true
|
331
|
+
|
332
|
+
@document \
|
333
|
+
.stubs(:first) \
|
334
|
+
.with(:name => 'joe', :first_scope => 'one', :second_scope => 'two') \
|
335
|
+
.returns(doc)
|
336
|
+
|
337
|
+
doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
|
338
|
+
doc2.should have_error_on(:name)
|
339
|
+
end
|
340
|
+
|
341
|
+
should "pass if the same name exists in a different scope" do
|
342
|
+
doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
|
343
|
+
doc.save.should be_true
|
344
|
+
|
345
|
+
@document \
|
346
|
+
.stubs(:first) \
|
347
|
+
.with(:name => 'joe', :first_scope => 'one', :second_scope => 'one') \
|
348
|
+
.returns(nil)
|
349
|
+
|
350
|
+
doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "one")
|
351
|
+
doc2.should_not have_error_on(:name)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
context "validates uniqueness of with :unique shortcut" do
|
357
|
+
should "work" do
|
358
|
+
@document = Class.new do
|
359
|
+
include MongoMapper::Document
|
360
|
+
set_collection_name 'test'
|
361
|
+
|
362
|
+
key :name, String, :unique => true
|
363
|
+
end
|
364
|
+
@document.collection.remove
|
365
|
+
|
366
|
+
doc = @document.create(:name => 'John')
|
367
|
+
doc.should_not have_error_on(:name)
|
368
|
+
|
369
|
+
@document \
|
370
|
+
.stubs(:first) \
|
371
|
+
.with(:name => 'John') \
|
372
|
+
.returns(doc)
|
373
|
+
|
374
|
+
second_john = @document.create(:name => 'John')
|
375
|
+
second_john.should have_error_on(:name, 'has already been taken')
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
data/test/models.rb
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
# custom type
|
2
|
+
class WindowSize
|
3
|
+
attr_reader :width, :height
|
4
|
+
|
5
|
+
def self.to_mongo(value)
|
6
|
+
value.to_a
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.from_mongo(value)
|
10
|
+
value.is_a?(self) ? value : WindowSize.new(value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
@width, @height = args.flatten
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_a
|
18
|
+
[width, height]
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
other.is_a?(self.class) && other.width == width && other.height == height
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module AccountsExtensions
|
27
|
+
def inactive
|
28
|
+
all(:last_logged_in => nil)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Post
|
33
|
+
include MongoMapper::Document
|
34
|
+
|
35
|
+
key :title, String
|
36
|
+
key :body, String
|
37
|
+
|
38
|
+
many :comments, :as => :commentable, :class_name => 'PostComment'
|
39
|
+
|
40
|
+
timestamps!
|
41
|
+
end
|
42
|
+
|
43
|
+
class PostComment
|
44
|
+
include MongoMapper::Document
|
45
|
+
|
46
|
+
key :username, String, :default => 'Anonymous'
|
47
|
+
key :body, String
|
48
|
+
|
49
|
+
key :commentable_id, ObjectId
|
50
|
+
key :commentable_type, String
|
51
|
+
belongs_to :commentable, :polymorphic => true
|
52
|
+
|
53
|
+
timestamps!
|
54
|
+
end
|
55
|
+
|
56
|
+
class Address
|
57
|
+
include MongoMapper::EmbeddedDocument
|
58
|
+
|
59
|
+
key :address, String
|
60
|
+
key :city, String
|
61
|
+
key :state, String
|
62
|
+
key :zip, Integer
|
63
|
+
end
|
64
|
+
|
65
|
+
class Message
|
66
|
+
include MongoMapper::Document
|
67
|
+
|
68
|
+
key :body, String
|
69
|
+
key :position, Integer
|
70
|
+
key :_type, String
|
71
|
+
key :room_id, ObjectId
|
72
|
+
|
73
|
+
belongs_to :room
|
74
|
+
end
|
75
|
+
|
76
|
+
class Enter < Message; end
|
77
|
+
class Exit < Message; end
|
78
|
+
class Chat < Message; end
|
79
|
+
|
80
|
+
class Room
|
81
|
+
include MongoMapper::Document
|
82
|
+
|
83
|
+
key :name, String
|
84
|
+
many :messages, :polymorphic => true, :order => 'position' do
|
85
|
+
def older
|
86
|
+
all(:position => {'$gt' => 5})
|
87
|
+
end
|
88
|
+
end
|
89
|
+
many :latest_messages, :class_name => 'Message', :order => 'position desc', :limit => 2
|
90
|
+
|
91
|
+
many :accounts, :polymorphic => true, :extend => AccountsExtensions
|
92
|
+
end
|
93
|
+
|
94
|
+
class Account
|
95
|
+
include MongoMapper::Document
|
96
|
+
|
97
|
+
key :_type, String
|
98
|
+
key :room_id, ObjectId
|
99
|
+
key :last_logged_in, Time
|
100
|
+
|
101
|
+
belongs_to :room
|
102
|
+
end
|
103
|
+
class User < Account; end
|
104
|
+
class Bot < Account; end
|
105
|
+
|
106
|
+
class Answer
|
107
|
+
include MongoMapper::Document
|
108
|
+
|
109
|
+
key :body, String
|
110
|
+
end
|
111
|
+
|
112
|
+
module PeopleExtensions
|
113
|
+
def find_by_name(name)
|
114
|
+
detect { |p| p.name == name }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
module CollaboratorsExtensions
|
119
|
+
def top
|
120
|
+
first
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Project
|
125
|
+
include MongoMapper::Document
|
126
|
+
|
127
|
+
key :name, String
|
128
|
+
|
129
|
+
many :people, :extend => PeopleExtensions
|
130
|
+
many :collaborators, :extend => CollaboratorsExtensions
|
131
|
+
|
132
|
+
many :statuses, :order => 'position' do
|
133
|
+
def open
|
134
|
+
all(:name => %w(New Assigned))
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
many :addresses do
|
139
|
+
def find_all_by_state(state)
|
140
|
+
# can't use select here for some reason
|
141
|
+
find_all { |a| a.state == state }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class Collaborator
|
147
|
+
include MongoMapper::Document
|
148
|
+
key :project_id, ObjectId
|
149
|
+
key :name, String
|
150
|
+
belongs_to :project
|
151
|
+
end
|
152
|
+
|
153
|
+
class Status
|
154
|
+
include MongoMapper::Document
|
155
|
+
|
156
|
+
key :project_id, ObjectId
|
157
|
+
key :target_id, ObjectId
|
158
|
+
key :target_type, String
|
159
|
+
key :name, String, :required => true
|
160
|
+
key :position, Integer
|
161
|
+
|
162
|
+
belongs_to :project
|
163
|
+
belongs_to :target, :polymorphic => true
|
164
|
+
end
|
165
|
+
|
166
|
+
class RealPerson
|
167
|
+
include MongoMapper::Document
|
168
|
+
|
169
|
+
key :room_id, ObjectId
|
170
|
+
key :name, String
|
171
|
+
|
172
|
+
belongs_to :room
|
173
|
+
|
174
|
+
many :pets
|
175
|
+
|
176
|
+
def realname=(n)
|
177
|
+
self.name = n
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class Person
|
182
|
+
include MongoMapper::EmbeddedDocument
|
183
|
+
|
184
|
+
key :name, String
|
185
|
+
key :child, Person
|
186
|
+
|
187
|
+
many :pets
|
188
|
+
end
|
189
|
+
|
190
|
+
class Pet
|
191
|
+
include MongoMapper::EmbeddedDocument
|
192
|
+
|
193
|
+
key :name, String
|
194
|
+
key :species, String
|
195
|
+
end
|
196
|
+
|
197
|
+
class Media
|
198
|
+
include MongoMapper::EmbeddedDocument
|
199
|
+
|
200
|
+
key :_type, String
|
201
|
+
key :file, String
|
202
|
+
|
203
|
+
key :visible, Boolean
|
204
|
+
end
|
205
|
+
|
206
|
+
class Video < Media
|
207
|
+
key :length, Integer
|
208
|
+
end
|
209
|
+
|
210
|
+
class Image < Media
|
211
|
+
key :width, Integer
|
212
|
+
key :height, Integer
|
213
|
+
end
|
214
|
+
|
215
|
+
class Music < Media
|
216
|
+
key :bitrate, String
|
217
|
+
end
|
218
|
+
|
219
|
+
class Catalog
|
220
|
+
include MongoMapper::Document
|
221
|
+
|
222
|
+
many :medias, :polymorphic => true do
|
223
|
+
def visible
|
224
|
+
# for some reason we can't use select here
|
225
|
+
find_all { |m| m.visible? }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
module TrModels
|
231
|
+
class Transport
|
232
|
+
include MongoMapper::EmbeddedDocument
|
233
|
+
|
234
|
+
key :_type, String
|
235
|
+
key :license_plate, String
|
236
|
+
key :purchased_on, Date
|
237
|
+
end
|
238
|
+
|
239
|
+
class Car < TrModels::Transport
|
240
|
+
include MongoMapper::EmbeddedDocument
|
241
|
+
|
242
|
+
key :model, String
|
243
|
+
key :year, Integer
|
244
|
+
end
|
245
|
+
|
246
|
+
class Bus < TrModels::Transport
|
247
|
+
include MongoMapper::EmbeddedDocument
|
248
|
+
|
249
|
+
key :max_passengers, Integer
|
250
|
+
end
|
251
|
+
|
252
|
+
class Ambulance < TrModels::Transport
|
253
|
+
include MongoMapper::EmbeddedDocument
|
254
|
+
|
255
|
+
key :icu, Boolean
|
256
|
+
end
|
257
|
+
|
258
|
+
class Fleet
|
259
|
+
include MongoMapper::Document
|
260
|
+
|
261
|
+
module TransportsExtension
|
262
|
+
def to_be_replaced
|
263
|
+
# for some reason we can't use select
|
264
|
+
find_all { |t| t.purchased_on < 2.years.ago.to_date }
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
many :transports, :polymorphic => true, :class_name => "TrModels::Transport", :extend => TransportsExtension
|
269
|
+
key :name, String
|
270
|
+
end
|
271
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module CustomMatchers
|
2
|
+
custom_matcher :be_nil do |receiver, matcher, args|
|
3
|
+
matcher.positive_failure_message = "Expected #{receiver} to be nil but it wasn't"
|
4
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be nil but it was"
|
5
|
+
receiver.nil?
|
6
|
+
end
|
7
|
+
|
8
|
+
custom_matcher :be_blank do |receiver, matcher, args|
|
9
|
+
matcher.positive_failure_message = "Expected #{receiver} to be blank but it wasn't"
|
10
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be blank but it was"
|
11
|
+
receiver.blank?
|
12
|
+
end
|
13
|
+
|
14
|
+
custom_matcher :be_true do |receiver, matcher, args|
|
15
|
+
matcher.positive_failure_message = "Expected #{receiver} to be true but it wasn't"
|
16
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be true but it was"
|
17
|
+
receiver.eql?(true)
|
18
|
+
end
|
19
|
+
|
20
|
+
custom_matcher :be_false do |receiver, matcher, args|
|
21
|
+
matcher.positive_failure_message = "Expected #{receiver} to be false but it wasn't"
|
22
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be false but it was"
|
23
|
+
receiver.eql?(false)
|
24
|
+
end
|
25
|
+
|
26
|
+
custom_matcher :be_valid do |receiver, matcher, args|
|
27
|
+
matcher.positive_failure_message = "Expected to be valid but it was invalid #{receiver.errors.inspect}"
|
28
|
+
matcher.negative_failure_message = "Expected to be invalid but it was valid #{receiver.errors.inspect}"
|
29
|
+
receiver.valid?
|
30
|
+
end
|
31
|
+
|
32
|
+
custom_matcher :have_error_on do |receiver, matcher, args|
|
33
|
+
receiver.valid?
|
34
|
+
attribute = args[0]
|
35
|
+
expected_message = args[1]
|
36
|
+
|
37
|
+
if expected_message.nil?
|
38
|
+
matcher.positive_failure_message = "#{receiver} had no errors on #{attribute}"
|
39
|
+
matcher.negative_failure_message = "#{receiver} had errors on #{attribute} #{receiver.errors.inspect}"
|
40
|
+
!receiver.errors.on(attribute).blank?
|
41
|
+
else
|
42
|
+
actual = receiver.errors.on(attribute)
|
43
|
+
matcher.positive_failure_message = %Q(Expected error on #{attribute} to be "#{expected_message}" but was "#{actual}")
|
44
|
+
matcher.negative_failure_message = %Q(Expected error on #{attribute} not to be "#{expected_message}" but was "#{actual}")
|
45
|
+
actual == expected_message
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
custom_matcher :have_index do |receiver, matcher, args|
|
50
|
+
index_name = args[0]
|
51
|
+
matcher.positive_failure_message = "#{receiver} does not have index named #{index_name}, but should"
|
52
|
+
matcher.negative_failure_message = "#{receiver} does have index named #{index_name}, but should not"
|
53
|
+
!receiver.collection.index_information.detect { |index| index[0] == index_name }.nil?
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Test::Unit::TestCase
|
2
|
+
def run_with_test_timing(*args, &block)
|
3
|
+
begin_time = Time.now
|
4
|
+
run_without_test_timing(*args, &block)
|
5
|
+
end_time = Time.now
|
6
|
+
|
7
|
+
duration = end_time - begin_time
|
8
|
+
threshold = 0.5
|
9
|
+
|
10
|
+
if duration > threshold
|
11
|
+
puts "\nSLOW TEST: #{duration} - #{self.name}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
alias_method_chain :run, :test_timing unless method_defined?(:run_without_test_timing)
|
16
|
+
end
|