mongodb-mongo 0.6.3 → 0.6.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/Rakefile +13 -2
- data/bin/standard_benchmark +1 -1
- data/examples/types.rb +2 -2
- data/lib/mongo/cursor.rb +17 -5
- data/lib/mongo/db.rb +11 -1
- data/lib/mongo/message/message_header.rb +8 -2
- data/lib/mongo/mongo.rb +4 -9
- data/lib/mongo/query.rb +5 -2
- data/lib/mongo/types/code.rb +34 -0
- data/lib/mongo/types/dbref.rb +4 -4
- data/lib/mongo/util/bson.rb +152 -92
- data/lib/mongo/util/byte_buffer.rb +15 -3
- data/lib/mongo/util/ordered_hash.rb +6 -0
- data/lib/mongo/util/xml_to_ruby.rb +4 -2
- data/mongo-ruby-driver.gemspec +2 -1
- data/tests/test_admin.rb +7 -0
- data/tests/test_bson.rb +13 -5
- data/tests/test_cursor.rb +52 -0
- data/tests/test_db.rb +1 -1
- data/tests/test_db_api.rb +47 -0
- data/tests/test_mongo.rb +1 -1
- data/tests/test_ordered_hash.rb +27 -0
- data/tests/test_round_trip.rb +2 -2
- metadata +2 -1
data/Rakefile
CHANGED
@@ -4,7 +4,10 @@ require 'fileutils'
|
|
4
4
|
require 'rake'
|
5
5
|
require 'rake/testtask'
|
6
6
|
require 'rake/gempackagetask'
|
7
|
-
|
7
|
+
begin
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
rescue LoadError
|
10
|
+
end
|
8
11
|
|
9
12
|
# NOTE: some of the tests assume Mongo is running
|
10
13
|
Rake::TestTask.new do |t|
|
@@ -23,7 +26,15 @@ task :publish => [:rdoc] do
|
|
23
26
|
Rake::RubyForgePublisher.new(GEM, RUBYFORGE_USER).upload
|
24
27
|
end
|
25
28
|
|
26
|
-
|
29
|
+
desc "Compile the extension"
|
30
|
+
task :compile do
|
31
|
+
cd 'ext/cbson'
|
32
|
+
ruby 'extconf.rb'
|
33
|
+
sh 'make'
|
34
|
+
cp 'cbson.bundle', '../../lib/mongo/ext/cbson.bundle'
|
35
|
+
end
|
36
|
+
|
37
|
+
namespace :gem do
|
27
38
|
|
28
39
|
desc "Install the gem locally"
|
29
40
|
task :install do
|
data/bin/standard_benchmark
CHANGED
@@ -64,7 +64,7 @@ benchmark('insert (medium, no index)', insert, PER_TRIAL, db, 'medium_none', MED
|
|
64
64
|
benchmark('insert (large, no index)', insert, PER_TRIAL, db, 'large_none', LARGE)
|
65
65
|
|
66
66
|
index_on_x = Proc.new { |coll, object|
|
67
|
-
coll.create_index('
|
67
|
+
coll.create_index('x')
|
68
68
|
}
|
69
69
|
benchmark('insert (small, indexed)', insert, PER_TRIAL, db, 'small_index', SMALL, index_on_x)
|
70
70
|
benchmark('insert (medium, indexed)', insert, PER_TRIAL, db, 'medium_index', MEDIUM, index_on_x)
|
data/examples/types.rb
CHANGED
@@ -25,8 +25,8 @@ coll.insert('array' => [1, 2, 3],
|
|
25
25
|
'float' => 33.33333,
|
26
26
|
'regex' => /foobar/i,
|
27
27
|
'boolean' => true,
|
28
|
-
'$where' => 'this.x == 3',
|
29
|
-
'dbref' => DBRef.new(
|
28
|
+
'$where' => Code.new('this.x == 3'),
|
29
|
+
'dbref' => DBRef.new(coll.name, ObjectID.new),
|
30
30
|
|
31
31
|
# NOTE: the undefined type is not saved to the database properly. This is a
|
32
32
|
# Mongo bug. However, the undefined type may go away completely.
|
data/lib/mongo/cursor.rb
CHANGED
@@ -18,6 +18,9 @@ require 'mongo/message'
|
|
18
18
|
require 'mongo/util/byte_buffer'
|
19
19
|
require 'mongo/util/bson'
|
20
20
|
|
21
|
+
require 'logger'
|
22
|
+
LOG = Logger.new('recv_file.log', 'daily')
|
23
|
+
|
21
24
|
module XGen
|
22
25
|
module Mongo
|
23
26
|
module Driver
|
@@ -160,12 +163,15 @@ module XGen
|
|
160
163
|
end
|
161
164
|
|
162
165
|
def read_message_header
|
163
|
-
MessageHeader.new.read_header(@db
|
166
|
+
MessageHeader.new.read_header(@db)
|
164
167
|
end
|
165
168
|
|
166
169
|
def read_response_header
|
167
170
|
header_buf = ByteBuffer.new
|
168
|
-
|
171
|
+
read = @db.receive_full(RESPONSE_HEADER_SIZE)
|
172
|
+
header_buf.put_array(read.unpack("C*"))
|
173
|
+
LOG.debug "resp head: #{read.inspect}\n"
|
174
|
+
raise "BAD SIZE" unless read.length == RESPONSE_HEADER_SIZE
|
169
175
|
raise "Short read for DB response header; expected #{RESPONSE_HEADER_SIZE} bytes, saw #{header_buf.length}" unless header_buf.length == RESPONSE_HEADER_SIZE
|
170
176
|
header_buf.rewind
|
171
177
|
@result_flags = header_buf.get_int
|
@@ -200,13 +206,19 @@ module XGen
|
|
200
206
|
|
201
207
|
def object_from_stream
|
202
208
|
buf = ByteBuffer.new
|
203
|
-
|
209
|
+
read1 = @db.receive_full(4)
|
210
|
+
buf.put_array(read1.unpack("C*"))
|
211
|
+
LOG.debug "size: #{read1.inspect}\n"
|
212
|
+
raise "BAD SIZE" unless read1.length == 4
|
204
213
|
buf.rewind
|
205
214
|
size = buf.get_int
|
206
|
-
|
215
|
+
read2 = @db.receive_full(size - 4)
|
216
|
+
buf.put_array(read2.unpack("C*"), 4)
|
217
|
+
LOG.debug "body: #{read2.inspect}\n"
|
218
|
+
raise "BAD SIZE" unless read2.length == size - 4
|
207
219
|
@n_remaining -= 1
|
208
220
|
buf.rewind
|
209
|
-
BSON.new
|
221
|
+
BSON.new.deserialize(buf)
|
210
222
|
end
|
211
223
|
|
212
224
|
def send_query_if_needed
|
data/lib/mongo/db.rb
CHANGED
@@ -143,7 +143,7 @@ module XGen
|
|
143
143
|
@semaphore.lock if semaphore_is_locked
|
144
144
|
|
145
145
|
break if @slave_ok || is_master
|
146
|
-
rescue => ex
|
146
|
+
rescue SocketError, SystemCallError, IOError => ex
|
147
147
|
close if @socket
|
148
148
|
end
|
149
149
|
@socket
|
@@ -301,6 +301,16 @@ module XGen
|
|
301
301
|
@socket != nil
|
302
302
|
end
|
303
303
|
|
304
|
+
def receive_full(length)
|
305
|
+
message = ""
|
306
|
+
while message.length < length do
|
307
|
+
chunk = @socket.recv(length - message.length)
|
308
|
+
raise "connection closed" unless chunk.length > 0
|
309
|
+
message += chunk
|
310
|
+
end
|
311
|
+
message
|
312
|
+
end
|
313
|
+
|
304
314
|
# Send a MsgMessage to the database.
|
305
315
|
def send_message(msg)
|
306
316
|
send_to_db(MsgMessage.new(msg))
|
@@ -16,6 +16,9 @@
|
|
16
16
|
|
17
17
|
require 'mongo/util/byte_buffer'
|
18
18
|
|
19
|
+
require 'logger'
|
20
|
+
LOG = Logger.new('recv_file.log', 'daily')
|
21
|
+
|
19
22
|
module XGen
|
20
23
|
module Mongo
|
21
24
|
module Driver
|
@@ -28,9 +31,12 @@ module XGen
|
|
28
31
|
@buf = ByteBuffer.new
|
29
32
|
end
|
30
33
|
|
31
|
-
def read_header(
|
34
|
+
def read_header(db)
|
32
35
|
@buf.rewind
|
33
|
-
|
36
|
+
read = db.receive_full(HEADER_SIZE)
|
37
|
+
@buf.put_array(read.unpack("C*"))
|
38
|
+
LOG.debug "header: #{read.inspect}\n"
|
39
|
+
raise "BAD SIZE" unless read.length == HEADER_SIZE
|
34
40
|
raise "Short read for DB response header: expected #{HEADER_SIZE} bytes, saw #{@buf.size}" unless @buf.size == HEADER_SIZE
|
35
41
|
@buf.rewind
|
36
42
|
@size = @buf.get_int
|
data/lib/mongo/mongo.rb
CHANGED
@@ -34,7 +34,7 @@ module XGen
|
|
34
34
|
# and :right. Each key maps to either
|
35
35
|
# * a server name, in which case port is DEFAULT_PORT
|
36
36
|
# * a port number, in which case server is "localhost"
|
37
|
-
# * an array containing a server name and a port number in
|
37
|
+
# * an array containing a server name and a port number in that order
|
38
38
|
#
|
39
39
|
# +options+ are passed on to each DB instance:
|
40
40
|
#
|
@@ -127,8 +127,8 @@ module XGen
|
|
127
127
|
|
128
128
|
protected
|
129
129
|
|
130
|
-
# Turns an array containing
|
131
|
-
#
|
130
|
+
# Turns an array containing a host name string and a
|
131
|
+
# port number integer into a [host, port] pair array.
|
132
132
|
def pair_val_to_connection(a)
|
133
133
|
case a
|
134
134
|
when nil
|
@@ -138,12 +138,7 @@ module XGen
|
|
138
138
|
when Integer
|
139
139
|
['localhost', a]
|
140
140
|
when Array
|
141
|
-
|
142
|
-
connection[0] = a[0] if a[0].kind_of?(String)
|
143
|
-
connection[0] = a[1] if a[1].kind_of?(String)
|
144
|
-
connection[1] = a[0] if a[0].kind_of?(Integer)
|
145
|
-
connection[1] = a[1] if a[1].kind_of?(Integer)
|
146
|
-
connection
|
141
|
+
a
|
147
142
|
end
|
148
143
|
end
|
149
144
|
|
data/lib/mongo/query.rb
CHANGED
@@ -16,6 +16,7 @@
|
|
16
16
|
|
17
17
|
require 'mongo/collection'
|
18
18
|
require 'mongo/message'
|
19
|
+
require 'mongo/types/code'
|
19
20
|
|
20
21
|
module XGen
|
21
22
|
module Mongo
|
@@ -70,14 +71,16 @@ module XGen
|
|
70
71
|
self.fields = return_fields
|
71
72
|
end
|
72
73
|
|
73
|
-
# Set query selector hash. If sel is
|
74
|
+
# Set query selector hash. If sel is Code/string, it will be used as a
|
74
75
|
# $where clause. (See Mongo docs for details.)
|
75
76
|
def selector=(sel)
|
76
77
|
@selector = case sel
|
77
78
|
when nil
|
78
79
|
{}
|
79
|
-
when
|
80
|
+
when Code
|
80
81
|
{"$where" => sel}
|
82
|
+
when String
|
83
|
+
{"$where" => Code.new(sel)}
|
81
84
|
when Hash
|
82
85
|
sel
|
83
86
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
module XGen
|
18
|
+
module Mongo
|
19
|
+
module Driver
|
20
|
+
|
21
|
+
# JavaScript code to be evaluated by MongoDB
|
22
|
+
class Code < String
|
23
|
+
# Hash mapping identifiers to their values
|
24
|
+
attr_accessor :scope
|
25
|
+
|
26
|
+
def initialize(code, scope={})
|
27
|
+
super(code)
|
28
|
+
@scope = scope
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/mongo/types/dbref.rb
CHANGED
@@ -20,11 +20,11 @@ module XGen
|
|
20
20
|
|
21
21
|
class DBRef
|
22
22
|
|
23
|
-
attr_reader :
|
23
|
+
attr_reader :namespace, :object_id
|
24
24
|
|
25
|
-
def initialize(
|
26
|
-
@
|
27
|
-
|
25
|
+
def initialize(namespace, object_id)
|
26
|
+
@namespace, @object_id =
|
27
|
+
namespace, object_id
|
28
28
|
end
|
29
29
|
|
30
30
|
def to_s
|
data/lib/mongo/util/bson.rb
CHANGED
@@ -62,9 +62,7 @@ class BSON
|
|
62
62
|
buf.put_array(to_utf8(val.to_s).unpack("C*") + [0])
|
63
63
|
end
|
64
64
|
|
65
|
-
def initialize(
|
66
|
-
# db is only needed during deserialization when the data contains a DBRef
|
67
|
-
@db = db
|
65
|
+
def initialize()
|
68
66
|
@buf = ByteBuffer.new
|
69
67
|
end
|
70
68
|
|
@@ -72,27 +70,34 @@ class BSON
|
|
72
70
|
@buf.to_a
|
73
71
|
end
|
74
72
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
73
|
+
begin
|
74
|
+
require 'mongo/ext/cbson'
|
75
|
+
def serialize(obj)
|
76
|
+
@buf = ByteBuffer.new(CBson.serialize(obj))
|
77
|
+
end
|
78
|
+
rescue LoadError
|
79
|
+
def serialize(obj)
|
80
|
+
raise "Document is null" unless obj
|
81
|
+
|
82
|
+
@buf.rewind
|
83
|
+
# put in a placeholder for the total size
|
84
|
+
@buf.put_int(0)
|
85
|
+
|
86
|
+
# Write key/value pairs. Always write _id first if it exists.
|
87
|
+
oid = obj['_id'] || obj[:_id]
|
88
|
+
serialize_key_value('_id', oid) if oid
|
89
|
+
obj.each {|k, v| serialize_key_value(k, v) unless k == '_id' || k == :_id }
|
90
|
+
|
91
|
+
serialize_eoo_element(@buf)
|
92
|
+
@buf.put_int(@buf.size, 0)
|
93
|
+
self
|
94
|
+
end
|
90
95
|
end
|
91
96
|
|
92
97
|
def serialize_key_value(k, v)
|
93
|
-
type = bson_type(v
|
98
|
+
type = bson_type(v)
|
94
99
|
case type
|
95
|
-
when STRING,
|
100
|
+
when STRING, SYMBOL
|
96
101
|
serialize_string_element(@buf, k, v, type)
|
97
102
|
when NUMBER, NUMBER_INT
|
98
103
|
serialize_number_element(@buf, k, v, type)
|
@@ -117,76 +122,92 @@ class BSON
|
|
117
122
|
when UNDEFINED
|
118
123
|
serialize_undefined_element(@buf, k)
|
119
124
|
when CODE_W_SCOPE
|
120
|
-
|
121
|
-
raise "unimplemented type #{type}"
|
125
|
+
serialize_code_w_scope(@buf, k, v)
|
122
126
|
else
|
123
127
|
raise "unhandled type #{type}"
|
124
128
|
end
|
125
129
|
end
|
126
130
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
@buf.get_int # eat message size
|
133
|
-
doc = OrderedHash.new
|
134
|
-
while @buf.more?
|
135
|
-
type = @buf.get
|
136
|
-
case type
|
137
|
-
when STRING, CODE
|
138
|
-
key = deserialize_cstr(@buf)
|
139
|
-
doc[key] = deserialize_string_data(@buf)
|
140
|
-
when SYMBOL
|
141
|
-
key = deserialize_cstr(@buf)
|
142
|
-
doc[key] = deserialize_string_data(@buf).intern
|
143
|
-
when NUMBER
|
144
|
-
key = deserialize_cstr(@buf)
|
145
|
-
doc[key] = deserialize_number_data(@buf)
|
146
|
-
when NUMBER_INT
|
147
|
-
key = deserialize_cstr(@buf)
|
148
|
-
doc[key] = deserialize_number_int_data(@buf)
|
149
|
-
when OID
|
150
|
-
key = deserialize_cstr(@buf)
|
151
|
-
doc[key] = deserialize_oid_data(@buf)
|
152
|
-
when ARRAY
|
153
|
-
key = deserialize_cstr(@buf)
|
154
|
-
doc[key] = deserialize_array_data(@buf, doc)
|
155
|
-
when REGEX
|
156
|
-
key = deserialize_cstr(@buf)
|
157
|
-
doc[key] = deserialize_regex_data(@buf)
|
158
|
-
when OBJECT
|
159
|
-
key = deserialize_cstr(@buf)
|
160
|
-
doc[key] = deserialize_object_data(@buf, doc)
|
161
|
-
when BOOLEAN
|
162
|
-
key = deserialize_cstr(@buf)
|
163
|
-
doc[key] = deserialize_boolean_data(@buf)
|
164
|
-
when DATE
|
165
|
-
key = deserialize_cstr(@buf)
|
166
|
-
doc[key] = deserialize_date_data(@buf)
|
167
|
-
when NULL
|
168
|
-
key = deserialize_cstr(@buf)
|
169
|
-
doc[key] = nil
|
170
|
-
when UNDEFINED
|
171
|
-
key = deserialize_cstr(@buf)
|
172
|
-
doc[key] = Undefined.new
|
173
|
-
when REF
|
174
|
-
key = deserialize_cstr(@buf)
|
175
|
-
doc[key] = deserialize_dbref_data(@buf, key, parent)
|
176
|
-
when BINARY
|
177
|
-
key = deserialize_cstr(@buf)
|
178
|
-
doc[key] = deserialize_binary_data(@buf)
|
179
|
-
when CODE_W_SCOPE
|
180
|
-
# TODO CODE_W_SCOPE unimplemented; may be removed
|
181
|
-
raise "unimplemented type #{type}"
|
182
|
-
when EOO
|
183
|
-
break
|
131
|
+
begin
|
132
|
+
require 'mongo/ext/cbson'
|
133
|
+
def deserialize(buf=nil)
|
134
|
+
if buf.is_a? String
|
135
|
+
@buf = ByteBuffer.new(buf) if buf
|
184
136
|
else
|
185
|
-
|
137
|
+
@buf = ByteBuffer.new(buf.to_a) if buf
|
186
138
|
end
|
139
|
+
@buf.rewind
|
140
|
+
CBson.deserialize(@buf.to_s)
|
141
|
+
end
|
142
|
+
rescue LoadError
|
143
|
+
def deserialize(buf=nil)
|
144
|
+
# If buf is nil, use @buf, assumed to contain already-serialized BSON.
|
145
|
+
# This is only true during testing.
|
146
|
+
if buf.is_a? String
|
147
|
+
@buf = ByteBuffer.new(buf) if buf
|
148
|
+
else
|
149
|
+
@buf = ByteBuffer.new(buf.to_a) if buf
|
150
|
+
end
|
151
|
+
@buf.rewind
|
152
|
+
@buf.get_int # eat message size
|
153
|
+
doc = OrderedHash.new
|
154
|
+
while @buf.more?
|
155
|
+
type = @buf.get
|
156
|
+
case type
|
157
|
+
when STRING, CODE
|
158
|
+
key = deserialize_cstr(@buf)
|
159
|
+
doc[key] = deserialize_string_data(@buf)
|
160
|
+
when SYMBOL
|
161
|
+
key = deserialize_cstr(@buf)
|
162
|
+
doc[key] = deserialize_string_data(@buf).intern
|
163
|
+
when NUMBER
|
164
|
+
key = deserialize_cstr(@buf)
|
165
|
+
doc[key] = deserialize_number_data(@buf)
|
166
|
+
when NUMBER_INT
|
167
|
+
key = deserialize_cstr(@buf)
|
168
|
+
doc[key] = deserialize_number_int_data(@buf)
|
169
|
+
when OID
|
170
|
+
key = deserialize_cstr(@buf)
|
171
|
+
doc[key] = deserialize_oid_data(@buf)
|
172
|
+
when ARRAY
|
173
|
+
key = deserialize_cstr(@buf)
|
174
|
+
doc[key] = deserialize_array_data(@buf)
|
175
|
+
when REGEX
|
176
|
+
key = deserialize_cstr(@buf)
|
177
|
+
doc[key] = deserialize_regex_data(@buf)
|
178
|
+
when OBJECT
|
179
|
+
key = deserialize_cstr(@buf)
|
180
|
+
doc[key] = deserialize_object_data(@buf)
|
181
|
+
when BOOLEAN
|
182
|
+
key = deserialize_cstr(@buf)
|
183
|
+
doc[key] = deserialize_boolean_data(@buf)
|
184
|
+
when DATE
|
185
|
+
key = deserialize_cstr(@buf)
|
186
|
+
doc[key] = deserialize_date_data(@buf)
|
187
|
+
when NULL
|
188
|
+
key = deserialize_cstr(@buf)
|
189
|
+
doc[key] = nil
|
190
|
+
when UNDEFINED
|
191
|
+
key = deserialize_cstr(@buf)
|
192
|
+
doc[key] = Undefined.new
|
193
|
+
when REF
|
194
|
+
key = deserialize_cstr(@buf)
|
195
|
+
doc[key] = deserialize_dbref_data(@buf)
|
196
|
+
when BINARY
|
197
|
+
key = deserialize_cstr(@buf)
|
198
|
+
doc[key] = deserialize_binary_data(@buf)
|
199
|
+
when CODE_W_SCOPE
|
200
|
+
key = deserialize_cstr(@buf)
|
201
|
+
doc[key] = deserialize_code_w_scope_data(@buf)
|
202
|
+
when EOO
|
203
|
+
break
|
204
|
+
else
|
205
|
+
raise "Unknown type #{type}, key = #{key}"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
@buf.rewind
|
209
|
+
doc
|
187
210
|
end
|
188
|
-
@buf.rewind
|
189
|
-
doc
|
190
211
|
end
|
191
212
|
|
192
213
|
# For debugging.
|
@@ -221,14 +242,14 @@ class BSON
|
|
221
242
|
buf.get_int
|
222
243
|
end
|
223
244
|
|
224
|
-
def deserialize_object_data(buf
|
245
|
+
def deserialize_object_data(buf)
|
225
246
|
size = buf.get_int
|
226
247
|
buf.position -= 4
|
227
|
-
BSON.new(
|
248
|
+
BSON.new().deserialize(buf.get(size))
|
228
249
|
end
|
229
250
|
|
230
|
-
def deserialize_array_data(buf
|
231
|
-
h = deserialize_object_data(buf
|
251
|
+
def deserialize_array_data(buf)
|
252
|
+
h = deserialize_object_data(buf)
|
232
253
|
a = []
|
233
254
|
h.each { |k, v| a[k.to_i] = v }
|
234
255
|
a
|
@@ -248,21 +269,42 @@ class BSON
|
|
248
269
|
def deserialize_string_data(buf)
|
249
270
|
len = buf.get_int
|
250
271
|
bytes = buf.get(len)
|
251
|
-
str = bytes[0..-2]
|
272
|
+
str = bytes[0..-2]
|
273
|
+
if str.respond_to? "pack"
|
274
|
+
str = str.pack("C*")
|
275
|
+
end
|
252
276
|
if RUBY_VERSION >= '1.9'
|
253
277
|
str.force_encoding("utf-8")
|
254
278
|
end
|
255
279
|
str
|
256
280
|
end
|
257
281
|
|
282
|
+
def deserialize_code_w_scope_data(buf)
|
283
|
+
buf.get_int
|
284
|
+
len = buf.get_int
|
285
|
+
code = buf.get(len)[0..-2]
|
286
|
+
if code.respond_to? "pack"
|
287
|
+
code = code.pack("C*")
|
288
|
+
end
|
289
|
+
if RUBY_VERSION >= '1.9'
|
290
|
+
code.force_encoding("utf-8")
|
291
|
+
end
|
292
|
+
|
293
|
+
scope_size = buf.get_int
|
294
|
+
buf.position -= 4
|
295
|
+
scope = BSON.new().deserialize(buf.get(scope_size))
|
296
|
+
|
297
|
+
Code.new(code, scope)
|
298
|
+
end
|
299
|
+
|
258
300
|
def deserialize_oid_data(buf)
|
259
301
|
ObjectID.new(buf.get(12))
|
260
302
|
end
|
261
303
|
|
262
|
-
def deserialize_dbref_data(buf
|
304
|
+
def deserialize_dbref_data(buf)
|
263
305
|
ns = deserialize_string_data(buf)
|
264
306
|
oid = deserialize_oid_data(buf)
|
265
|
-
DBRef.new(
|
307
|
+
DBRef.new(ns, oid)
|
266
308
|
end
|
267
309
|
|
268
310
|
def deserialize_binary_data(buf)
|
@@ -391,6 +433,23 @@ class BSON
|
|
391
433
|
buf.position = end_pos
|
392
434
|
end
|
393
435
|
|
436
|
+
def serialize_code_w_scope(buf, key, val)
|
437
|
+
buf.put(CODE_W_SCOPE)
|
438
|
+
self.class.serialize_cstr(buf, key)
|
439
|
+
|
440
|
+
# Make a hole for the length
|
441
|
+
len_pos = buf.position
|
442
|
+
buf.put_int(0)
|
443
|
+
|
444
|
+
buf.put_int(val.length + 1)
|
445
|
+
self.class.serialize_cstr(buf, val)
|
446
|
+
buf.put_array(BSON.new.serialize(val.scope).to_a)
|
447
|
+
|
448
|
+
end_pos = buf.position
|
449
|
+
buf.put_int(end_pos - len_pos, len_pos)
|
450
|
+
buf.position = end_pos
|
451
|
+
end
|
452
|
+
|
394
453
|
def deserialize_cstr(buf)
|
395
454
|
chars = ""
|
396
455
|
while true
|
@@ -404,7 +463,7 @@ class BSON
|
|
404
463
|
chars
|
405
464
|
end
|
406
465
|
|
407
|
-
def bson_type(o
|
466
|
+
def bson_type(o)
|
408
467
|
case o
|
409
468
|
when nil
|
410
469
|
NULL
|
@@ -414,9 +473,10 @@ class BSON
|
|
414
473
|
NUMBER
|
415
474
|
when ByteBuffer
|
416
475
|
BINARY
|
476
|
+
when Code
|
477
|
+
CODE_W_SCOPE
|
417
478
|
when String
|
418
|
-
|
419
|
-
key == "$where" ? CODE : STRING
|
479
|
+
STRING
|
420
480
|
when Array
|
421
481
|
ARRAY
|
422
482
|
when Regexp
|
@@ -100,7 +100,11 @@ class ByteBuffer
|
|
100
100
|
if one_byte
|
101
101
|
@buf[start]
|
102
102
|
else
|
103
|
-
@buf
|
103
|
+
if @buf.respond_to? "unpack"
|
104
|
+
@buf[start, len].unpack("C*")
|
105
|
+
else
|
106
|
+
@buf[start, len]
|
107
|
+
end
|
104
108
|
end
|
105
109
|
end
|
106
110
|
|
@@ -135,11 +139,19 @@ class ByteBuffer
|
|
135
139
|
end
|
136
140
|
|
137
141
|
def to_a
|
138
|
-
@buf
|
142
|
+
if @buf.respond_to? "unpack"
|
143
|
+
@buf.unpack("C*")
|
144
|
+
else
|
145
|
+
@buf
|
146
|
+
end
|
139
147
|
end
|
140
148
|
|
141
149
|
def to_s
|
142
|
-
@buf.pack
|
150
|
+
if @buf.respond_to? "pack"
|
151
|
+
@buf.pack("C*")
|
152
|
+
else
|
153
|
+
@buf
|
154
|
+
end
|
143
155
|
end
|
144
156
|
|
145
157
|
def dump
|
@@ -42,8 +42,10 @@ class XMLToRuby
|
|
42
42
|
e.text.to_i
|
43
43
|
when 'number'
|
44
44
|
e.text.to_f
|
45
|
-
when 'string'
|
45
|
+
when 'string'
|
46
46
|
e.text.to_s
|
47
|
+
when 'code'
|
48
|
+
Code.new(e.text.to_s)
|
47
49
|
when 'binary'
|
48
50
|
bin = Binary.new
|
49
51
|
decoded = Base64.decode64(e.text.to_s)
|
@@ -99,7 +101,7 @@ class XMLToRuby
|
|
99
101
|
def dbref_to_ruby(elements)
|
100
102
|
ns = elements['ns'].text
|
101
103
|
oid_str = elements['oid'].text
|
102
|
-
DBRef.new(
|
104
|
+
DBRef.new(ns, ObjectID.from_string(oid_str))
|
103
105
|
end
|
104
106
|
|
105
107
|
end
|
data/mongo-ruby-driver.gemspec
CHANGED
@@ -35,6 +35,7 @@ PACKAGE_FILES = ['README.rdoc', 'Rakefile', 'mongo-ruby-driver.gemspec',
|
|
35
35
|
'lib/mongo/mongo.rb',
|
36
36
|
'lib/mongo/query.rb',
|
37
37
|
'lib/mongo/types/binary.rb',
|
38
|
+
'lib/mongo/types/code.rb',
|
38
39
|
'lib/mongo/types/dbref.rb',
|
39
40
|
'lib/mongo/types/objectid.rb',
|
40
41
|
'lib/mongo/types/regexp_of_holding.rb',
|
@@ -74,7 +75,7 @@ TEST_FILES = ['tests/mongo-qa/_common.rb',
|
|
74
75
|
|
75
76
|
Gem::Specification.new do |s|
|
76
77
|
s.name = 'mongo'
|
77
|
-
s.version = '0.6.
|
78
|
+
s.version = '0.6.4'
|
78
79
|
s.platform = Gem::Platform::RUBY
|
79
80
|
s.summary = 'Simple pure-Ruby driver for the 10gen Mongo DB'
|
80
81
|
s.description = 'A pure-Ruby driver for the 10gen Mongo DB. For more information about Mongo, see http://www.mongodb.org.'
|
data/tests/test_admin.rb
CHANGED
@@ -33,6 +33,13 @@ class AdminTest < Test::Unit::TestCase
|
|
33
33
|
assert_equal :slow_only, @admin.profiling_level
|
34
34
|
@admin.profiling_level = :off
|
35
35
|
assert_equal :off, @admin.profiling_level
|
36
|
+
@admin.profiling_level = :all
|
37
|
+
assert_equal :all, @admin.profiling_level
|
38
|
+
begin
|
39
|
+
@admin.profiling_level = :medium
|
40
|
+
fail "shouldn't be able to do this"
|
41
|
+
rescue
|
42
|
+
end
|
36
43
|
end
|
37
44
|
|
38
45
|
def test_profiling_info
|
data/tests/test_bson.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
2
2
|
require 'mongo'
|
3
|
+
require 'mongo/util/ordered_hash'
|
3
4
|
require 'test/unit'
|
4
5
|
|
5
6
|
class BSONTest < Test::Unit::TestCase
|
@@ -16,14 +17,11 @@ class BSONTest < Test::Unit::TestCase
|
|
16
17
|
def test_string
|
17
18
|
doc = {'doc' => 'hello, world'}
|
18
19
|
@b.serialize(doc)
|
19
|
-
assert_equal @b.to_a, [0x1b, 0x00, 0x00, 0x00, 0x02, ?d, ?o, ?c, 0x00, 0x0D,
|
20
|
-
0x00, 0x00, 0x00, ?h, ?e, ?l, ?l, ?o, ","[0], " "[0],
|
21
|
-
?w, ?o, ?r, ?l, ?d, 0x00, 0x00]
|
22
20
|
assert_equal doc, @b.deserialize
|
23
21
|
end
|
24
22
|
|
25
23
|
def test_code
|
26
|
-
doc = {'$where' => 'this.a.b < this.b'}
|
24
|
+
doc = {'$where' => Code.new('this.a.b < this.b')}
|
27
25
|
@b.serialize(doc)
|
28
26
|
assert_equal doc, @b.deserialize
|
29
27
|
end
|
@@ -40,6 +38,16 @@ class BSONTest < Test::Unit::TestCase
|
|
40
38
|
assert_equal doc, @b.deserialize
|
41
39
|
end
|
42
40
|
|
41
|
+
def test_ordered_hash
|
42
|
+
doc = OrderedHash.new
|
43
|
+
doc["b"] = 1
|
44
|
+
doc["a"] = 2
|
45
|
+
doc["c"] = 3
|
46
|
+
doc["d"] = 4
|
47
|
+
@b.serialize(doc)
|
48
|
+
assert_equal doc, @b.deserialize
|
49
|
+
end
|
50
|
+
|
43
51
|
def test_object
|
44
52
|
doc = {'doc' => {'age' => 42, 'name' => 'Spongebob', 'shoe_size' => 9.5}}
|
45
53
|
@b.serialize(doc)
|
@@ -100,7 +108,7 @@ class BSONTest < Test::Unit::TestCase
|
|
100
108
|
def test_dbref
|
101
109
|
oid = ObjectID.new
|
102
110
|
doc = {}
|
103
|
-
doc['dbref'] = DBRef.new(
|
111
|
+
doc['dbref'] = DBRef.new('namespace', oid)
|
104
112
|
@b.serialize(doc)
|
105
113
|
doc2 = @b.deserialize
|
106
114
|
assert_equal 'namespace', doc2['dbref'].namespace
|
data/tests/test_cursor.rb
CHANGED
@@ -40,6 +40,58 @@ class CursorTest < Test::Unit::TestCase
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
def test_refill_via_get_more
|
44
|
+
1000.times { |i|
|
45
|
+
@@coll.insert('a' => i)
|
46
|
+
}
|
47
|
+
|
48
|
+
assert_equal 1001, @@coll.count
|
49
|
+
count = 0
|
50
|
+
@@coll.find.each { |obj|
|
51
|
+
count += obj['a']
|
52
|
+
}
|
53
|
+
assert_equal 1001, @@coll.count
|
54
|
+
|
55
|
+
# do the same thing again for debugging
|
56
|
+
assert_equal 1001, @@coll.count
|
57
|
+
count2 = 0
|
58
|
+
@@coll.find.each { |obj|
|
59
|
+
count2 += obj['a']
|
60
|
+
}
|
61
|
+
assert_equal 1001, @@coll.count
|
62
|
+
|
63
|
+
assert_equal count, count2
|
64
|
+
assert_equal 499501, count
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_refill_via_get_more_alt_coll
|
68
|
+
coll = @@db.collection('test-alt-coll')
|
69
|
+
coll.clear
|
70
|
+
coll.insert('a' => 1) # collection not created until it's used
|
71
|
+
|
72
|
+
1000.times { |i|
|
73
|
+
coll.insert('a' => i)
|
74
|
+
}
|
75
|
+
|
76
|
+
assert_equal 1001, coll.count
|
77
|
+
count = 0
|
78
|
+
coll.find.each { |obj|
|
79
|
+
count += obj['a']
|
80
|
+
}
|
81
|
+
assert_equal 1001, coll.count
|
82
|
+
|
83
|
+
# do the same thing again for debugging
|
84
|
+
assert_equal 1001, coll.count
|
85
|
+
count2 = 0
|
86
|
+
coll.find.each { |obj|
|
87
|
+
count2 += obj['a']
|
88
|
+
}
|
89
|
+
assert_equal 1001, coll.count
|
90
|
+
|
91
|
+
assert_equal count, count2
|
92
|
+
assert_equal 499501, count
|
93
|
+
end
|
94
|
+
|
43
95
|
def test_close_after_query_sent
|
44
96
|
begin
|
45
97
|
cursor = @@coll.find('a' => 1)
|
data/tests/test_db.rb
CHANGED
@@ -56,7 +56,7 @@ class DBTest < Test::Unit::TestCase
|
|
56
56
|
@@db = Mongo.new({:left => "this-should-fail", :right => [@@host, @@port]}).db('ruby-mongo-test')
|
57
57
|
assert @@db.connected?
|
58
58
|
ensure
|
59
|
-
@@db = Mongo.new(@@host, @@port) unless @@db.connected?
|
59
|
+
@@db = Mongo.new(@@host, @@port).db('ruby-mongo-test') unless @@db.connected?
|
60
60
|
@@users = @@db.collection('system.users')
|
61
61
|
end
|
62
62
|
|
data/tests/test_db_api.rb
CHANGED
@@ -233,6 +233,12 @@ class DBAPITest < Test::Unit::TestCase
|
|
233
233
|
assert !@@db.collection_names.include?(@@coll_full_name)
|
234
234
|
end
|
235
235
|
|
236
|
+
def test_other_drop
|
237
|
+
assert @@db.collection_names.include?(@@coll_full_name)
|
238
|
+
@@coll.drop
|
239
|
+
assert !@@db.collection_names.include?(@@coll_full_name)
|
240
|
+
end
|
241
|
+
|
236
242
|
def test_collection_names
|
237
243
|
names = @@db.collection_names
|
238
244
|
assert names.length >= 1
|
@@ -277,6 +283,7 @@ class DBAPITest < Test::Unit::TestCase
|
|
277
283
|
def test_index_information
|
278
284
|
name = @@db.create_index(@@coll.name, 'a')
|
279
285
|
list = @@db.index_information(@@coll.name)
|
286
|
+
assert_equal @@coll.index_information, list
|
280
287
|
assert_equal 1, list.length
|
281
288
|
|
282
289
|
info = list[0]
|
@@ -368,6 +375,37 @@ class DBAPITest < Test::Unit::TestCase
|
|
368
375
|
@@db.strict = false
|
369
376
|
@@db.drop_collection('foobar')
|
370
377
|
end
|
378
|
+
|
379
|
+
# Now we're not in strict mode - should succeed
|
380
|
+
@@db.create_collection('foobar')
|
381
|
+
@@db.create_collection('foobar')
|
382
|
+
@@db.drop_collection('foobar')
|
383
|
+
end
|
384
|
+
|
385
|
+
def test_replace
|
386
|
+
assert_equal @@coll.count, 1
|
387
|
+
assert_equal @@coll.find_first["a"], 1
|
388
|
+
|
389
|
+
@@coll.replace({"a" => 1}, {"a" => 2})
|
390
|
+
assert_equal @@coll.count, 1
|
391
|
+
assert_equal @@coll.find_first["a"], 2
|
392
|
+
|
393
|
+
@@coll.replace({"b" => 1}, {"a" => 3})
|
394
|
+
assert_equal @@coll.count, 1
|
395
|
+
assert_equal @@coll.find_first["a"], 2
|
396
|
+
end
|
397
|
+
|
398
|
+
def test_repsert
|
399
|
+
assert_equal @@coll.count, 1
|
400
|
+
assert_equal @@coll.find_first["a"], 1
|
401
|
+
|
402
|
+
@@coll.repsert({"a" => 1}, {"a" => 2})
|
403
|
+
assert_equal @@coll.count, 1
|
404
|
+
assert_equal @@coll.find_first["a"], 2
|
405
|
+
|
406
|
+
@@coll.repsert({"b" => 1}, {"a" => 3})
|
407
|
+
assert_equal @@coll.count, 2
|
408
|
+
assert @@coll.find_first({"a" => 3})
|
371
409
|
end
|
372
410
|
|
373
411
|
def test_to_a
|
@@ -404,6 +442,15 @@ class DBAPITest < Test::Unit::TestCase
|
|
404
442
|
assert_equal "#{@@db.host}:#{@@db.port}", @@db.master
|
405
443
|
end
|
406
444
|
|
445
|
+
def test_where
|
446
|
+
@@coll.insert('a' => 2)
|
447
|
+
@@coll.insert('a' => 3)
|
448
|
+
|
449
|
+
assert_equal 3, @@coll.count
|
450
|
+
assert_equal 1, @@coll.find('$where' => Code.new('this.a > 2')).count
|
451
|
+
assert_equal 2, @@coll.find('$where' => Code.new('this.a > i', {'i' => 1})).count
|
452
|
+
end
|
453
|
+
|
407
454
|
def test_hint
|
408
455
|
name = @@coll.create_index('a')
|
409
456
|
begin
|
data/tests/test_mongo.rb
CHANGED
@@ -64,7 +64,7 @@ class MongoTest < Test::Unit::TestCase
|
|
64
64
|
assert_equal ['localhost', Mongo::DEFAULT_PORT], pair[0]
|
65
65
|
assert_equal ['bar', Mongo::DEFAULT_PORT], pair[1]
|
66
66
|
|
67
|
-
db = Mongo.new({:right => [
|
67
|
+
db = Mongo.new({:right => ['foo', 123], :left => 'bar'})
|
68
68
|
pair = db.instance_variable_get('@pair')
|
69
69
|
assert_equal 2, pair.length
|
70
70
|
assert_equal ['bar', Mongo::DEFAULT_PORT], pair[0]
|
data/tests/test_ordered_hash.rb
CHANGED
@@ -16,6 +16,33 @@ class OrderedHashTest < Test::Unit::TestCase
|
|
16
16
|
assert_equal [], OrderedHash.new.keys
|
17
17
|
end
|
18
18
|
|
19
|
+
def test_equality
|
20
|
+
a = OrderedHash.new
|
21
|
+
a['x'] = 1
|
22
|
+
a['y'] = 2
|
23
|
+
|
24
|
+
b = OrderedHash.new
|
25
|
+
b['y'] = 2
|
26
|
+
b['x'] = 1
|
27
|
+
|
28
|
+
c = OrderedHash.new
|
29
|
+
c['x'] = 1
|
30
|
+
c['y'] = 2
|
31
|
+
|
32
|
+
d = OrderedHash.new
|
33
|
+
d['x'] = 2
|
34
|
+
d['y'] = 3
|
35
|
+
|
36
|
+
e = OrderedHash.new
|
37
|
+
e['z'] = 1
|
38
|
+
e['y'] = 2
|
39
|
+
|
40
|
+
assert_equal a, c
|
41
|
+
assert_not_equal a, b
|
42
|
+
assert_not_equal a, d
|
43
|
+
assert_not_equal a, e
|
44
|
+
end
|
45
|
+
|
19
46
|
def test_order_preserved
|
20
47
|
assert_equal @ordered_keys, @oh.keys
|
21
48
|
end
|
data/tests/test_round_trip.rb
CHANGED
@@ -93,9 +93,9 @@ EOS
|
|
93
93
|
# Turn those BSON bytes back into a Ruby object.
|
94
94
|
#
|
95
95
|
# We're passing a nil db to the contructor here, but that's OK because
|
96
|
-
# the BSON
|
96
|
+
# the BSON DBRef bytes don't contain the db object in any case, and we
|
97
97
|
# don't care what the database is.
|
98
|
-
obj_from_bson = BSON.new
|
98
|
+
obj_from_bson = BSON.new.deserialize(ByteBuffer.new(bson_from_ruby))
|
99
99
|
assert_kind_of OrderedHash, obj_from_bson
|
100
100
|
|
101
101
|
# Turn that Ruby object into BSON and compare it to the original BSON
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongodb-mongo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Menard
|
@@ -61,6 +61,7 @@ files:
|
|
61
61
|
- lib/mongo/mongo.rb
|
62
62
|
- lib/mongo/query.rb
|
63
63
|
- lib/mongo/types/binary.rb
|
64
|
+
- lib/mongo/types/code.rb
|
64
65
|
- lib/mongo/types/dbref.rb
|
65
66
|
- lib/mongo/types/objectid.rb
|
66
67
|
- lib/mongo/types/regexp_of_holding.rb
|