em-mongo 0.3.6 → 0.4.0
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/VERSION +1 -1
- data/lib/em-mongo/collection.rb +756 -23
- data/lib/em-mongo/connection.rb +100 -95
- data/lib/em-mongo/conversions.rb +2 -2
- data/lib/em-mongo/core_ext.rb +20 -0
- data/lib/em-mongo/cursor.rb +537 -0
- data/lib/em-mongo/database.rb +348 -20
- data/lib/em-mongo/exceptions.rb +2 -2
- data/lib/em-mongo/prev.rb +53 -0
- data/lib/em-mongo/request_response.rb +34 -0
- data/lib/em-mongo/server_response.rb +32 -0
- data/lib/em-mongo/support.rb +4 -4
- data/lib/em-mongo.rb +5 -0
- data/spec/integration/collection_spec.rb +654 -154
- data/spec/integration/cursor_spec.rb +350 -0
- data/spec/integration/database_spec.rb +149 -3
- data/spec/integration/request_response_spec.rb +63 -0
- metadata +12 -2
@@ -0,0 +1,350 @@
|
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__) + '/../')
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
describe EMMongo::Cursor do
|
6
|
+
include EM::Spec
|
7
|
+
|
8
|
+
it 'should describe itself via inspect' do
|
9
|
+
@conn, @coll = connection_and_collection
|
10
|
+
cursor = EM::Mongo::Cursor.new( @coll, :selector => {'a' => 1} )
|
11
|
+
cursor.inspect.should == "<EM::Mongo::Cursor:0x#{cursor.object_id.to_s} namespace='#{@coll.db.name}.#{@coll.name}' " +
|
12
|
+
"@selector=#{cursor.selector.inspect}>"
|
13
|
+
done
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should explain itself' do
|
17
|
+
@conn, @coll = connection_and_collection
|
18
|
+
cursor = EM::Mongo::Cursor.new(@coll, :selector => {'a' => 1} )
|
19
|
+
cursor.explain.callback do |explanation|
|
20
|
+
explanation['cursor'].should_not be_nil
|
21
|
+
explanation['n'].should be_kind_of Numeric
|
22
|
+
explanation['millis'].should be_kind_of Numeric
|
23
|
+
explanation['nscanned'].should be_kind_of Numeric
|
24
|
+
done
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should allow limit and skip to be chained" do
|
29
|
+
@conn, @coll = connection_and_collection
|
30
|
+
cursor = EM::Mongo::Cursor.new(@coll)
|
31
|
+
all = []
|
32
|
+
10.times do |i|
|
33
|
+
all << {"x" => i}
|
34
|
+
@coll.save(all[-1])
|
35
|
+
end
|
36
|
+
|
37
|
+
cursor.limit(5).skip(3).sort("x",1).to_a.callback do |results|
|
38
|
+
all.slice(3...8).each_with_index do |item,idx|
|
39
|
+
results[idx]["x"].should == item["x"]
|
40
|
+
end
|
41
|
+
done
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allow a limit larger than the batch size" do
|
46
|
+
@conn, @coll = connection_and_collection
|
47
|
+
cursor = EM::Mongo::Cursor.new(@coll, :selector => {})
|
48
|
+
all = []
|
49
|
+
1501.times do |i|
|
50
|
+
@coll.insert(i.to_s => i.to_s)
|
51
|
+
end
|
52
|
+
cursor.limit(1500).to_a.callback do |docs|
|
53
|
+
docs.length.should == 1500
|
54
|
+
done
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
it "should say if it has next" do
|
61
|
+
@conn, @coll = connection_and_collection
|
62
|
+
cursor = EM::Mongo::Cursor.new(@coll)
|
63
|
+
1.times do |i|
|
64
|
+
@coll.save("x" => 1)
|
65
|
+
end
|
66
|
+
cursor.has_next?.callback do |result|
|
67
|
+
result.should be_true
|
68
|
+
cursor.next_document.callback do |doc|
|
69
|
+
cursor.has_next?.callback do |result|
|
70
|
+
result.should be_false
|
71
|
+
done
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should rewind" do
|
78
|
+
@conn, @coll = connection_and_collection
|
79
|
+
cursor = EM::Mongo::Cursor.new(@coll)
|
80
|
+
100.times do |i|
|
81
|
+
@coll.save("x" => 1)
|
82
|
+
end
|
83
|
+
cursor.to_a.callback do |r1|
|
84
|
+
r1.length.should == 100
|
85
|
+
cursor.to_a.callback do |r2|
|
86
|
+
r2.length.should == 0
|
87
|
+
cursor.rewind!
|
88
|
+
cursor.to_a.callback do |r3|
|
89
|
+
r3.length.should == 100
|
90
|
+
done
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "Get More" do
|
98
|
+
it "should refill via get more" do
|
99
|
+
@conn, @coll = connection_and_collection
|
100
|
+
cursor = EM::Mongo::Cursor.new(@coll)
|
101
|
+
1000.times do |i|
|
102
|
+
@coll.save("x" => 1)
|
103
|
+
end
|
104
|
+
cursor.to_a.callback do |results|
|
105
|
+
results.length.should == 1000
|
106
|
+
done
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "Count" do
|
112
|
+
|
113
|
+
it 'should count 0 records in a empty collection' do
|
114
|
+
@conn, @coll = connection_and_collection
|
115
|
+
cursor = EM::Mongo::Cursor.new(@coll)
|
116
|
+
cursor.count.callback do |c|
|
117
|
+
c.should == 0
|
118
|
+
done
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should count records in a collection" do
|
123
|
+
@conn, @coll = connection_and_collection
|
124
|
+
cursor = EM::Mongo::Cursor.new(@coll)
|
125
|
+
10.times do |i|
|
126
|
+
@coll.save("x" => 1)
|
127
|
+
end
|
128
|
+
|
129
|
+
cursor.count.callback do |c|
|
130
|
+
c.should == 10
|
131
|
+
done
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should ignore skip and limit by default" do
|
136
|
+
@conn, @coll = connection_and_collection
|
137
|
+
cursor = EM::Mongo::Cursor.new(@coll).skip(5).limit(5)
|
138
|
+
10.times do |i|
|
139
|
+
@coll.save("x" => i)
|
140
|
+
end
|
141
|
+
|
142
|
+
cursor.count.callback do |c|
|
143
|
+
c.should == 10
|
144
|
+
done
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should account for skip when requested" do
|
149
|
+
@conn, @coll = connection_and_collection
|
150
|
+
cursor = EM::Mongo::Cursor.new(@coll).limit(5)
|
151
|
+
10.times do |i|
|
152
|
+
@coll.save("x" => i)
|
153
|
+
end
|
154
|
+
|
155
|
+
cursor.count(true).callback do |c|
|
156
|
+
c.should == 5
|
157
|
+
done
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should account for skip when requested" do
|
162
|
+
@conn, @coll = connection_and_collection
|
163
|
+
cursor = EM::Mongo::Cursor.new(@coll).skip(5)
|
164
|
+
10.times do |i|
|
165
|
+
@coll.save("x" => i)
|
166
|
+
end
|
167
|
+
|
168
|
+
cursor.count(true).callback do |c|
|
169
|
+
c.should == 5
|
170
|
+
done
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should count based on a simple selector" do
|
175
|
+
@conn, @coll = connection_and_collection
|
176
|
+
cursor = EM::Mongo::Cursor.new(@coll, :selector => {"x"=>1})
|
177
|
+
10.times do |i|
|
178
|
+
@coll.save("x" => i)
|
179
|
+
end
|
180
|
+
|
181
|
+
cursor.count(true).callback do |c|
|
182
|
+
c.should == 1
|
183
|
+
done
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should count based on a selector with an operator" do
|
188
|
+
@conn, @coll = connection_and_collection
|
189
|
+
cursor = EM::Mongo::Cursor.new(@coll, :selector => {"x"=>{"$lt"=>5}})
|
190
|
+
10.times do |i|
|
191
|
+
@coll.save("x" => i)
|
192
|
+
end
|
193
|
+
|
194
|
+
cursor.count(true).callback do |c|
|
195
|
+
c.should == 5
|
196
|
+
done
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should count a non-existing collection as 0 without vomiting blood" do
|
201
|
+
@conn, @coll = connection_and_collection
|
202
|
+
@coll = @conn.db.collection('imnotreallyheredontlookatme')
|
203
|
+
|
204
|
+
cursor = EM::Mongo::Cursor.new(@coll)
|
205
|
+
|
206
|
+
cursor.count(true).callback do |c|
|
207
|
+
c.should == 0
|
208
|
+
done
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "Sort" do
|
214
|
+
it "should sort ascending" do
|
215
|
+
@conn, @coll = connection_and_collection
|
216
|
+
5.times do |i|
|
217
|
+
@coll.save("x" => i)
|
218
|
+
end
|
219
|
+
cursor = EM::Mongo::Cursor.new(@coll).sort(:x, 1)
|
220
|
+
cursor.next_document.callback do |first|
|
221
|
+
first["x"].should == 0
|
222
|
+
done
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should sort descending" do
|
227
|
+
@conn, @coll = connection_and_collection
|
228
|
+
5.times do |i|
|
229
|
+
@coll.save("x" => i)
|
230
|
+
end
|
231
|
+
cursor = EM::Mongo::Cursor.new(@coll).sort(:x, -1)
|
232
|
+
cursor.next_document.callback do |first|
|
233
|
+
first["x"].should == 4
|
234
|
+
done
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should sort descending using a symbol sort dir" do
|
239
|
+
@conn, @coll = connection_and_collection
|
240
|
+
5.times do |i|
|
241
|
+
@coll.save("x" => i)
|
242
|
+
end
|
243
|
+
cursor = EM::Mongo::Cursor.new(@coll).sort(["x", :desc])
|
244
|
+
cursor.next_document.callback do |first|
|
245
|
+
first["x"].should == 4
|
246
|
+
done
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should not allow sort to be called on an executed cursor" do
|
251
|
+
@conn, @coll = connection_and_collection
|
252
|
+
5.times do |i|
|
253
|
+
@coll.save("x" => i)
|
254
|
+
end
|
255
|
+
cursor = EM::Mongo::Cursor.new(@coll).sort(["x", :desc])
|
256
|
+
cursor.next_document.callback do |first|
|
257
|
+
lambda { cursor.sort("x",1) }.should raise_error EM::Mongo::InvalidOperation
|
258
|
+
done
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should sort by dates" do
|
263
|
+
@conn, @coll = connection_and_collection
|
264
|
+
5.times do |i|
|
265
|
+
@coll.insert("x" => Time.utc(2000 + i))
|
266
|
+
end
|
267
|
+
cursor = EM::Mongo::Cursor.new(@coll).sort(["x", :desc])
|
268
|
+
cursor.next_document.callback do |first|
|
269
|
+
first["x"].year.should == 2004
|
270
|
+
done
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe "Each" do
|
275
|
+
it "should iterate through each doc, returning null when done" do
|
276
|
+
@conn, @coll = connection_and_collection
|
277
|
+
5.times do |i|
|
278
|
+
@coll.insert("x" => i)
|
279
|
+
end
|
280
|
+
cursor = EM::Mongo::Cursor.new(@coll)
|
281
|
+
counter = 0
|
282
|
+
cursor.each do |doc|
|
283
|
+
if doc
|
284
|
+
counter+=1
|
285
|
+
else
|
286
|
+
counter.should == 5
|
287
|
+
done
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe "to_a" do
|
294
|
+
it "should return an array of all documents in a query" do
|
295
|
+
@conn, @coll = connection_and_collection
|
296
|
+
5.times do |i|
|
297
|
+
@coll.insert("x" => i)
|
298
|
+
end
|
299
|
+
cursor = EM::Mongo::Cursor.new(@coll).sort("x",1)
|
300
|
+
cursor.to_a.callback do |docs|
|
301
|
+
docs.length.should == 5
|
302
|
+
5.times do |i|
|
303
|
+
docs[i]["x"].should == i
|
304
|
+
end
|
305
|
+
done
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
describe "Transformer (a robot in disguise)" do
|
311
|
+
it "should set the transformer when passed in the constructor" do
|
312
|
+
@conn, @coll = connection_and_collection
|
313
|
+
transformer = Proc.new {|doc|doc}
|
314
|
+
cursor = EM::Mongo::Cursor.new(@coll, :transformer => transformer)
|
315
|
+
cursor.transformer.should == transformer
|
316
|
+
done
|
317
|
+
end
|
318
|
+
it "should transform docs with next" do
|
319
|
+
@conn, @coll = connection_and_collection
|
320
|
+
@coll.insert({:a=>1})
|
321
|
+
klass = Struct.new(:id,:a)
|
322
|
+
transformer = Proc.new {|doc|klass.new(doc['_id'],doc['a'])}
|
323
|
+
cursor = EM::Mongo::Cursor.new(@coll, :transformer => transformer)
|
324
|
+
cursor.next.callback do |doc|
|
325
|
+
doc.should be_kind_of klass
|
326
|
+
doc.id.should be_kind_of BSON::ObjectId
|
327
|
+
doc.a.should == 1
|
328
|
+
done
|
329
|
+
end
|
330
|
+
end
|
331
|
+
it "should transform docs with each" do
|
332
|
+
@conn, @coll = connection_and_collection
|
333
|
+
@coll.insert({:a=>1})
|
334
|
+
klass = Struct.new(:id, :a)
|
335
|
+
transformer = Proc.new { |doc| klass.new(doc['_id'], doc['a']) }
|
336
|
+
cursor = EM::Mongo::Cursor.new(@coll, :transformer => transformer)
|
337
|
+
|
338
|
+
cursor.each do |doc|
|
339
|
+
if doc
|
340
|
+
doc.should be_kind_of klass
|
341
|
+
doc.id.should be_kind_of BSON::ObjectId
|
342
|
+
doc.a.should == 1
|
343
|
+
end
|
344
|
+
done
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
@@ -6,22 +6,76 @@ describe EMMongo::Database do
|
|
6
6
|
it 'should add a user' do
|
7
7
|
@conn = EM::Mongo::Connection.new
|
8
8
|
@db = @conn.db
|
9
|
-
@db.
|
9
|
+
@db.collection(EM::Mongo::Database::SYSTEM_USER_COLLECTION).remove({})
|
10
|
+
@db.add_user('test', 'test').callback do |res|
|
10
11
|
res.should_not == nil
|
11
12
|
res.should be_a_kind_of(BSON::ObjectId)
|
12
13
|
done
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
# This test requires the above test.
|
17
17
|
it 'should authenticate a user' do
|
18
18
|
@conn = EM::Mongo::Connection.new
|
19
19
|
@db = @conn.db
|
20
|
-
@db.
|
20
|
+
@db.add_user('test', 'test')
|
21
|
+
@db.authenticate('test', 'test').callback do |res|
|
21
22
|
res.should == true
|
22
23
|
done
|
23
24
|
end
|
24
25
|
end
|
26
|
+
|
27
|
+
it "should create a collection" do
|
28
|
+
@conn = EM::Mongo::Connection.new
|
29
|
+
@db = @conn.db
|
30
|
+
@db.create_collection("a").callback do |col|
|
31
|
+
col.should be_kind_of EM::Mongo::Collection
|
32
|
+
col.name.should == "a"
|
33
|
+
done
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should drop a collection" do
|
38
|
+
@conn = EM::Mongo::Connection.new
|
39
|
+
@db = @conn.db
|
40
|
+
@db.create_collection("a").callback do |col|
|
41
|
+
@db.drop_collection("a").callback do
|
42
|
+
@db.collection_names.callback do |names|
|
43
|
+
names.should_not include "a"
|
44
|
+
done
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should provide a list of collection names in the database" do
|
51
|
+
@conn = EM::Mongo::Connection.new
|
52
|
+
@db = @conn.db
|
53
|
+
@db.create_collection "a"
|
54
|
+
@db.create_collection("b").callback do
|
55
|
+
@db.collection_names.callback do |names|
|
56
|
+
names.should include "a"
|
57
|
+
names.should include "b"
|
58
|
+
done
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should provide a list of collections in the database" do
|
64
|
+
@conn = EM::Mongo::Connection.new
|
65
|
+
@db = @conn.db
|
66
|
+
@db.create_collection "a"
|
67
|
+
@db.create_collection("b").callback do
|
68
|
+
@db.collection_names.callback do |names|
|
69
|
+
@db.collections do |collections|
|
70
|
+
collections.length.should == names.length
|
71
|
+
collections.each do |col|
|
72
|
+
col.should be_kind_of EM::Mongo::Collection
|
73
|
+
end
|
74
|
+
end
|
75
|
+
done
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
25
79
|
|
26
80
|
it 'should cache collections correctly' do
|
27
81
|
@conn = EM::Mongo::Connection.new
|
@@ -34,4 +88,96 @@ describe EMMongo::Database do
|
|
34
88
|
done
|
35
89
|
end
|
36
90
|
|
91
|
+
describe "Errors" do
|
92
|
+
describe "when there are no errors" do
|
93
|
+
it "should return a nil 'err' from get_last_error" do
|
94
|
+
@conn = EM::Mongo::Connection.new
|
95
|
+
@db = @conn.db
|
96
|
+
@db.reset_error_history.callback do
|
97
|
+
@db.get_last_error.callback do |doc|
|
98
|
+
doc['err'].should be_nil
|
99
|
+
done
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
it "should have a false error?" do
|
104
|
+
@conn = EM::Mongo::Connection.new
|
105
|
+
@db = @conn.db
|
106
|
+
@db.reset_error_history.callback do
|
107
|
+
@db.error?.callback do |result|
|
108
|
+
result.should == false
|
109
|
+
done
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
describe "when there are errors" do
|
115
|
+
it "should return a value for 'err' from get_last_error" do
|
116
|
+
@conn = EM::Mongo::Connection.new
|
117
|
+
@db = @conn.db
|
118
|
+
@db.command({:forceerror=>1}, :check_response => false).callback do
|
119
|
+
@db.get_last_error.callback do |doc|
|
120
|
+
doc['err'].should_not be_nil
|
121
|
+
done
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
it "should have a true error?" do
|
126
|
+
@conn = EM::Mongo::Connection.new
|
127
|
+
@db = @conn.db
|
128
|
+
@db.command({:forceerror=>1}, :check_response => false).callback do
|
129
|
+
@db.error?.callback do |result|
|
130
|
+
result.should == true
|
131
|
+
done
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
it "should be able to reset the error history" do
|
137
|
+
@conn = EM::Mongo::Connection.new
|
138
|
+
@db = @conn.db
|
139
|
+
@db.command({:forceerror=>1}, :check_response => false).callback do
|
140
|
+
@db.reset_error_history.callback do
|
141
|
+
@db.error?.callback do |result|
|
142
|
+
result.should == false
|
143
|
+
done
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "Command" do
|
151
|
+
it "should fail when the database returns an error" do
|
152
|
+
@conn = EM::Mongo::Connection.new
|
153
|
+
@db = @conn.db
|
154
|
+
@db.command({:non_command => 1}, :check_response => true).errback do
|
155
|
+
done
|
156
|
+
end
|
157
|
+
end
|
158
|
+
it "should not fail when checkresponse is false" do
|
159
|
+
@conn = EM::Mongo::Connection.new
|
160
|
+
@db = @conn.db
|
161
|
+
@db.command({:non_command => 1}, :check_response => false).callback do
|
162
|
+
done
|
163
|
+
end
|
164
|
+
end
|
165
|
+
it "should succesfully execute a valid command" do
|
166
|
+
@conn, @coll = connection_and_collection
|
167
|
+
@db = @conn.db
|
168
|
+
@coll.insert( {:col => {:easy => "andy" } } )
|
169
|
+
@db.command({:collstats => @coll.name}).callback do |doc|
|
170
|
+
doc.should_not be_nil
|
171
|
+
doc["count"].should == 1
|
172
|
+
done
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "Indexes" do
|
178
|
+
#Index functions are integration tested via the collection specs. Maybe the wrong order,
|
179
|
+
#but the collection index functions all call down to the database index functions, and the
|
180
|
+
#tests would simply duplicate eachother
|
181
|
+
end
|
182
|
+
|
37
183
|
end
|