em-mongo 0.2.10 → 0.2.11
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/em-mongo/connection.rb +35 -19
- data/spec/integration/collection_spec.rb +219 -0
- data/spec/integration/connection_spec.rb +48 -0
- metadata +14 -3
data/lib/em-mongo/connection.rb
CHANGED
@@ -5,12 +5,14 @@ module EM::Mongo
|
|
5
5
|
DEFAULT_NS = "ns"
|
6
6
|
DEFAULT_QUERY_DOCS = 101
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
class EMConnection < EM::Connection
|
9
|
+
MAX_RETRIES = 5
|
10
|
+
|
11
|
+
class Error < Exception;
|
10
12
|
class ConnectionNotBound
|
11
13
|
end
|
12
14
|
end
|
13
|
-
|
15
|
+
|
14
16
|
include EM::Deferrable
|
15
17
|
|
16
18
|
RESERVED = 0
|
@@ -20,7 +22,7 @@ module EM::Mongo
|
|
20
22
|
OP_INSERT = 2002
|
21
23
|
OP_QUERY = 2004
|
22
24
|
OP_DELETE = 2006
|
23
|
-
|
25
|
+
|
24
26
|
STANDARD_HEADER_SIZE = 16
|
25
27
|
RESPONSE_HEADER_SIZE = 20
|
26
28
|
|
@@ -50,7 +52,6 @@ module EM::Mongo
|
|
50
52
|
end
|
51
53
|
|
52
54
|
def send_command(buffer, request_id, &blk)
|
53
|
-
|
54
55
|
callback do
|
55
56
|
send_data buffer
|
56
57
|
end
|
@@ -81,7 +82,7 @@ module EM::Mongo
|
|
81
82
|
message.put_int(flags)
|
82
83
|
|
83
84
|
message.put_array(BSON::BSON_CODER.serialize(selector, true, true).to_a)
|
84
|
-
message.put_array(BSON::BSON_CODER.serialize(document, false, true).to_a)
|
85
|
+
message.put_array(BSON::BSON_CODER.serialize(document, false, true).to_a)
|
85
86
|
|
86
87
|
req_id = new_request_id
|
87
88
|
message.prepend!(message_headers(OP_UPDATE, req_id, message))
|
@@ -114,11 +115,12 @@ module EM::Mongo
|
|
114
115
|
# EM hooks
|
115
116
|
def initialize(options={})
|
116
117
|
@request_id = 0
|
118
|
+
@retries = 0
|
117
119
|
@responses = {}
|
118
120
|
@is_connected = false
|
119
121
|
@host = options[:host] || DEFAULT_IP
|
120
122
|
@port = options[:port] || DEFAULT_PORT
|
121
|
-
@on_unbind = options[:unbind_cb] || proc {}
|
123
|
+
@on_unbind = options[:unbind_cb] || proc {}
|
122
124
|
|
123
125
|
@on_close = proc {
|
124
126
|
raise Error, "failure with mongodb server #{@host}:#{@port}"
|
@@ -127,14 +129,15 @@ module EM::Mongo
|
|
127
129
|
errback { @on_close.call }
|
128
130
|
end
|
129
131
|
|
130
|
-
def self.connect(host = DEFAULT_IP, port = DEFAULT_PORT, timeout = nil)
|
131
|
-
opt = {:host => host, :port => port, :timeout => timeout}
|
132
|
+
def self.connect(host = DEFAULT_IP, port = DEFAULT_PORT, timeout = nil, opts = nil)
|
133
|
+
opt = {:host => host, :port => port, :timeout => timeout}.merge(opts)
|
132
134
|
EM.connect(host, port, self, opt)
|
133
135
|
end
|
134
136
|
|
135
137
|
def connection_completed
|
136
138
|
@buffer = BSON::ByteBuffer.new
|
137
139
|
@is_connected = true
|
140
|
+
@retries = 0
|
138
141
|
succeed
|
139
142
|
end
|
140
143
|
|
@@ -146,7 +149,7 @@ module EM::Mongo
|
|
146
149
|
def remaining_bytes(buffer)
|
147
150
|
buffer.size-buffer.position
|
148
151
|
end
|
149
|
-
|
152
|
+
|
150
153
|
def peek_size(buffer)
|
151
154
|
position= buffer.position
|
152
155
|
size= buffer.get_int
|
@@ -157,7 +160,7 @@ module EM::Mongo
|
|
157
160
|
def receive_data(data)
|
158
161
|
|
159
162
|
@buffer.append!(BSON::ByteBuffer.new(data.unpack('C*')))
|
160
|
-
|
163
|
+
|
161
164
|
@buffer.rewind
|
162
165
|
while message_received?(@buffer)
|
163
166
|
response_to, docs= next_response
|
@@ -173,12 +176,12 @@ module EM::Mongo
|
|
173
176
|
@buffer.clear
|
174
177
|
end
|
175
178
|
|
176
|
-
close_connection if @close_pending && @responses.empty?
|
177
|
-
|
179
|
+
close_connection if @close_pending && @responses.empty?
|
180
|
+
|
178
181
|
end
|
179
|
-
|
182
|
+
|
180
183
|
def next_response()
|
181
|
-
|
184
|
+
|
182
185
|
# Header
|
183
186
|
size = @buffer.get_int
|
184
187
|
request_id = @buffer.get_int
|
@@ -204,7 +207,20 @@ module EM::Mongo
|
|
204
207
|
|
205
208
|
def unbind
|
206
209
|
@is_connected = false
|
207
|
-
|
210
|
+
|
211
|
+
# XXX do we need to fail the responses here?
|
212
|
+
@request_id = 0
|
213
|
+
@responses = {}
|
214
|
+
|
215
|
+
set_deferred_status(nil)
|
216
|
+
|
217
|
+
if @retries >= MAX_RETRIES
|
218
|
+
@on_unbind.call
|
219
|
+
return
|
220
|
+
end
|
221
|
+
|
222
|
+
@retries += 1
|
223
|
+
EM.add_timer(5) { reconnect(@host, @port) }
|
208
224
|
end
|
209
225
|
|
210
226
|
def close
|
@@ -238,11 +254,11 @@ module EM::Mongo
|
|
238
254
|
end
|
239
255
|
end
|
240
256
|
class Connection
|
241
|
-
def initialize(host = DEFAULT_IP, port = DEFAULT_PORT, timeout = nil)
|
242
|
-
@em_connection = EMConnection.connect(host, port, timeout)
|
257
|
+
def initialize(host = DEFAULT_IP, port = DEFAULT_PORT, timeout = nil, opts = {})
|
258
|
+
@em_connection = EMConnection.connect(host, port, timeout, opts)
|
243
259
|
@db = {}
|
244
260
|
end
|
245
|
-
|
261
|
+
|
246
262
|
def db(name = DEFAULT_DB)
|
247
263
|
@db[name] ||= EM::Mongo::Database.new(name, @em_connection)
|
248
264
|
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe EMMongo::Collection do
|
4
|
+
include EM::SpecHelper
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
@numbers = {
|
8
|
+
1 => 'one',
|
9
|
+
2 => 'two',
|
10
|
+
3 => 'three',
|
11
|
+
4 => 'four',
|
12
|
+
5 => 'five',
|
13
|
+
6 => 'six',
|
14
|
+
7 => 'seven',
|
15
|
+
8 => 'eight',
|
16
|
+
9 => 'nine'
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
after(:all) do
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should insert an object' do
|
24
|
+
EM::Spec::Mongo.collection do |collection|
|
25
|
+
obj = collection.insert('hello' => 'world')
|
26
|
+
obj.keys.should include '_id'
|
27
|
+
obj['_id'].should be_a_kind_of String
|
28
|
+
obj['_id'].length.should == 24
|
29
|
+
EM::Spec::Mongo.close
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should find an object by attribute' do
|
34
|
+
EM::Spec::Mongo.collection do |collection|
|
35
|
+
collection.insert("hello" => 'world')
|
36
|
+
r = collection.find({"hello" => "world"},{}) do |res|
|
37
|
+
res.size.should >= 1
|
38
|
+
res[0]["hello"].should == "world"
|
39
|
+
EM::Spec::Mongo.close
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should find an object by id' do
|
45
|
+
EM::Spec::Mongo.collection do |collection|
|
46
|
+
obj = collection.insert('hello' => 'world')
|
47
|
+
collection.find({'_id' => obj['_id']},{}) do |res|
|
48
|
+
res.size.should >= 1
|
49
|
+
res[0]['hello'].should == "world"
|
50
|
+
EM::Spec::Mongo.close
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should find all objects' do
|
56
|
+
EM::Spec::Mongo.collection do |collection|
|
57
|
+
collection.insert('one' => 'one')
|
58
|
+
collection.insert('two' => 'two')
|
59
|
+
collection.find do |res|
|
60
|
+
res.size.should >= 2
|
61
|
+
EM::Spec::Mongo.close
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should find large sets of objects' do
|
67
|
+
EM::Spec::Mongo.collection do |collection|
|
68
|
+
(0..1500).each { |n| collection.insert({n.to_s => n.to_s}) }
|
69
|
+
collection.find do |res|
|
70
|
+
res.size.should == EM::Mongo::DEFAULT_QUERY_DOCS
|
71
|
+
collection.find({}, {:limit => 1500}) do |res|
|
72
|
+
res.size.should == 1500
|
73
|
+
EM::Spec::Mongo.close
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should update an object' do
|
80
|
+
EM::Spec::Mongo.collection do |collection|
|
81
|
+
obj = collection.insert('hello' => 'world')
|
82
|
+
collection.update({'hello' => 'world'}, {'hello' => 'newworld'})
|
83
|
+
collection.find({'_id' => obj['_id']},{}) do |res|
|
84
|
+
res[0]['hello'].should == 'newworld'
|
85
|
+
EM::Spec::Mongo.close
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should update an object with $inc' do
|
91
|
+
EM::Spec::Mongo.collection do |collection|
|
92
|
+
obj = collection.insert('hello' => 'world')
|
93
|
+
collection.update({'hello' => 'world'}, {'$inc' => {'count' => 1}})
|
94
|
+
collection.find({'_id' => obj['_id']},{}) do |res|
|
95
|
+
res.first['hello'].should == 'world'
|
96
|
+
res.first['count'].should == 1
|
97
|
+
EM::Spec::Mongo.close
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should remove an object' do
|
103
|
+
EM::Spec::Mongo.collection do |collection|
|
104
|
+
obj = collection.insert('hello' => 'world')
|
105
|
+
collection.remove('_id' => obj['_id'])
|
106
|
+
collection.find({'hello' => "world"}) do |res|
|
107
|
+
res.size.should == 0
|
108
|
+
EM::Spec::Mongo.close
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should remove all objects' do
|
114
|
+
EM::Spec::Mongo.collection do |collection|
|
115
|
+
collection.insert('one' => 'one')
|
116
|
+
collection.insert('two' => 'two')
|
117
|
+
collection.remove
|
118
|
+
collection.find do |res|
|
119
|
+
res.size.should == 0
|
120
|
+
EM::Spec::Mongo.close
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should insert a Time' do
|
126
|
+
EM::Spec::Mongo.collection do |collection|
|
127
|
+
t = Time.now.utc.freeze
|
128
|
+
collection.insert('date' => t)
|
129
|
+
collection.find do |res|
|
130
|
+
res[0]['date'].to_s.should == t.to_s
|
131
|
+
EM::Spec::Mongo.close
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should insert a complex object' do
|
137
|
+
EM::Spec::Mongo.collection do |collection|
|
138
|
+
obj = {
|
139
|
+
'array' => [1,2,3],
|
140
|
+
'float' => 123.456,
|
141
|
+
'hash' => {'boolean' => true},
|
142
|
+
'nil' => nil,
|
143
|
+
'symbol' => :name,
|
144
|
+
'string' => 'hello world',
|
145
|
+
'time' => Time.now.to_f,
|
146
|
+
'regex' => /abc$/ix
|
147
|
+
}
|
148
|
+
retobj = collection.insert(obj)
|
149
|
+
collection.find({'_id' => obj['_id']}) do |ret|
|
150
|
+
ret.size.should == 1
|
151
|
+
ret[0].each_key do |key|
|
152
|
+
ret[0][key].should == obj[key]
|
153
|
+
end
|
154
|
+
EM::Spec::Mongo.close
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should find an object using nested properties' do
|
161
|
+
EM::Spec::Mongo.collection do |collection|
|
162
|
+
collection.insert({
|
163
|
+
'name' => 'Google',
|
164
|
+
'address' => {
|
165
|
+
'city' => 'Mountain View',
|
166
|
+
'state' => 'California'}
|
167
|
+
})
|
168
|
+
|
169
|
+
collection.first('address.city' => 'Mountain View') do |res|
|
170
|
+
res['name'].should == 'Google'
|
171
|
+
EM::Spec::Mongo.close
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should find objects with specific values' do
|
177
|
+
EM::Spec::Mongo.collection do |collection|
|
178
|
+
@numbers.each do |num, word|
|
179
|
+
collection.insert({'num' => num, 'word' => word})
|
180
|
+
end
|
181
|
+
|
182
|
+
collection.find({'num' => {'$in' => [1,3,5]}}) do |res|
|
183
|
+
res.size.should == 3
|
184
|
+
res.map{|r| r['num'] }.sort.should == [1,3,5]
|
185
|
+
EM::Spec::Mongo.close
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should find objects greater than something' do
|
191
|
+
EM::Spec::Mongo.collection do |collection|
|
192
|
+
@numbers.each do |num, word|
|
193
|
+
collection.insert('num' => num, 'word' => word)
|
194
|
+
end
|
195
|
+
|
196
|
+
collection.find({'num' => {'$gt' => 3}}) do |res|
|
197
|
+
res.size.should == 6
|
198
|
+
res.map{|r| r['num'] }.sort.should == [4,5,6,7,8,9]
|
199
|
+
EM::Spec::Mongo.close
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'should handle multiple pending queries' do
|
205
|
+
EM::Spec::Mongo.collection do |collection|
|
206
|
+
id = collection.insert("foo" => "bar")['_id']
|
207
|
+
received = 0
|
208
|
+
|
209
|
+
10.times do |n|
|
210
|
+
collection.first("_id" => id) do |res|
|
211
|
+
received += 1
|
212
|
+
EM::Spec::Mongo.close if received == 10
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe EMMongo::Connection do
|
4
|
+
include EM::SpecHelper
|
5
|
+
|
6
|
+
it 'should connect' do
|
7
|
+
em do
|
8
|
+
connection = EMMongo::Connection.new
|
9
|
+
EM.next_tick do
|
10
|
+
connection.should be_connected
|
11
|
+
done
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should close' do
|
17
|
+
em do
|
18
|
+
connection = EMMongo::Connection.new
|
19
|
+
|
20
|
+
EM.add_timer(1) do
|
21
|
+
connection.should be_connected
|
22
|
+
connection.close
|
23
|
+
end
|
24
|
+
|
25
|
+
EM.add_timer(2) do
|
26
|
+
EM.next_tick do
|
27
|
+
connection.should_not be_connected
|
28
|
+
done
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should instantiate a Database' do
|
35
|
+
EM::Spec::Mongo.connection do |connection|
|
36
|
+
db1 = connection.db
|
37
|
+
db1.should be_kind_of(EM::Mongo::Database)
|
38
|
+
|
39
|
+
db2 = connection.db('db2')
|
40
|
+
db2.should be_kind_of(EM::Mongo::Database)
|
41
|
+
db2.should_not == db1
|
42
|
+
|
43
|
+
EM::Spec::Mongo.close
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 11
|
9
|
+
version: 0.2.11
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- bcg
|
@@ -21,6 +21,7 @@ dependencies:
|
|
21
21
|
name: eventmachine
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
24
25
|
requirements:
|
25
26
|
- - ">="
|
26
27
|
- !ruby/object:Gem::Version
|
@@ -35,6 +36,7 @@ dependencies:
|
|
35
36
|
name: bson
|
36
37
|
prerelease: false
|
37
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
38
40
|
requirements:
|
39
41
|
- - ">="
|
40
42
|
- !ruby/object:Gem::Version
|
@@ -57,6 +59,11 @@ files:
|
|
57
59
|
- lib/em-mongo/collection.rb
|
58
60
|
- lib/em-mongo/connection.rb
|
59
61
|
- lib/em-mongo.rb
|
62
|
+
- spec/collection_spec.rb
|
63
|
+
- spec/connection_spec.rb
|
64
|
+
- spec/integration/collection_spec.rb
|
65
|
+
- spec/integration/connection_spec.rb
|
66
|
+
- spec/spec_helper.rb
|
60
67
|
has_rdoc: true
|
61
68
|
homepage:
|
62
69
|
licenses: []
|
@@ -67,6 +74,7 @@ rdoc_options:
|
|
67
74
|
require_paths:
|
68
75
|
- lib
|
69
76
|
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
70
78
|
requirements:
|
71
79
|
- - ">="
|
72
80
|
- !ruby/object:Gem::Version
|
@@ -74,6 +82,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
74
82
|
- 0
|
75
83
|
version: "0"
|
76
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
77
86
|
requirements:
|
78
87
|
- - ">="
|
79
88
|
- !ruby/object:Gem::Version
|
@@ -83,11 +92,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
92
|
requirements: []
|
84
93
|
|
85
94
|
rubyforge_project:
|
86
|
-
rubygems_version: 1.3.
|
95
|
+
rubygems_version: 1.3.7
|
87
96
|
signing_key:
|
88
97
|
specification_version: 3
|
89
98
|
summary: EventMachine driver for MongoDB.
|
90
99
|
test_files:
|
91
100
|
- spec/collection_spec.rb
|
92
101
|
- spec/connection_spec.rb
|
102
|
+
- spec/integration/collection_spec.rb
|
103
|
+
- spec/integration/connection_spec.rb
|
93
104
|
- spec/spec_helper.rb
|