em-mongo 0.2.10 → 0.2.11
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/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
|