em-mongo 0.1.1 → 0.2.4
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/collection.rb +2 -8
- data/lib/em-mongo/connection.rb +110 -44
- data/lib/em-mongo.rb +14 -15
- data/spec/collection_spec.rb +64 -38
- data/spec/connection_spec.rb +4 -4
- data/spec/spec_helper.rb +5 -6
- metadata +22 -10
- data/lib/em-mongo/buffer.rb +0 -414
- data/spec/buffer_spec.rb +0 -123
data/lib/em-mongo/collection.rb
CHANGED
@@ -15,7 +15,7 @@ module EM::Mongo
|
|
15
15
|
skip = opts.delete(:skip) || 0
|
16
16
|
limit = opts.delete(:limit) || 0
|
17
17
|
|
18
|
-
@connection.find(@name, skip, limit, selector, &blk)
|
18
|
+
@connection.find(@name, skip, limit, selector, nil, &blk)
|
19
19
|
end
|
20
20
|
|
21
21
|
def first(selector={}, opts={}, &blk)
|
@@ -26,7 +26,7 @@ module EM::Mongo
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def insert(obj)
|
29
|
-
obj[
|
29
|
+
obj['_id'] ||= EM::Mongo::Util.unique_id
|
30
30
|
@connection.insert(@name, obj)
|
31
31
|
obj
|
32
32
|
end
|
@@ -36,11 +36,5 @@ module EM::Mongo
|
|
36
36
|
true
|
37
37
|
end
|
38
38
|
|
39
|
-
#def method_missing meth
|
40
|
-
# puts meth
|
41
|
-
# raise ArgumentError, 'collection cannot take block' if block_given?
|
42
|
-
# (@subns ||= {})[meth] ||= self.class.new("#{@ns}.#{meth}", @client)
|
43
|
-
#end
|
44
|
-
|
45
39
|
end
|
46
40
|
end
|
data/lib/em-mongo/connection.rb
CHANGED
@@ -19,6 +19,9 @@ module EM::Mongo
|
|
19
19
|
OP_INSERT = 2002
|
20
20
|
OP_QUERY = 2004
|
21
21
|
OP_DELETE = 2006
|
22
|
+
|
23
|
+
STANDARD_HEADER_SIZE = 16
|
24
|
+
RESPONSE_HEADER_SIZE = 20
|
22
25
|
|
23
26
|
attr_reader :connection
|
24
27
|
|
@@ -30,56 +33,73 @@ module EM::Mongo
|
|
30
33
|
@is_connected
|
31
34
|
end
|
32
35
|
|
33
|
-
# RMongo interface
|
36
|
+
# XXX RMongo interface
|
34
37
|
def collection(db = DEFAULT_DB, ns = DEFAULT_NS)
|
35
38
|
raise "Not connected" if not connected?
|
36
39
|
EM::Mongo::Collection.new(db, ns, self)
|
37
40
|
end
|
38
41
|
|
42
|
+
def new_request_id
|
43
|
+
@request_id += 1
|
44
|
+
end
|
45
|
+
|
39
46
|
# MongoDB Commands
|
40
47
|
|
41
|
-
def
|
42
|
-
|
48
|
+
def message_headers(operation, request_id, message)
|
49
|
+
headers = BSON::ByteBuffer.new
|
50
|
+
headers.put_int(16 + message.size)
|
51
|
+
headers.put_int(request_id)
|
52
|
+
headers.put_int(0)
|
53
|
+
headers.put_int(operation)
|
54
|
+
headers
|
55
|
+
end
|
43
56
|
|
44
|
-
|
45
|
-
buf = Buffer.new
|
46
|
-
buf.write :int, request_id,
|
47
|
-
:int, response = 0,
|
48
|
-
:int, operation = id
|
57
|
+
def send_command(buffer, request_id, &blk)
|
49
58
|
|
50
|
-
|
51
|
-
send_data
|
52
|
-
|
53
|
-
}
|
59
|
+
callback do
|
60
|
+
send_data buffer
|
61
|
+
end
|
54
62
|
|
55
63
|
@responses[request_id] = blk if blk
|
56
64
|
request_id
|
57
65
|
end
|
58
66
|
|
67
|
+
|
59
68
|
def insert(collection_name, documents)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
69
|
+
message = BSON::ByteBuffer.new([0, 0, 0, 0])
|
70
|
+
BSON::BSON_RUBY.serialize_cstr(message, collection_name)
|
71
|
+
|
72
|
+
documents = [documents] if not documents.is_a?(Array)
|
73
|
+
documents.each { |doc| message.put_array(BSON::BSON_CODER.serialize(doc, true, true).to_a) }
|
74
|
+
|
75
|
+
req_id = new_request_id
|
76
|
+
message.prepend!(message_headers(OP_INSERT, req_id, message))
|
77
|
+
send_command(message.to_s, req_id)
|
64
78
|
end
|
65
79
|
|
66
80
|
def delete(collection_name, selector)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
81
|
+
message = BSON::ByteBuffer.new([0, 0, 0, 0])
|
82
|
+
BSON::BSON_RUBY.serialize_cstr(message, collection_name)
|
83
|
+
message.put_int(0)
|
84
|
+
message.put_array(BSON::BSON_CODER.serialize(selector, false, true).to_a)
|
85
|
+
req_id = new_request_id
|
86
|
+
message.prepend!(message_headers(OP_DELETE, req_id, message))
|
87
|
+
send_command(message.to_s, req_id)
|
71
88
|
end
|
72
89
|
|
73
|
-
def find(collection_name, skip, limit, query, &blk)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
90
|
+
def find(collection_name, skip, limit, query, fields, &blk)
|
91
|
+
message = BSON::ByteBuffer.new
|
92
|
+
message.put_int(RESERVED) # query options
|
93
|
+
BSON::BSON_RUBY.serialize_cstr(message, collection_name)
|
94
|
+
message.put_int(skip)
|
95
|
+
message.put_int(limit)
|
96
|
+
message.put_array(BSON::BSON_CODER.serialize(query, false).to_a)
|
97
|
+
message.put_array(BSON::BSON_CODER.serialize(fields, false).to_a) if fields
|
98
|
+
req_id = new_request_id
|
99
|
+
message.prepend!(message_headers(OP_QUERY, req_id, message))
|
100
|
+
send_command(message.to_s, req_id, &blk)
|
80
101
|
end
|
81
102
|
|
82
|
-
|
83
103
|
# EM hooks
|
84
104
|
def initialize(options={})
|
85
105
|
@request_id = 0
|
@@ -88,7 +108,7 @@ module EM::Mongo
|
|
88
108
|
@host = options[:host] || DEFAULT_IP
|
89
109
|
@port = options[:port] || DEFAULT_PORT
|
90
110
|
|
91
|
-
@on_close = proc{
|
111
|
+
@on_close = proc {
|
92
112
|
raise Error, "could not connect to server #{@host}:#{@port}"
|
93
113
|
}
|
94
114
|
timeout options[:timeout] if options[:timeout]
|
@@ -101,36 +121,82 @@ module EM::Mongo
|
|
101
121
|
end
|
102
122
|
|
103
123
|
def connection_completed
|
104
|
-
|
105
|
-
@buf = Buffer.new
|
124
|
+
@buffer = BSON::ByteBuffer.new
|
106
125
|
@is_connected = true
|
107
126
|
@on_close = proc{
|
108
127
|
}
|
109
128
|
succeed
|
110
129
|
end
|
111
130
|
|
112
|
-
def receive_data data
|
113
|
-
log "receive_data: #{data.size}"#, data
|
114
131
|
|
115
|
-
|
132
|
+
def message_received?
|
133
|
+
size = @buffer.get_int
|
134
|
+
@buffer.rewind
|
135
|
+
@buffer.size >= size-4 ? true : false
|
136
|
+
end
|
137
|
+
|
138
|
+
def receive_data(data)
|
139
|
+
|
140
|
+
@buffer.put_array(data.unpack('C*'))
|
141
|
+
@buffer.rewind
|
142
|
+
return if @buffer.size < STANDARD_HEADER_SIZE
|
143
|
+
|
144
|
+
if message_received?
|
145
|
+
|
146
|
+
# Header
|
147
|
+
header = BSON::ByteBuffer.new
|
148
|
+
header.put_array(@buffer.get(STANDARD_HEADER_SIZE))
|
149
|
+
unless header.size == STANDARD_HEADER_SIZE
|
150
|
+
raise "Short read for DB header: " +
|
151
|
+
"expected #{STANDARD_HEADER_SIZE} bytes, saw #{header.size}"
|
152
|
+
end
|
153
|
+
header.rewind
|
154
|
+
size = header.get_int
|
155
|
+
request_id = header.get_int
|
156
|
+
response_to = header.get_int
|
157
|
+
op = header.get_int
|
158
|
+
|
159
|
+
# Response Header
|
160
|
+
response_header = BSON::ByteBuffer.new
|
161
|
+
response_header.put_array(@buffer.get(RESPONSE_HEADER_SIZE))
|
162
|
+
if response_header.length != RESPONSE_HEADER_SIZE
|
163
|
+
raise "Short read for DB response header; " +
|
164
|
+
"expected #{RESPONSE_HEADER_SIZE} bytes, saw #{response_header.length}"
|
165
|
+
end
|
166
|
+
|
167
|
+
response_header.rewind
|
168
|
+
result_flags = response_header.get_int
|
169
|
+
cursor_id = response_header.get_long
|
170
|
+
starting_from = response_header.get_int
|
171
|
+
number_remaining = response_header.get_int
|
116
172
|
|
117
|
-
|
118
|
-
|
173
|
+
# Documents
|
174
|
+
docs = (1..number_remaining).map do |n|
|
119
175
|
|
120
|
-
|
176
|
+
buf = BSON::ByteBuffer.new
|
177
|
+
buf.put_int(@buffer.get_int)
|
121
178
|
|
122
|
-
|
123
|
-
|
179
|
+
buf.rewind
|
180
|
+
size = buf.get_int
|
124
181
|
|
125
|
-
|
126
|
-
|
182
|
+
if size > @buffer.size
|
183
|
+
@buffer = ''
|
184
|
+
raise "Buffer Overflow: Failed to parse buffer"
|
185
|
+
end
|
186
|
+
buf.put_array(@buffer.get(size-4), 4)
|
187
|
+
|
188
|
+
buf.rewind
|
189
|
+
BSON::BSON_CODER.deserialize(buf)
|
127
190
|
end
|
128
191
|
|
129
|
-
|
130
|
-
|
192
|
+
@buffer.clear
|
193
|
+
|
194
|
+
if cb = @responses.delete(response_to)
|
195
|
+
cb.call(docs)
|
131
196
|
end
|
132
|
-
close_connection if @close_pending and @responses.size == 0
|
197
|
+
close_connection if @close_pending and @responses.size == 0
|
133
198
|
end
|
199
|
+
|
134
200
|
end
|
135
201
|
|
136
202
|
def send_data data
|
data/lib/em-mongo.rb
CHANGED
@@ -1,30 +1,29 @@
|
|
1
|
-
require 'eventmachine'
|
2
|
-
require 'pp'
|
3
1
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require 'uuid'
|
8
|
-
end
|
2
|
+
require "eventmachine"
|
3
|
+
begin; require "bson_ext"; rescue LoadError; require "bson"; end
|
4
|
+
|
9
5
|
|
10
6
|
module EM::Mongo
|
11
|
-
|
7
|
+
|
8
|
+
module Version
|
9
|
+
MAJOR = 0
|
10
|
+
MINOR = 2
|
11
|
+
TINY = 4
|
12
|
+
STRING = [MAJOR, MINOR, TINY].join('.')
|
13
|
+
end
|
14
|
+
|
15
|
+
NAME = 'em-mongo'
|
12
16
|
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
13
|
-
PATH
|
17
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
14
18
|
|
15
19
|
class Util
|
16
20
|
def self.unique_id
|
17
|
-
|
18
|
-
SecureRandom.hex(12)
|
19
|
-
else
|
20
|
-
UUID.new.generate(:compact).gsub(/^(.{20})(.{8})(.{4})$/){ $1+$3 }
|
21
|
-
end
|
21
|
+
BSON::ObjectID.new.to_s
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
require File.join(EM::Mongo::LIBPATH, "em-mongo/connection")
|
27
|
-
require File.join(EM::Mongo::LIBPATH, "em-mongo/buffer")
|
28
27
|
require File.join(EM::Mongo::LIBPATH, "em-mongo/collection")
|
29
28
|
|
30
29
|
EMMongo = EM::Mongo
|
data/spec/collection_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__)
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
2
|
|
3
3
|
describe EMMongo::Collection do
|
4
4
|
include EM::SpecHelper
|
@@ -22,20 +22,31 @@ describe EMMongo::Collection do
|
|
22
22
|
|
23
23
|
it 'should insert an object' do
|
24
24
|
EM::Spec::Mongo.collection do |collection|
|
25
|
-
obj = collection.insert(
|
26
|
-
obj.keys.should include
|
27
|
-
obj[
|
28
|
-
obj[
|
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
29
|
EM::Spec::Mongo.close
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
it 'should find an object' do
|
33
|
+
it 'should find an object by attribute' do
|
34
34
|
EM::Spec::Mongo.collection do |collection|
|
35
|
-
collection.insert(
|
36
|
-
r = collection.find({
|
35
|
+
collection.insert("hello" => 'world')
|
36
|
+
r = collection.find({"hello" => "world"},{}) do |res|
|
37
37
|
res.size.should >= 1
|
38
|
-
res[0][
|
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"
|
39
50
|
EM::Spec::Mongo.close
|
40
51
|
end
|
41
52
|
end
|
@@ -43,8 +54,8 @@ describe EMMongo::Collection do
|
|
43
54
|
|
44
55
|
it 'should find all objects' do
|
45
56
|
EM::Spec::Mongo.collection do |collection|
|
46
|
-
collection.insert(
|
47
|
-
collection.insert(
|
57
|
+
collection.insert('one' => 'one')
|
58
|
+
o = collection.insert('two' => 'two')
|
48
59
|
collection.find do |res|
|
49
60
|
res.size.should >= 2
|
50
61
|
EM::Spec::Mongo.close
|
@@ -54,9 +65,9 @@ describe EMMongo::Collection do
|
|
54
65
|
|
55
66
|
it 'should remove an object' do
|
56
67
|
EM::Spec::Mongo.collection do |collection|
|
57
|
-
obj = collection.insert(
|
58
|
-
collection.remove(obj)
|
59
|
-
collection.find({
|
68
|
+
obj = collection.insert('hello' => 'world')
|
69
|
+
collection.remove('_id' => obj['_id'])
|
70
|
+
collection.find({'hello' => "world"}) do |res|
|
60
71
|
res.size.should == 0
|
61
72
|
EM::Spec::Mongo.close
|
62
73
|
end
|
@@ -65,8 +76,8 @@ describe EMMongo::Collection do
|
|
65
76
|
|
66
77
|
it 'should remove all objects' do
|
67
78
|
EM::Spec::Mongo.collection do |collection|
|
68
|
-
collection.insert(
|
69
|
-
collection.insert(
|
79
|
+
collection.insert('one' => 'one')
|
80
|
+
collection.insert('two' => 'two')
|
70
81
|
collection.remove
|
71
82
|
collection.find do |res|
|
72
83
|
res.size.should == 0
|
@@ -75,37 +86,52 @@ describe EMMongo::Collection do
|
|
75
86
|
end
|
76
87
|
end
|
77
88
|
|
89
|
+
it 'should insert a Time' do
|
90
|
+
EM::Spec::Mongo.collection do |collection|
|
91
|
+
t = Time.now.utc.freeze
|
92
|
+
collection.insert('date' => t)
|
93
|
+
collection.find do |res|
|
94
|
+
res[0]['date'].to_s.should == t.to_s
|
95
|
+
EM::Spec::Mongo.close
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
78
100
|
it 'should insert a complex object' do
|
79
101
|
EM::Spec::Mongo.collection do |collection|
|
80
102
|
obj = {
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
103
|
+
'array' => [1,2,3],
|
104
|
+
'float' => 123.456,
|
105
|
+
'hash' => {'boolean' => true},
|
106
|
+
'nil' => nil,
|
107
|
+
'symbol' => :name,
|
108
|
+
'string' => 'hello world',
|
109
|
+
'time' => Time.now.to_f,
|
110
|
+
'regex' => /abc$/ix
|
89
111
|
}
|
90
|
-
|
91
|
-
collection.find(
|
92
|
-
ret.should ==
|
112
|
+
retobj = collection.insert(obj)
|
113
|
+
collection.find({'_id' => obj['_id']}) do |ret|
|
114
|
+
ret.size.should == 1
|
115
|
+
ret[0].each_key do |key|
|
116
|
+
ret[0][key].should == obj[key]
|
117
|
+
end
|
93
118
|
EM::Spec::Mongo.close
|
94
119
|
end
|
120
|
+
|
95
121
|
end
|
96
122
|
end
|
97
123
|
|
98
124
|
it 'should find an object using nested properties' do
|
99
125
|
EM::Spec::Mongo.collection do |collection|
|
100
126
|
collection.insert({
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
127
|
+
'name' => 'Google',
|
128
|
+
'address' => {
|
129
|
+
'city' => 'Mountain View',
|
130
|
+
'state' => 'California'}
|
105
131
|
})
|
106
132
|
|
107
133
|
collection.first('address.city' => 'Mountain View') do |res|
|
108
|
-
res[
|
134
|
+
res['name'].should == 'Google'
|
109
135
|
EM::Spec::Mongo.close
|
110
136
|
end
|
111
137
|
end
|
@@ -114,12 +140,12 @@ describe EMMongo::Collection do
|
|
114
140
|
it 'should find objects with specific values' do
|
115
141
|
EM::Spec::Mongo.collection do |collection|
|
116
142
|
@numbers.each do |num, word|
|
117
|
-
collection.insert(
|
143
|
+
collection.insert({'num' => num, 'word' => word})
|
118
144
|
end
|
119
145
|
|
120
|
-
collection.find({
|
146
|
+
collection.find({'num' => {'$in' => [1,3,5]}}) do |res|
|
121
147
|
res.size.should == 3
|
122
|
-
res.map{|r| r[
|
148
|
+
res.map{|r| r['num'] }.sort.should == [1,3,5]
|
123
149
|
EM::Spec::Mongo.close
|
124
150
|
end
|
125
151
|
end
|
@@ -128,12 +154,12 @@ describe EMMongo::Collection do
|
|
128
154
|
it 'should find objects greater than something' do
|
129
155
|
EM::Spec::Mongo.collection do |collection|
|
130
156
|
@numbers.each do |num, word|
|
131
|
-
collection.insert(
|
157
|
+
collection.insert('num' => num, 'word' => word)
|
132
158
|
end
|
133
159
|
|
134
|
-
collection.find({
|
160
|
+
collection.find({'num' => {'$gt' => 3}}) do |res|
|
135
161
|
res.size.should == 6
|
136
|
-
res.map{|r| r[
|
162
|
+
res.map{|r| r['num'] }.sort.should == [4,5,6,7,8,9]
|
137
163
|
EM::Spec::Mongo.close
|
138
164
|
end
|
139
165
|
end
|
data/spec/connection_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__)
|
1
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
2
2
|
|
3
3
|
describe EMMongo::Connection do
|
4
4
|
include EM::SpecHelper
|
@@ -32,7 +32,7 @@ describe EMMongo::Connection do
|
|
32
32
|
# Support the old RMongo interface for now
|
33
33
|
it 'should instantiate a Collection' do
|
34
34
|
EM::Spec::Mongo.connection do |connection|
|
35
|
-
connection.collection.is_a?(Collection).should == true
|
35
|
+
connection.collection.is_a?(EM::Mongo::Collection).should == true
|
36
36
|
EM::Spec::Mongo.close
|
37
37
|
end
|
38
38
|
end
|
@@ -40,9 +40,9 @@ describe EMMongo::Connection do
|
|
40
40
|
it 'should instantiate a Databse' do
|
41
41
|
EM::Spec::Mongo.connection do |connection|
|
42
42
|
db1 = connection.db
|
43
|
-
db1.is_a?(Database).should == true
|
43
|
+
db1.is_a?(EM::Mongo::Database).should == true
|
44
44
|
db2 = connection.db('db2')
|
45
|
-
db2.is_a?(Database).should == true
|
45
|
+
db2.is_a?(EM::Mongo::Database).should == true
|
46
46
|
db2.should_not == db1
|
47
47
|
EM::Spec::Mongo.close
|
48
48
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
require
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
#require File.dirname(__FILE__)+'/../../em-spec/lib/em/spec/rspec'
|
7
|
-
#EM.spec_backend = EventMachine::Spec::Rspec
|
4
|
+
Bundler.setup(:test)
|
5
|
+
|
6
|
+
require File.expand_path('../lib/em-mongo', File.dirname(__FILE__))
|
8
7
|
|
9
8
|
require "em-spec/rspec"
|
10
9
|
|
metadata
CHANGED
@@ -4,17 +4,17 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 4
|
9
|
+
version: 0.2.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
|
-
-
|
12
|
+
- bcg
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-04-19 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -31,8 +31,22 @@ dependencies:
|
|
31
31
|
version: 0.12.10
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
|
-
|
35
|
-
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: bson
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
- 20
|
44
|
+
- 1
|
45
|
+
version: 0.20.1
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
description: EventMachine drive for MongoDB.
|
49
|
+
email: brenden.grace@gmail.com
|
36
50
|
executables: []
|
37
51
|
|
38
52
|
extensions: []
|
@@ -40,7 +54,6 @@ extensions: []
|
|
40
54
|
extra_rdoc_files: []
|
41
55
|
|
42
56
|
files:
|
43
|
-
- lib/em-mongo/buffer.rb
|
44
57
|
- lib/em-mongo/collection.rb
|
45
58
|
- lib/em-mongo/connection.rb
|
46
59
|
- lib/em-mongo.rb
|
@@ -73,9 +86,8 @@ rubyforge_project:
|
|
73
86
|
rubygems_version: 1.3.6
|
74
87
|
signing_key:
|
75
88
|
specification_version: 3
|
76
|
-
summary:
|
89
|
+
summary: EventMachine drive for MongoDB.
|
77
90
|
test_files:
|
78
|
-
- spec/buffer_spec.rb
|
79
91
|
- spec/collection_spec.rb
|
80
92
|
- spec/connection_spec.rb
|
81
93
|
- spec/spec_helper.rb
|
data/lib/em-mongo/buffer.rb
DELETED
@@ -1,414 +0,0 @@
|
|
1
|
-
if [].map.respond_to? :with_index
|
2
|
-
class Array
|
3
|
-
def enum_with_index
|
4
|
-
each.with_index
|
5
|
-
end
|
6
|
-
end
|
7
|
-
else
|
8
|
-
require 'enumerator'
|
9
|
-
end
|
10
|
-
|
11
|
-
module EM::Mongo
|
12
|
-
class Buffer
|
13
|
-
class Overflow < Exception; end
|
14
|
-
class InvalidType < Exception; end
|
15
|
-
|
16
|
-
def initialize data = ''
|
17
|
-
@data = data
|
18
|
-
@pos = 0
|
19
|
-
end
|
20
|
-
attr_reader :pos
|
21
|
-
|
22
|
-
def data
|
23
|
-
@data.clone
|
24
|
-
end
|
25
|
-
alias :contents :data
|
26
|
-
alias :to_s :data
|
27
|
-
|
28
|
-
def << data
|
29
|
-
@data << data.to_s
|
30
|
-
self
|
31
|
-
end
|
32
|
-
|
33
|
-
def length
|
34
|
-
@data.length
|
35
|
-
end
|
36
|
-
alias :size :length
|
37
|
-
|
38
|
-
def empty?
|
39
|
-
pos == length
|
40
|
-
end
|
41
|
-
|
42
|
-
def rewind
|
43
|
-
@pos = 0
|
44
|
-
end
|
45
|
-
|
46
|
-
def read *types
|
47
|
-
values = types.map do |type|
|
48
|
-
case type
|
49
|
-
when :byte
|
50
|
-
_read(1, 'C')
|
51
|
-
when :short
|
52
|
-
_read(2, 'n')
|
53
|
-
when :int
|
54
|
-
_read(4, 'I')
|
55
|
-
when :double
|
56
|
-
_read(8, 'd')
|
57
|
-
when :long
|
58
|
-
_read(4, 'N')
|
59
|
-
when :longlong
|
60
|
-
upper, lower = _read(8, 'NN')
|
61
|
-
upper << 32 | lower
|
62
|
-
when :cstring
|
63
|
-
str = @data.unpack("@#{@pos}Z*").first
|
64
|
-
@data.slice!(@pos, str.size+1)
|
65
|
-
str
|
66
|
-
when :oid
|
67
|
-
# _read(12, 'i*').enum_with_index.inject([]) do |a, (num, i)|
|
68
|
-
# a[ i == 0 ? 1 : i == 1 ? 0 : i ] = num # swap first two
|
69
|
-
# a
|
70
|
-
# end.map do |num|
|
71
|
-
# num.to_s(16).rjust(8,'0')
|
72
|
-
# end.join('')
|
73
|
-
|
74
|
-
_read(12, 'I*').map do |num|
|
75
|
-
num.to_s(16).rjust(8,'0')
|
76
|
-
end.join('')
|
77
|
-
when :bson
|
78
|
-
bson = {}
|
79
|
-
data = Buffer.new _read(read(:int)-4)
|
80
|
-
|
81
|
-
until data.empty?
|
82
|
-
type = data.read(:byte)
|
83
|
-
next if type == 0 # end of object
|
84
|
-
|
85
|
-
key = data.read(:cstring).intern
|
86
|
-
|
87
|
-
bson[key] = case type
|
88
|
-
when 1 # number
|
89
|
-
data.read(:double)
|
90
|
-
when 2 # string
|
91
|
-
data.read(:int)
|
92
|
-
data.read(:cstring)
|
93
|
-
when 3 # object
|
94
|
-
data.read(:bson)
|
95
|
-
when 4 # array
|
96
|
-
data.read(:bson).inject([]){ |a, (k,v)| a[k.to_s.to_i] = v; a }
|
97
|
-
when 5 # binary
|
98
|
-
data._read data.read(:int)
|
99
|
-
when 6 # undefined
|
100
|
-
when 7 # oid
|
101
|
-
data.read(:oid)
|
102
|
-
when 8 # bool
|
103
|
-
data.read(:byte) == 1 ? true : false
|
104
|
-
when 9 # time
|
105
|
-
Time.at data.read(:longlong)/1000.0
|
106
|
-
when 10 # nil
|
107
|
-
nil
|
108
|
-
when 11 # regex
|
109
|
-
source = data.read(:cstring)
|
110
|
-
options = data.read(:cstring).split('')
|
111
|
-
|
112
|
-
options = { 'i' => 1, 'm' => 2, 'x' => 4 }.inject(0) do |s, (o, n)|
|
113
|
-
s |= n if options.include?(o)
|
114
|
-
s
|
115
|
-
end
|
116
|
-
|
117
|
-
Regexp.new(source, options)
|
118
|
-
when 12 # ref
|
119
|
-
ref = {}
|
120
|
-
ref[:_ns] = data.read(:cstring)
|
121
|
-
ref[:_id] = data.read(:oid)
|
122
|
-
ref
|
123
|
-
when 13 # code
|
124
|
-
data.read(:int)
|
125
|
-
data.read(:cstring)
|
126
|
-
when 14 # symbol
|
127
|
-
data.read(:int)
|
128
|
-
data.read(:cstring).intern
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
bson
|
133
|
-
else
|
134
|
-
raise InvalidType, "Cannot read data of type #{type}"
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
types.size == 1 ? values.first : values
|
139
|
-
end
|
140
|
-
|
141
|
-
def write *args
|
142
|
-
args.each_slice(2) do |type, data|
|
143
|
-
case type
|
144
|
-
when :byte
|
145
|
-
_write(data, 'C')
|
146
|
-
when :short
|
147
|
-
_write(data, 'n')
|
148
|
-
when :int
|
149
|
-
_write(data, 'I')
|
150
|
-
when :double
|
151
|
-
_write(data, 'd')
|
152
|
-
when :long
|
153
|
-
_write(data, 'N')
|
154
|
-
when :longlong
|
155
|
-
lower = data & 0xffffffff
|
156
|
-
upper = (data & ~0xffffffff) >> 32
|
157
|
-
_write([upper, lower], 'NN')
|
158
|
-
when :cstring
|
159
|
-
_write(data.to_s + "\0")
|
160
|
-
when :oid
|
161
|
-
# data.scan(/.{8}/).enum_with_index.inject([]) do |a, (num, i)|
|
162
|
-
# a[ i == 0 ? 1 : i == 1 ? 0 : i ] = num # swap first two
|
163
|
-
# a
|
164
|
-
# end.each do |num|
|
165
|
-
# write(:int, num.to_i(16))
|
166
|
-
# end
|
167
|
-
data.scan(/.{8}/).each do |num|
|
168
|
-
write(:int, num.to_i(16))
|
169
|
-
end
|
170
|
-
when :bson
|
171
|
-
buf = Buffer.new
|
172
|
-
data.each do |key,value|
|
173
|
-
case value
|
174
|
-
when Numeric
|
175
|
-
id = 1
|
176
|
-
type = :double
|
177
|
-
when String
|
178
|
-
if key == :_id
|
179
|
-
id = 7
|
180
|
-
type = :oid
|
181
|
-
else
|
182
|
-
id = 2
|
183
|
-
type = proc{ |out|
|
184
|
-
out.write(:int, value.length+1)
|
185
|
-
out.write(:cstring, value)
|
186
|
-
}
|
187
|
-
end
|
188
|
-
when Hash
|
189
|
-
if data.keys.map{|k| k.to_s}.sort == %w[ _id _ns ]
|
190
|
-
id = 12 # ref
|
191
|
-
type = proc{ |out|
|
192
|
-
out.write(:cstring, data[:_ns])
|
193
|
-
out.write(:oid, data[:_id])
|
194
|
-
}
|
195
|
-
else
|
196
|
-
id = 3
|
197
|
-
type = :bson
|
198
|
-
end
|
199
|
-
when Array
|
200
|
-
id = 4
|
201
|
-
type = :bson
|
202
|
-
value = value.enum_with_index.inject({}){ |h, (v, i)| h.update i => v }
|
203
|
-
when TrueClass, FalseClass
|
204
|
-
id = 8
|
205
|
-
type = :byte
|
206
|
-
value = value ? 1 : 0
|
207
|
-
when Time
|
208
|
-
id = 9
|
209
|
-
type = :longlong
|
210
|
-
value = value.to_i * 1000 + (value.tv_usec/1000)
|
211
|
-
when NilClass
|
212
|
-
id = 10
|
213
|
-
type = nil
|
214
|
-
when Regexp
|
215
|
-
id = 11
|
216
|
-
type = proc{ |out|
|
217
|
-
out.write(:cstring, value.source)
|
218
|
-
out.write(:cstring, { 'i' => 1, 'm' => 2, 'x' => 4 }.inject('') do |s, (o, n)|
|
219
|
-
s += o if value.options & n > 0
|
220
|
-
s
|
221
|
-
end)
|
222
|
-
}
|
223
|
-
when Symbol
|
224
|
-
id = 14
|
225
|
-
type = proc{ |out|
|
226
|
-
out.write(:int, value.to_s.length+1)
|
227
|
-
out.write(:cstring, value.to_s)
|
228
|
-
}
|
229
|
-
end
|
230
|
-
|
231
|
-
buf.write(:byte, id)
|
232
|
-
buf.write(:cstring, key)
|
233
|
-
|
234
|
-
if type.respond_to? :call
|
235
|
-
type.call(buf)
|
236
|
-
elsif type
|
237
|
-
buf.write(type, value)
|
238
|
-
end
|
239
|
-
end
|
240
|
-
buf.write(:byte, 0) # eoo
|
241
|
-
|
242
|
-
write(:int, buf.size+4)
|
243
|
-
_write(buf.to_s)
|
244
|
-
else
|
245
|
-
raise InvalidType, "Cannot write data of type #{type}"
|
246
|
-
end
|
247
|
-
end
|
248
|
-
self
|
249
|
-
end
|
250
|
-
|
251
|
-
def _peek(pos, size, pack)
|
252
|
-
data = @data[pos,size]
|
253
|
-
data = data.unpack(pack)
|
254
|
-
data = data.pop if data.size == 1
|
255
|
-
data
|
256
|
-
end
|
257
|
-
|
258
|
-
def _read size, pack = nil
|
259
|
-
if @pos + size > length
|
260
|
-
raise Overflow
|
261
|
-
else
|
262
|
-
data = @data[@pos,size]
|
263
|
-
@data[@pos,size] = ''
|
264
|
-
if pack
|
265
|
-
data = data.unpack(pack)
|
266
|
-
data = data.pop if data.size == 1
|
267
|
-
end
|
268
|
-
data
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
def _write data, pack = nil
|
273
|
-
data = [*data].pack(pack) if pack
|
274
|
-
@data[@pos,0] = data
|
275
|
-
@pos += data.length
|
276
|
-
end
|
277
|
-
|
278
|
-
def extract
|
279
|
-
begin
|
280
|
-
cur_data, cur_pos = @data.clone, @pos
|
281
|
-
yield self
|
282
|
-
rescue Overflow
|
283
|
-
@data, @pos = cur_data, cur_pos
|
284
|
-
nil
|
285
|
-
end
|
286
|
-
end
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
if $0 =~ /bacon/ or $0 == __FILE__
|
291
|
-
require 'bacon'
|
292
|
-
Bacon.summary_on_exit
|
293
|
-
include Mongo
|
294
|
-
|
295
|
-
describe Buffer do
|
296
|
-
before do
|
297
|
-
@buf = Buffer.new
|
298
|
-
end
|
299
|
-
|
300
|
-
should 'have contents' do
|
301
|
-
@buf.contents.should == ''
|
302
|
-
end
|
303
|
-
|
304
|
-
should 'initialize with data' do
|
305
|
-
@buf = Buffer.new('abc')
|
306
|
-
@buf.contents.should == 'abc'
|
307
|
-
end
|
308
|
-
|
309
|
-
should 'append raw data' do
|
310
|
-
@buf << 'abc'
|
311
|
-
@buf << 'def'
|
312
|
-
@buf.contents.should == 'abcdef'
|
313
|
-
end
|
314
|
-
|
315
|
-
should 'append other buffers' do
|
316
|
-
@buf << Buffer.new('abc')
|
317
|
-
@buf.data.should == 'abc'
|
318
|
-
end
|
319
|
-
|
320
|
-
should 'have a position' do
|
321
|
-
@buf.pos.should == 0
|
322
|
-
end
|
323
|
-
|
324
|
-
should 'have a length' do
|
325
|
-
@buf.length.should == 0
|
326
|
-
@buf << 'abc'
|
327
|
-
@buf.length.should == 3
|
328
|
-
end
|
329
|
-
|
330
|
-
should 'know the end' do
|
331
|
-
@buf.empty?.should == true
|
332
|
-
end
|
333
|
-
|
334
|
-
should 'read and write data' do
|
335
|
-
@buf._write('abc')
|
336
|
-
@buf.rewind
|
337
|
-
@buf._read(2).should == 'ab'
|
338
|
-
@buf._read(1).should == 'c'
|
339
|
-
end
|
340
|
-
|
341
|
-
should 'raise on overflow' do
|
342
|
-
lambda{ @buf._read(1) }.should.raise Buffer::Overflow
|
343
|
-
end
|
344
|
-
|
345
|
-
should 'raise on invalid types' do
|
346
|
-
lambda{ @buf.read(:junk) }.should.raise Buffer::InvalidType
|
347
|
-
lambda{ @buf.write(:junk, 1) }.should.raise Buffer::InvalidType
|
348
|
-
end
|
349
|
-
|
350
|
-
{ :byte => 0b10101010,
|
351
|
-
:short => 100,
|
352
|
-
:int => 65536,
|
353
|
-
:double => 123.456,
|
354
|
-
:long => 100_000_000,
|
355
|
-
:longlong => 666_555_444_333_222_111,
|
356
|
-
:cstring => 'hello',
|
357
|
-
}.each do |type, value|
|
358
|
-
|
359
|
-
should "read and write a #{type}" do
|
360
|
-
@buf.write(type, value)
|
361
|
-
@buf.rewind
|
362
|
-
@buf.read(type).should == value
|
363
|
-
@buf.should.be.empty
|
364
|
-
end
|
365
|
-
|
366
|
-
end
|
367
|
-
|
368
|
-
should "read and write multiple times" do
|
369
|
-
arr = [ :byte, 0b10101010, :short, 100 ]
|
370
|
-
@buf.write(*arr)
|
371
|
-
@buf.rewind
|
372
|
-
@buf.read(arr.shift).should == arr.shift
|
373
|
-
@buf.read(arr.shift).should == arr.shift
|
374
|
-
end
|
375
|
-
|
376
|
-
[
|
377
|
-
{ :num => 1 },
|
378
|
-
{ :symbol => :abc },
|
379
|
-
{ :object => {} },
|
380
|
-
{ :array => [1, 2, 3] },
|
381
|
-
{ :string => 'abcdefg' },
|
382
|
-
{ :oid => { :_id => '51d9ca7053a2012be4ecd660' } },
|
383
|
-
{ :ref => { :_ns => 'namespace',
|
384
|
-
:_id => '51d9ca7053a2012be4ecd660' } },
|
385
|
-
{ :boolean => true },
|
386
|
-
{ :time => Time.at(Time.now.to_i) },
|
387
|
-
{ :null => nil },
|
388
|
-
{ :regex => /^.*?def/im }
|
389
|
-
]. each do |bson|
|
390
|
-
|
391
|
-
should "read and write bson with #{bson.keys.first}s" do
|
392
|
-
@buf.write(:bson, bson)
|
393
|
-
@buf.rewind
|
394
|
-
@buf.read(:bson).should == bson
|
395
|
-
@buf.should.be.empty
|
396
|
-
end
|
397
|
-
|
398
|
-
end
|
399
|
-
|
400
|
-
should 'do transactional reads with #extract' do
|
401
|
-
@buf.write :byte, 8
|
402
|
-
orig = @buf.to_s
|
403
|
-
|
404
|
-
@buf.rewind
|
405
|
-
@buf.extract do |b|
|
406
|
-
b.read :byte
|
407
|
-
b.read :short
|
408
|
-
end
|
409
|
-
|
410
|
-
@buf.pos.should == 0
|
411
|
-
@buf.data.should == orig
|
412
|
-
end
|
413
|
-
end
|
414
|
-
end
|
data/spec/buffer_spec.rb
DELETED
@@ -1,123 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
-
|
3
|
-
include EMMongo
|
4
|
-
|
5
|
-
describe Buffer do
|
6
|
-
before do
|
7
|
-
@buf = Buffer.new
|
8
|
-
end
|
9
|
-
|
10
|
-
it 'should have contents' do
|
11
|
-
@buf.contents.should == ''
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'should initialize with data' do
|
15
|
-
@buf = Buffer.new('abc')
|
16
|
-
@buf.contents.should == 'abc'
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'should append raw data' do
|
20
|
-
@buf << 'abc'
|
21
|
-
@buf << 'def'
|
22
|
-
@buf.contents.should == 'abcdef'
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'should append other buffers' do
|
26
|
-
@buf << Buffer.new('abc')
|
27
|
-
@buf.data.should == 'abc'
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'should have a position' do
|
31
|
-
@buf.pos.should == 0
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'should have a length' do
|
35
|
-
@buf.length.should == 0
|
36
|
-
@buf << 'abc'
|
37
|
-
@buf.length.should == 3
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'should know the end' do
|
41
|
-
@buf.empty?.should == true
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'should read and write data' do
|
45
|
-
@buf._write('abc')
|
46
|
-
@buf.rewind
|
47
|
-
@buf._read(2).should == 'ab'
|
48
|
-
@buf._read(1).should == 'c'
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'should raise on overflow' do
|
52
|
-
lambda{ @buf._read(1) }.should { raise Buffer::Overflow }
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'should raise on invalid types' do
|
56
|
-
lambda{ @buf.read(:junk) }.should { raise Buffer::InvalidType }
|
57
|
-
lambda{ @buf.write(:junk, 1) }.should { raise Buffer::InvalidType }
|
58
|
-
end
|
59
|
-
|
60
|
-
{ :byte => 0b10101010,
|
61
|
-
:short => 100,
|
62
|
-
:int => 65536,
|
63
|
-
:double => 123.456,
|
64
|
-
:long => 100_000_000,
|
65
|
-
:longlong => 666_555_444_333_222_111,
|
66
|
-
:cstring => 'hello',
|
67
|
-
}.each do |type, value|
|
68
|
-
|
69
|
-
it "should read and write a #{type}" do
|
70
|
-
@buf.write(type, value)
|
71
|
-
@buf.rewind
|
72
|
-
@buf.read(type).should == value
|
73
|
-
#@buf.should.be.empty
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
it "should read and write multiple times" do
|
79
|
-
arr = [ :byte, 0b10101010, :short, 100 ]
|
80
|
-
@buf.write(*arr)
|
81
|
-
@buf.rewind
|
82
|
-
@buf.read(arr.shift).should == arr.shift
|
83
|
-
@buf.read(arr.shift).should == arr.shift
|
84
|
-
end
|
85
|
-
|
86
|
-
[
|
87
|
-
{ :num => 1 },
|
88
|
-
{ :symbol => :abc },
|
89
|
-
{ :object => {} },
|
90
|
-
{ :array => [1, 2, 3] },
|
91
|
-
{ :string => 'abcdefg' },
|
92
|
-
{ :oid => { :_id => '51d9ca7053a2012be4ecd660' } },
|
93
|
-
{ :ref => { :_ns => 'namespace',
|
94
|
-
:_id => '51d9ca7053a2012be4ecd660' } },
|
95
|
-
{ :boolean => true },
|
96
|
-
{ :time => Time.at(Time.now.to_i) },
|
97
|
-
{ :null => nil },
|
98
|
-
{ :regex => /^.*?def/im }
|
99
|
-
]. each do |bson|
|
100
|
-
|
101
|
-
it "should read and write bson with #{bson.keys.first}s" do
|
102
|
-
@buf.write(:bson, bson)
|
103
|
-
@buf.rewind
|
104
|
-
@buf.read(:bson).should == bson
|
105
|
-
#@buf.should.be.empty
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'should do transactional reads with #extract' do
|
111
|
-
@buf.write :byte, 8
|
112
|
-
orig = @buf.to_s
|
113
|
-
|
114
|
-
@buf.rewind
|
115
|
-
@buf.extract do |b|
|
116
|
-
b.read :byte
|
117
|
-
b.read :short
|
118
|
-
end
|
119
|
-
|
120
|
-
@buf.pos.should == 0
|
121
|
-
@buf.data.should == orig
|
122
|
-
end
|
123
|
-
end
|