mongo 0.0.2 → 0.0.3
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/README.rdoc +17 -0
- data/Rakefile +12 -1
- data/bin/validate +51 -0
- data/lib/mongo.rb +2 -0
- data/lib/mongo/binary.rb +34 -0
- data/lib/mongo/cursor.rb +1 -1
- data/lib/mongo/objectid.rb +17 -7
- data/lib/mongo/undefined.rb +31 -0
- data/lib/mongo/util/bson.rb +75 -16
- data/lib/mongo/util/ordered_hash.rb +1 -1
- data/lib/mongo/util/xml_to_ruby.rb +102 -0
- data/tests/test_bson.rb +29 -1
- data/tests/test_objectid.rb +12 -0
- data/tests/test_round_trip.rb +80 -96
- metadata +6 -2
data/README.rdoc
CHANGED
@@ -57,6 +57,23 @@ If you have the source code, you can run the tests.
|
|
57
57
|
|
58
58
|
The tests assume that the Mongo database is running on the default port.
|
59
59
|
|
60
|
+
The project mongo-qa (http://github.com/mongodb/mongo-qa) contains many more
|
61
|
+
Mongo driver tests that are language independent. To run thoses tests as part
|
62
|
+
of the "rake test" task, run
|
63
|
+
|
64
|
+
$ rake mongo_qa
|
65
|
+
$ rake test
|
66
|
+
|
67
|
+
The mongo_qa task uses the "git clone" command to make a copy of that project
|
68
|
+
in a directory named mongo-qa. If the directory already exists, then the
|
69
|
+
mongo_qa task uses "git pull" to updated the code that's there. The Ruby
|
70
|
+
driver tests will then use some of the data files from that project when it
|
71
|
+
runs BSON tests. You can delete this directory at any time if you don't want
|
72
|
+
to run those tests any more.
|
73
|
+
|
74
|
+
Additionally, the script bin/validate is used by the mongo-qa project's
|
75
|
+
validator script.
|
76
|
+
|
60
77
|
|
61
78
|
= Documentation
|
62
79
|
|
data/Rakefile
CHANGED
@@ -7,7 +7,7 @@ require 'rake/gempackagetask'
|
|
7
7
|
require 'rake/contrib/rubyforgepublisher'
|
8
8
|
|
9
9
|
GEM = "mongo"
|
10
|
-
GEM_VERSION = '0.0.
|
10
|
+
GEM_VERSION = '0.0.3'
|
11
11
|
SUMMARY = 'Simple pure-Ruby driver for the 10gen Mongo DB'
|
12
12
|
DESCRIPTION = 'This is a simple pure-Ruby driver for the 10gen Mongo DB. For more information about Mongo, see http://www.mongodb.org.'
|
13
13
|
AUTHOR = 'Jim Menard'
|
@@ -42,6 +42,17 @@ Rake::TestTask.new do |t|
|
|
42
42
|
t.test_files = FileList['tests/test*.rb']
|
43
43
|
end
|
44
44
|
|
45
|
+
desc "Clone or pull (update) the mongo-qa project used for testing"
|
46
|
+
task :mongo_qa do
|
47
|
+
if File.exist?('mongo-qa')
|
48
|
+
Dir.chdir('mongo-qa') do
|
49
|
+
system('git pull')
|
50
|
+
end
|
51
|
+
else
|
52
|
+
system('git clone git://github.com/mongodb/mongo-qa.git')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
45
56
|
desc "Generate documentation"
|
46
57
|
task :rdoc do
|
47
58
|
FileUtils.rm_rf('html')
|
data/bin/validate
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# usage: validate somefile.xson somefile.bson
|
4
|
+
#
|
5
|
+
# Reads somefile.xson file (XML that describes a Mongo-type document),
|
6
|
+
# converts it into a Ruby OrderedHash, runs that through the BSON
|
7
|
+
# serialization code, and writes the BSON bytes to somefile.bson.
|
8
|
+
#
|
9
|
+
# In addition, this script takes the generated BSON, reads it in then writes
|
10
|
+
# it back out to a temp BSON file. If they are different, we report that error
|
11
|
+
# to STDOUT.
|
12
|
+
#
|
13
|
+
# This script is used by the mongo-qa project
|
14
|
+
# (http://github.com/mongodb/mongo-qa).
|
15
|
+
|
16
|
+
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
17
|
+
require 'mongo'
|
18
|
+
require 'mongo/util/xml_to_ruby'
|
19
|
+
|
20
|
+
if ARGV.length < 2
|
21
|
+
$stderr.puts "usage: validate somefile.xson somefile.bson"
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
# Translate the .xson XML into a Ruby object, turn that object into BSON, and
|
26
|
+
# write the BSON to the file as requested.
|
27
|
+
obj = File.open(ARGV[0], 'rb') { |f| XMLToRuby.new.xml_to_ruby(f) }
|
28
|
+
bson = BSON.new.serialize(obj).to_a
|
29
|
+
File.open(ARGV[1], 'wb') { |f| bson.each { |b| f.putc(b) } }
|
30
|
+
|
31
|
+
# Now the additional testing. Read the generated BSON back in, deserialize it,
|
32
|
+
# and re-serialize the results. Compare that BSON with the BSON from the file
|
33
|
+
# we output.
|
34
|
+
bson = File.open(ARGV[1], 'rb') { |f| f.read }
|
35
|
+
bson = if RUBY_VERSION >= '1.9'
|
36
|
+
bson.bytes.to_a
|
37
|
+
else
|
38
|
+
bson.split(//).collect { |c| c[0] }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Turn the Ruby object into BSON bytes and compare with the BSON bytes from
|
42
|
+
# the file.
|
43
|
+
bson_from_ruby = BSON.new.serialize(obj).to_a
|
44
|
+
|
45
|
+
if bson.length != bson_from_ruby.length
|
46
|
+
$stderr.puts "error: round-trip BSON lengths differ when testing #{ARGV[0]}"
|
47
|
+
exit 1
|
48
|
+
elsif bson != bson_from_ruby
|
49
|
+
$stderr.puts "error: round-trip BSON contents differ when testing #{ARGV[0]}"
|
50
|
+
exit 1
|
51
|
+
end
|
data/lib/mongo.rb
CHANGED
data/lib/mongo/binary.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify it
|
5
|
+
# under the terms of the GNU Affero General Public License, version 3, as
|
6
|
+
# published by the Free Software Foundation.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
10
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
11
|
+
# for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
module XGen
|
18
|
+
module Mongo
|
19
|
+
module Driver
|
20
|
+
|
21
|
+
# An array of binary bytes. The only reason this exists is so that the
|
22
|
+
# BSON encoder will know to output the Mongo BINARY type.
|
23
|
+
class Binary < String; end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class String
|
30
|
+
# Convert a string into a XGen::Mongo::Driver::Binary
|
31
|
+
def to_mongo_binary
|
32
|
+
XGen::Mongo::Driver::Binary.new(self)
|
33
|
+
end
|
34
|
+
end
|
data/lib/mongo/cursor.rb
CHANGED
data/lib/mongo/objectid.rb
CHANGED
@@ -47,6 +47,14 @@ module XGen
|
|
47
47
|
MACHINE = ( val = rand(0x1000000); [val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff] )
|
48
48
|
PID = ( val = rand(0x10000); [val & 0xff, (val >> 8) & 0xff]; )
|
49
49
|
|
50
|
+
# The string representation of an OID is different than its internal
|
51
|
+
# and BSON byte representations. The BYTE_ORDER here maps
|
52
|
+
# internal/BSON byte position (the index in BYTE_ORDER) to the
|
53
|
+
# position of the two hex characters representing that byte in the
|
54
|
+
# string representation. For example, the 0th BSON byte corresponds to
|
55
|
+
# the (0-based) 7th pair of hex chars in the string.
|
56
|
+
BYTE_ORDER = [7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 9, 8]
|
57
|
+
|
50
58
|
LOCK = Object.new
|
51
59
|
LOCK.extend Mutex_m
|
52
60
|
|
@@ -57,12 +65,10 @@ module XGen
|
|
57
65
|
# with that value.
|
58
66
|
def self.from_string(str)
|
59
67
|
data = []
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
self.new(data.reverse)
|
68
|
+
BYTE_ORDER.each_with_index { |string_position, data_index|
|
69
|
+
data[data_index] = str[string_position * 2, 2].to_i(16)
|
70
|
+
}
|
71
|
+
self.new(data)
|
66
72
|
end
|
67
73
|
|
68
74
|
# +data+ is an array of bytes. If nil, a new id will be generated.
|
@@ -81,7 +87,11 @@ module XGen
|
|
81
87
|
end
|
82
88
|
|
83
89
|
def to_s
|
84
|
-
|
90
|
+
str = ' ' * 24
|
91
|
+
BYTE_ORDER.each_with_index { |string_position, data_index|
|
92
|
+
str[string_position * 2, 2] = '%02x' % @data[data_index]
|
93
|
+
}
|
94
|
+
str
|
85
95
|
end
|
86
96
|
|
87
97
|
# (Would normally be private, but isn't so we can test it.)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify it
|
5
|
+
# under the terms of the GNU Affero General Public License, version 3, as
|
6
|
+
# published by the Free Software Foundation.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
10
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
11
|
+
# for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
module XGen
|
18
|
+
module Mongo
|
19
|
+
module Driver
|
20
|
+
|
21
|
+
# A special "undefined" type to match Mongo's storage of UNKNOWN values.
|
22
|
+
# "UNKNOWN" comes from JavaScript.
|
23
|
+
#
|
24
|
+
# NOTE: this class does not attempt to provide ANY of the semantics an
|
25
|
+
# "unknown" object might need. It isn't nil, it isn't special in any
|
26
|
+
# way, and there isn't any singleton value.
|
27
|
+
class Undefined < Object; end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/mongo/util/bson.rb
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
15
|
# ++
|
16
16
|
|
17
|
+
require 'base64'
|
17
18
|
require 'mongo/util/byte_buffer'
|
18
19
|
require 'mongo/util/ordered_hash'
|
19
20
|
require 'mongo/objectid'
|
@@ -46,7 +47,9 @@ class BSON
|
|
46
47
|
buf.put_array(val.to_s.unpack("C*") + [0])
|
47
48
|
end
|
48
49
|
|
49
|
-
def initialize
|
50
|
+
def initialize(db=nil)
|
51
|
+
# db is only needed during deserialization when the data contains a DBRef
|
52
|
+
@db = db
|
50
53
|
@buf = ByteBuffer.new
|
51
54
|
end
|
52
55
|
|
@@ -84,7 +87,13 @@ class BSON
|
|
84
87
|
serialize_null_element(@buf, k)
|
85
88
|
when REF
|
86
89
|
serialize_dbref_element(@buf, k, v)
|
87
|
-
when
|
90
|
+
when SYMBOL
|
91
|
+
serialize_symbol_element(@buf, k, v)
|
92
|
+
when BINARY
|
93
|
+
serialize_binary_element(@buf, k, v)
|
94
|
+
when UNDEFINED
|
95
|
+
serialize_undefined_element(@buf, k)
|
96
|
+
when CODE_W_SCOPE
|
88
97
|
# TODO
|
89
98
|
raise "unimplemented type #{type}"
|
90
99
|
else
|
@@ -96,13 +105,13 @@ class BSON
|
|
96
105
|
self
|
97
106
|
end
|
98
107
|
|
99
|
-
def deserialize(buf=nil)
|
108
|
+
def deserialize(buf=nil, parent=nil)
|
100
109
|
# If buf is nil, use @buf, assumed to contain already-serialized BSON.
|
101
110
|
# This is only true during testing.
|
102
111
|
@buf = ByteBuffer.new(buf.to_a) if buf
|
103
112
|
@buf.rewind
|
104
113
|
@buf.get_int # eat message size
|
105
|
-
doc =
|
114
|
+
doc = OrderedHash.new
|
106
115
|
while @buf.more?
|
107
116
|
type = @buf.get
|
108
117
|
case type
|
@@ -120,13 +129,13 @@ class BSON
|
|
120
129
|
doc[key] = deserialize_oid_data(@buf)
|
121
130
|
when ARRAY
|
122
131
|
key = deserialize_cstr(@buf)
|
123
|
-
doc[key] = deserialize_array_data(@buf)
|
132
|
+
doc[key] = deserialize_array_data(@buf, doc)
|
124
133
|
when REGEX
|
125
134
|
key = deserialize_cstr(@buf)
|
126
135
|
doc[key] = deserialize_regex_data(@buf)
|
127
136
|
when OBJECT
|
128
137
|
key = deserialize_cstr(@buf)
|
129
|
-
doc[key] = deserialize_object_data(@buf)
|
138
|
+
doc[key] = deserialize_object_data(@buf, doc)
|
130
139
|
when BOOLEAN
|
131
140
|
key = deserialize_cstr(@buf)
|
132
141
|
doc[key] = deserialize_boolean_data(@buf)
|
@@ -136,10 +145,19 @@ class BSON
|
|
136
145
|
when NULL
|
137
146
|
key = deserialize_cstr(@buf)
|
138
147
|
doc[key] = nil
|
148
|
+
when UNDEFINED
|
149
|
+
key = deserialize_cstr(@buf)
|
150
|
+
doc[key] = XGen::Mongo::Driver::Undefined.new
|
139
151
|
when REF
|
140
152
|
key = deserialize_cstr(@buf)
|
141
|
-
doc[key] = deserialize_dbref_data(@buf)
|
142
|
-
when
|
153
|
+
doc[key] = deserialize_dbref_data(@buf, key, parent)
|
154
|
+
when SYMBOL
|
155
|
+
key = deserialize_cstr(@buf)
|
156
|
+
doc[key] = deserialize_symbol_data(@buf)
|
157
|
+
when BINARY
|
158
|
+
key = deserialize_cstr(@buf)
|
159
|
+
doc[key] = deserialize_binary_data(@buf)
|
160
|
+
when CODE_W_SCOPE
|
143
161
|
# TODO
|
144
162
|
raise "unimplemented type #{type}"
|
145
163
|
when EOO
|
@@ -183,14 +201,14 @@ class BSON
|
|
183
201
|
buf.get_int
|
184
202
|
end
|
185
203
|
|
186
|
-
def deserialize_object_data(buf)
|
204
|
+
def deserialize_object_data(buf, parent)
|
187
205
|
size = buf.get_int
|
188
206
|
buf.position -= 4
|
189
|
-
BSON.new.deserialize(buf.get(size))
|
207
|
+
BSON.new(@db).deserialize(buf.get(size), parent)
|
190
208
|
end
|
191
209
|
|
192
|
-
def deserialize_array_data(buf)
|
193
|
-
h = deserialize_object_data(buf)
|
210
|
+
def deserialize_array_data(buf, parent)
|
211
|
+
h = deserialize_object_data(buf, parent)
|
194
212
|
a = []
|
195
213
|
h.each { |k, v| a[k.to_i] = v }
|
196
214
|
a
|
@@ -216,12 +234,22 @@ class BSON
|
|
216
234
|
XGen::Mongo::Driver::ObjectID.new(buf.get(12))
|
217
235
|
end
|
218
236
|
|
219
|
-
def deserialize_dbref_data(buf)
|
237
|
+
def deserialize_dbref_data(buf, key, parent)
|
220
238
|
ns = deserialize_cstr(buf)
|
221
239
|
oid = deserialize_oid_data(buf)
|
222
|
-
|
223
|
-
|
224
|
-
|
240
|
+
XGen::Mongo::Driver::DBRef.new(parent, key, @db, ns, oid)
|
241
|
+
end
|
242
|
+
|
243
|
+
def deserialize_symbol_data(buf)
|
244
|
+
deserialize_cstr(buf).intern
|
245
|
+
end
|
246
|
+
|
247
|
+
def deserialize_binary_data(buf)
|
248
|
+
len = buf.get_int
|
249
|
+
bytes = buf.get(len)
|
250
|
+
str = ''
|
251
|
+
bytes.each { |c| str << c.chr }
|
252
|
+
str.to_mongo_binary
|
225
253
|
end
|
226
254
|
|
227
255
|
def serialize_eoo_element(buf)
|
@@ -240,6 +268,31 @@ class BSON
|
|
240
268
|
buf.put_array(val.object_id.to_a)
|
241
269
|
end
|
242
270
|
|
271
|
+
def serialize_symbol_element(buf, key, val)
|
272
|
+
buf.put(SYMBOL)
|
273
|
+
self.class.serialize_cstr(buf, key)
|
274
|
+
self.class.serialize_cstr(buf, val)
|
275
|
+
end
|
276
|
+
|
277
|
+
def serialize_binary_element(buf, key, val)
|
278
|
+
buf.put(BINARY)
|
279
|
+
self.class.serialize_cstr(buf, key)
|
280
|
+
buf.put_int(val.length)
|
281
|
+
bytes = if RUBY_VERSION >= '1.9'
|
282
|
+
val.bytes.to_a
|
283
|
+
else
|
284
|
+
a = []
|
285
|
+
val.each_byte { |byte| a << byte }
|
286
|
+
a
|
287
|
+
end
|
288
|
+
buf.put_array(bytes)
|
289
|
+
end
|
290
|
+
|
291
|
+
def serialize_undefined_element(buf, key)
|
292
|
+
buf.put(UNDEFINED)
|
293
|
+
self.class.serialize_cstr(buf, key)
|
294
|
+
end
|
295
|
+
|
243
296
|
def serialize_boolean_element(buf, key, val)
|
244
297
|
buf.put(BOOLEAN)
|
245
298
|
self.class.serialize_cstr(buf, key)
|
@@ -337,6 +390,8 @@ class BSON
|
|
337
390
|
NUMBER_INT
|
338
391
|
when Numeric
|
339
392
|
NUMBER
|
393
|
+
when XGen::Mongo::Driver::Binary # must be before String
|
394
|
+
BINARY
|
340
395
|
when String
|
341
396
|
# magic awful stuff - the DB requires that a where clause is sent as CODE
|
342
397
|
key == "$where" ? CODE : STRING
|
@@ -354,6 +409,10 @@ class BSON
|
|
354
409
|
DATE
|
355
410
|
when Hash
|
356
411
|
OBJECT
|
412
|
+
when Symbol
|
413
|
+
SYMBOL
|
414
|
+
when XGen::Mongo::Driver::Undefined
|
415
|
+
UNDEFINED
|
357
416
|
else
|
358
417
|
raise "Unknown type of object: #{o.class.name}"
|
359
418
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify it
|
5
|
+
# under the terms of the GNU Affero General Public License, version 3, as
|
6
|
+
# published by the Free Software Foundation.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
10
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
11
|
+
# for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
require 'rexml/document'
|
18
|
+
require 'mongo'
|
19
|
+
|
20
|
+
# Converts a .xson file (an XML file that describes a Mongo-type document) to
|
21
|
+
# an OrderedHash.
|
22
|
+
class XMLToRuby
|
23
|
+
|
24
|
+
include XGen::Mongo::Driver
|
25
|
+
|
26
|
+
def xml_to_ruby(io)
|
27
|
+
doc = REXML::Document.new(io)
|
28
|
+
doc_to_ruby(doc.root.elements['doc'])
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def element_to_ruby(e)
|
34
|
+
type = e.name
|
35
|
+
child = e.elements[1]
|
36
|
+
case type
|
37
|
+
when 'oid'
|
38
|
+
ObjectID.from_string(e.text)
|
39
|
+
when 'ref'
|
40
|
+
dbref_to_ruby(e.elements)
|
41
|
+
when 'int'
|
42
|
+
e.text.to_i
|
43
|
+
when 'number'
|
44
|
+
e.text.to_f
|
45
|
+
when 'string', 'code'
|
46
|
+
e.text.to_s
|
47
|
+
when 'binary'
|
48
|
+
Base64.decode64(e.text.to_s).to_mongo_binary
|
49
|
+
when 'symbol'
|
50
|
+
e.text.to_s.intern
|
51
|
+
when 'boolean'
|
52
|
+
e.text.to_s == 'true'
|
53
|
+
when 'array'
|
54
|
+
array_to_ruby(e.elements)
|
55
|
+
when 'date'
|
56
|
+
Time.at(e.text.to_f / 1000.0)
|
57
|
+
when 'regex'
|
58
|
+
regex_to_ruby(e.elements)
|
59
|
+
when 'null'
|
60
|
+
nil
|
61
|
+
when 'undefined'
|
62
|
+
Undefined.new
|
63
|
+
when 'doc'
|
64
|
+
doc_to_ruby(e)
|
65
|
+
else
|
66
|
+
raise "Unknown type #{type} in element with name #{e.attributes['name']}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def doc_to_ruby(element)
|
71
|
+
oh = OrderedHash.new
|
72
|
+
element.elements.each { |e| oh[e.attributes['name']] = element_to_ruby(e) }
|
73
|
+
oh
|
74
|
+
end
|
75
|
+
|
76
|
+
def array_to_ruby(elements)
|
77
|
+
a = []
|
78
|
+
elements.each { |e|
|
79
|
+
index_str = e.attributes['name']
|
80
|
+
a[index_str.to_i] = element_to_ruby(e)
|
81
|
+
}
|
82
|
+
a
|
83
|
+
end
|
84
|
+
|
85
|
+
def regex_to_ruby(elements)
|
86
|
+
pattern = elements['pattern'].text
|
87
|
+
options_str = elements['options'].text || ''
|
88
|
+
|
89
|
+
options = 0
|
90
|
+
options |= Regexp::IGNORECASE if options_str.include?('i')
|
91
|
+
options |= Regexp::MULTILINE if options_str.include?('m')
|
92
|
+
options |= Regexp::EXTENDED if options_str.include?('x')
|
93
|
+
Regexp.new(pattern, options)
|
94
|
+
end
|
95
|
+
|
96
|
+
def dbref_to_ruby(elements)
|
97
|
+
ns = elements['ns'].text
|
98
|
+
oid_str = elements['oid'].text
|
99
|
+
DBRef.new(nil, nil, nil, ns, ObjectID.from_string(oid_str))
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
data/tests/test_bson.rb
CHANGED
@@ -8,6 +8,9 @@ class BSONTest < Test::Unit::TestCase
|
|
8
8
|
include XGen::Mongo::Driver
|
9
9
|
|
10
10
|
def setup
|
11
|
+
# We don't pass a DB to the constructor, even though we are about to test
|
12
|
+
# deserialization. This means that when we deserialize, any DBRefs will
|
13
|
+
# have nil @db ivars. That's fine for now.
|
11
14
|
@b = BSON.new
|
12
15
|
end
|
13
16
|
|
@@ -79,11 +82,36 @@ class BSONTest < Test::Unit::TestCase
|
|
79
82
|
|
80
83
|
def test_dbref
|
81
84
|
oid = ObjectID.new
|
82
|
-
doc = {
|
85
|
+
doc = {}
|
86
|
+
doc['dbref'] = DBRef.new(doc, 'dbref', nil, 'namespace', oid)
|
83
87
|
@b.serialize(doc)
|
84
88
|
doc2 = @b.deserialize
|
85
89
|
assert_equal 'namespace', doc2['dbref'].namespace
|
86
90
|
assert_equal oid, doc2['dbref'].object_id
|
87
91
|
end
|
88
92
|
|
93
|
+
def test_symbol
|
94
|
+
doc = {'sym' => :foo}
|
95
|
+
@b.serialize(doc)
|
96
|
+
doc2 = @b.deserialize
|
97
|
+
assert_equal :foo, doc2['sym']
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_binary
|
101
|
+
bin = 'binstring'.to_mongo_binary
|
102
|
+
assert_kind_of Binary, bin
|
103
|
+
|
104
|
+
doc = {'bin' => bin}
|
105
|
+
@b.serialize(doc)
|
106
|
+
doc2 = @b.deserialize
|
107
|
+
assert_equal 'binstring', doc2['bin']
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_undefined
|
111
|
+
doc = {'undef' => Undefined.new}
|
112
|
+
@b.serialize(doc)
|
113
|
+
doc2 = @b.deserialize
|
114
|
+
assert_kind_of Undefined, doc2['undef']
|
115
|
+
end
|
116
|
+
|
89
117
|
end
|
data/tests/test_objectid.rb
CHANGED
@@ -73,4 +73,16 @@ class ObjectIDTest < Test::Unit::TestCase
|
|
73
73
|
assert_equal @o.to_s, o2.to_s
|
74
74
|
end
|
75
75
|
|
76
|
+
def test_from_string_leading_zeroes
|
77
|
+
hex_str = '000000000000000000abcdef'
|
78
|
+
o = ObjectID.from_string(hex_str)
|
79
|
+
assert_equal hex_str, o.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_byte_order
|
83
|
+
hex_str = '000102030405060708090A0B'
|
84
|
+
o = ObjectID.from_string(hex_str)
|
85
|
+
assert_equal [0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0b, 0x0a, 0x09, 0x08], o.to_a
|
86
|
+
end
|
87
|
+
|
76
88
|
end
|
data/tests/test_round_trip.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
HERE = File.dirname(__FILE__)
|
2
2
|
$LOAD_PATH[0,0] = File.join(HERE, '..', 'lib')
|
3
3
|
require 'mongo'
|
4
|
-
require '
|
4
|
+
require 'mongo/util/xml_to_ruby'
|
5
5
|
require 'test/unit'
|
6
6
|
|
7
7
|
# For each xml/bson file in the data subdirectory, we turn the XML into an
|
8
8
|
# OrderedHash and then test both Ruby-to-BSON and BSON-to-Ruby translations.
|
9
|
+
#
|
10
|
+
# There is a whole other project that includes similar tests
|
11
|
+
# (http://github.com/mongodb/mongo-qa). If the directory ../mongo-qa exists,
|
12
|
+
# then we find the BSON test files there and use those, too. Use the Rake task
|
13
|
+
# "mongo_qa" to obtain those tests.
|
9
14
|
class RoundTripTest < Test::Unit::TestCase
|
10
15
|
|
11
16
|
include XGen::Mongo::Driver
|
@@ -16,117 +21,96 @@ class RoundTripTest < Test::Unit::TestCase
|
|
16
21
|
unless @@ruby
|
17
22
|
names = Dir[File.join(HERE, 'data', '*.xml')].collect {|f| File.basename(f).sub(/\.xml$/, '') }
|
18
23
|
@@ruby = {}
|
19
|
-
names.each { |name|
|
24
|
+
names.each { |name|
|
25
|
+
File.open(File.join(HERE, 'data', "#{name}.xml")) { |f|
|
26
|
+
@@ruby[name] = XMLToRuby.new.xml_to_ruby(f)
|
27
|
+
}
|
28
|
+
}
|
20
29
|
end
|
21
30
|
end
|
22
31
|
|
23
|
-
def
|
24
|
-
|
25
|
-
doc = REXML::Document.new(f)
|
26
|
-
doc_to_ruby(doc.root.elements['doc'])
|
27
|
-
}
|
28
|
-
end
|
29
|
-
|
30
|
-
def element_to_ruby(e)
|
31
|
-
type = e.name
|
32
|
-
child = e.elements[1]
|
33
|
-
case type
|
34
|
-
when 'oid'
|
35
|
-
ObjectID.from_string(e.text)
|
36
|
-
when 'ref'
|
37
|
-
dbref_to_ruby(e.elements)
|
38
|
-
when 'int'
|
39
|
-
e.text.to_i
|
40
|
-
when 'number'
|
41
|
-
e.text.to_f
|
42
|
-
when 'string', 'code'
|
43
|
-
e.text.to_s
|
44
|
-
when 'boolean'
|
45
|
-
e.text.to_s == 'true'
|
46
|
-
when 'array'
|
47
|
-
array_to_ruby(e.elements)
|
48
|
-
when 'date'
|
49
|
-
Time.at(e.text.to_f / 1000.0)
|
50
|
-
when 'regex'
|
51
|
-
regex_to_ruby(e.elements)
|
52
|
-
when 'null'
|
53
|
-
nil
|
54
|
-
when 'doc'
|
55
|
-
doc_to_ruby(e)
|
56
|
-
else
|
57
|
-
raise "Unknown type #{type} in element with name #{e.attributes['name']}"
|
58
|
-
end
|
32
|
+
def test_dummy
|
33
|
+
assert true
|
59
34
|
end
|
60
35
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
|
36
|
+
def self.create_test_for_round_trip_files_in_dir(dir)
|
37
|
+
names = Dir[File.join(dir, '*.xson')].collect {|f| File.basename(f).sub(/\.xson$/, '') }
|
38
|
+
names.each { |name|
|
39
|
+
eval <<EOS
|
40
|
+
def test_#{name}_#{dir.gsub(/[^a-zA-Z0-9_]/, '_')}
|
41
|
+
one_round_trip("#{dir}", "#{name}")
|
42
|
+
end
|
43
|
+
EOS
|
44
|
+
}
|
65
45
|
end
|
66
46
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
47
|
+
# Dynamically generate one test for each test file. This way, if one test
|
48
|
+
# fails the others will still run.
|
49
|
+
create_test_for_round_trip_files_in_dir(File.join(HERE, 'data'))
|
50
|
+
mongo_qa_dir = File.join(HERE, '..', 'mongo-qa/modules/bson_tests')
|
51
|
+
if File.exist?(mongo_qa_dir)
|
52
|
+
%w(basic_types complex single_types).each { |subdir_name|
|
53
|
+
create_test_for_round_trip_files_in_dir(File.join(mongo_qa_dir, subdir_name))
|
72
54
|
}
|
73
|
-
a
|
74
55
|
end
|
75
56
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
57
|
+
# Round-trip comparisons of Ruby-to-BSON and back.
|
58
|
+
# * Take the objects that were read from XML
|
59
|
+
# * Turn them into BSON bytes
|
60
|
+
# * Compare that with the BSON files we have
|
61
|
+
# * Turn those BSON bytes back in to Ruby objects
|
62
|
+
# * Turn them back into BSON bytes
|
63
|
+
# * Compare that with the BSON files we have (or the bytes that were already
|
64
|
+
# generated)
|
65
|
+
def one_round_trip(dir, name)
|
66
|
+
obj = File.open(File.join(dir, "#{name}.xson")) { |f|
|
67
|
+
XMLToRuby.new.xml_to_ruby(f)
|
68
|
+
}
|
86
69
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
70
|
+
File.open(File.join(dir, "#{name}.bson"), 'r') { |f|
|
71
|
+
# Read the BSON from the file
|
72
|
+
bson = f.read
|
73
|
+
bson = if RUBY_VERSION >= '1.9'
|
74
|
+
bson.bytes.to_a
|
75
|
+
else
|
76
|
+
bson.split(//).collect { |c| c[0] }
|
77
|
+
end
|
92
78
|
|
93
|
-
#
|
94
|
-
#
|
95
|
-
|
96
|
-
# $stderr.puts "@@ruby['smorgasbord'] = #{@@ruby['smorgasbord'].inspect.gsub(/, /, ",\n")}" # DEBUG
|
97
|
-
# end
|
79
|
+
# Turn the Ruby object into BSON bytes and compare with the BSON bytes
|
80
|
+
# from the file.
|
81
|
+
bson_from_ruby = BSON.new.serialize(obj).to_a
|
98
82
|
|
99
|
-
|
100
|
-
@@ruby.each { |name, obj|
|
101
|
-
File.open(File.join(HERE, 'data', "#{name}.bson"), 'r') { |f|
|
102
|
-
bson = f.read
|
103
|
-
bson = if RUBY_VERSION >= '1.9'
|
104
|
-
bson.bytes
|
105
|
-
else
|
106
|
-
bson.split(//).collect { |c| c[0] }
|
107
|
-
end
|
108
|
-
bson_from_ruby = BSON.new.serialize(obj).to_a
|
83
|
+
begin
|
109
84
|
assert_equal bson.length, bson_from_ruby.length
|
110
85
|
assert_equal bson, bson_from_ruby
|
111
|
-
|
86
|
+
rescue => ex
|
87
|
+
# File.open(File.join(dir, "#{name}_out_a.bson"), 'wb') { |f| # DEBUG
|
88
|
+
# bson_from_ruby.each { |b| f.putc(b) }
|
89
|
+
# }
|
90
|
+
raise ex
|
91
|
+
end
|
92
|
+
|
93
|
+
# Turn those BSON bytes back into a Ruby object.
|
94
|
+
#
|
95
|
+
# We're passing a nil db to the contructor here, but that's OK because
|
96
|
+
# the BSON DBFef bytes don't contain the db object in any case, and we
|
97
|
+
# don't care what the database is.
|
98
|
+
obj_from_bson = BSON.new(nil).deserialize(ByteBuffer.new(bson_from_ruby))
|
99
|
+
assert_kind_of OrderedHash, obj_from_bson
|
100
|
+
|
101
|
+
# Turn that Ruby object into BSON and compare it to the original BSON
|
102
|
+
# bytes.
|
103
|
+
bson_from_ruby = BSON.new.serialize(obj_from_bson).to_a
|
104
|
+
begin
|
105
|
+
assert_equal bson.length, bson_from_ruby.length
|
106
|
+
assert_equal bson, bson_from_ruby
|
107
|
+
rescue => ex
|
108
|
+
# File.open(File.join(dir, "#{name}_out_b.bson"), 'wb') { |f| # DEBUG
|
109
|
+
# bson_from_ruby.each { |b| f.putc(b) }
|
110
|
+
# }
|
111
|
+
raise ex
|
112
|
+
end
|
112
113
|
}
|
113
114
|
end
|
114
115
|
|
115
|
-
# def test_bson_to_ruby
|
116
|
-
# @@ruby.each { |name, obj|
|
117
|
-
# File.open(File.join(HERE, 'data', "#{name}.bson"), 'r') { |f|
|
118
|
-
# bson = f.read
|
119
|
-
# obj_from_bson = BSON.new.deserialize(ByteBuffer.new(bson.split(//)))
|
120
|
-
# assert_equal obj, obj_from_bson
|
121
|
-
# }
|
122
|
-
# }
|
123
|
-
# end
|
124
|
-
|
125
|
-
# # Technically reduntant, given the other tests
|
126
|
-
# def test_roundtrips
|
127
|
-
# @@ruby.each { |name, obj|
|
128
|
-
|
129
|
-
# }
|
130
|
-
# end
|
131
|
-
|
132
116
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Menard
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
12
|
+
date: 2009-01-12 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -23,7 +23,9 @@ extra_rdoc_files: []
|
|
23
23
|
|
24
24
|
files:
|
25
25
|
- bin/mongo_console
|
26
|
+
- bin/validate
|
26
27
|
- lib/mongo/admin.rb
|
28
|
+
- lib/mongo/binary.rb
|
27
29
|
- lib/mongo/collection.rb
|
28
30
|
- lib/mongo/cursor.rb
|
29
31
|
- lib/mongo/db.rb
|
@@ -42,9 +44,11 @@ files:
|
|
42
44
|
- lib/mongo/mongo.rb
|
43
45
|
- lib/mongo/objectid.rb
|
44
46
|
- lib/mongo/query.rb
|
47
|
+
- lib/mongo/undefined.rb
|
45
48
|
- lib/mongo/util/bson.rb
|
46
49
|
- lib/mongo/util/byte_buffer.rb
|
47
50
|
- lib/mongo/util/ordered_hash.rb
|
51
|
+
- lib/mongo/util/xml_to_ruby.rb
|
48
52
|
- lib/mongo.rb
|
49
53
|
- tests/test_admin.rb
|
50
54
|
- tests/test_bson.rb
|