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.
@@ -5,12 +5,14 @@ module EM::Mongo
5
5
  DEFAULT_NS = "ns"
6
6
  DEFAULT_QUERY_DOCS = 101
7
7
 
8
- module EMConnection
9
- class Error < Exception;
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
- @on_unbind.call
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
- - 10
9
- version: 0.2.10
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.6
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