mongo 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/lib/mongo.rb +1 -0
- data/lib/mongo/dbref.rb +37 -0
- data/lib/mongo/objectid.rb +12 -0
- data/lib/mongo/util/bson.rb +38 -15
- data/lib/mongo/util/ordered_hash.rb +6 -0
- data/tests/test_bson.rb +10 -0
- data/tests/test_objectid.rb +8 -0
- data/tests/test_ordered_hash.rb +4 -0
- data/tests/test_round_trip.rb +132 -0
- metadata +4 -2
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.2'
|
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'
|
data/lib/mongo.rb
CHANGED
data/lib/mongo/dbref.rb
ADDED
@@ -0,0 +1,37 @@
|
|
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
|
+
class DBRef
|
22
|
+
|
23
|
+
attr_reader :parent, :field_name, :db, :namespace, :object_id
|
24
|
+
|
25
|
+
def initialize(parent, field_name, db, namespace, object_id)
|
26
|
+
@parent, @field_name, @db, @namespace, @object_id =
|
27
|
+
parent, field_name, db, namespace, object_id
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
"ns: #{namespace}, id: #{object_id}"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/mongo/objectid.rb
CHANGED
@@ -53,6 +53,18 @@ module XGen
|
|
53
53
|
@@index_time = Time.new.to_i
|
54
54
|
@@index = 0
|
55
55
|
|
56
|
+
# Given a string representation of an ObjectID, return a new ObjectID
|
57
|
+
# with that value.
|
58
|
+
def self.from_string(str)
|
59
|
+
data = []
|
60
|
+
i = str.to_i(16)
|
61
|
+
while i > 0
|
62
|
+
data << (i & 0xff)
|
63
|
+
i >>= 8
|
64
|
+
end
|
65
|
+
self.new(data.reverse)
|
66
|
+
end
|
67
|
+
|
56
68
|
# +data+ is an array of bytes. If nil, a new id will be generated.
|
57
69
|
# The time +t+ is only used for testing; leave it nil.
|
58
70
|
def initialize(data=nil, t=nil)
|
data/lib/mongo/util/bson.rb
CHANGED
@@ -17,6 +17,7 @@
|
|
17
17
|
require 'mongo/util/byte_buffer'
|
18
18
|
require 'mongo/util/ordered_hash'
|
19
19
|
require 'mongo/objectid'
|
20
|
+
require 'mongo/dbref'
|
20
21
|
|
21
22
|
# A BSON seralizer/deserializer.
|
22
23
|
class BSON
|
@@ -81,7 +82,9 @@ class BSON
|
|
81
82
|
serialize_date_element(@buf, k, v)
|
82
83
|
when NULL
|
83
84
|
serialize_null_element(@buf, k)
|
84
|
-
when
|
85
|
+
when REF
|
86
|
+
serialize_dbref_element(@buf, k, v)
|
87
|
+
when BINARY, UNDEFINED, SYMBOL, CODE_W_SCOPE
|
85
88
|
# TODO
|
86
89
|
raise "unimplemented type #{type}"
|
87
90
|
else
|
@@ -104,36 +107,39 @@ class BSON
|
|
104
107
|
type = @buf.get
|
105
108
|
case type
|
106
109
|
when STRING, CODE
|
107
|
-
key =
|
110
|
+
key = deserialize_cstr(@buf)
|
108
111
|
doc[key] = deserialize_string_data(@buf)
|
109
112
|
when NUMBER
|
110
|
-
key =
|
113
|
+
key = deserialize_cstr(@buf)
|
111
114
|
doc[key] = deserialize_number_data(@buf)
|
112
115
|
when NUMBER_INT
|
113
|
-
key =
|
116
|
+
key = deserialize_cstr(@buf)
|
114
117
|
doc[key] = deserialize_number_int_data(@buf)
|
115
118
|
when OID
|
116
|
-
key =
|
119
|
+
key = deserialize_cstr(@buf)
|
117
120
|
doc[key] = deserialize_oid_data(@buf)
|
118
121
|
when ARRAY
|
119
|
-
key =
|
122
|
+
key = deserialize_cstr(@buf)
|
120
123
|
doc[key] = deserialize_array_data(@buf)
|
121
124
|
when REGEX
|
122
|
-
key =
|
125
|
+
key = deserialize_cstr(@buf)
|
123
126
|
doc[key] = deserialize_regex_data(@buf)
|
124
127
|
when OBJECT
|
125
|
-
key =
|
128
|
+
key = deserialize_cstr(@buf)
|
126
129
|
doc[key] = deserialize_object_data(@buf)
|
127
130
|
when BOOLEAN
|
128
|
-
key =
|
131
|
+
key = deserialize_cstr(@buf)
|
129
132
|
doc[key] = deserialize_boolean_data(@buf)
|
130
133
|
when DATE
|
131
|
-
key =
|
134
|
+
key = deserialize_cstr(@buf)
|
132
135
|
doc[key] = deserialize_date_data(@buf)
|
133
136
|
when NULL
|
134
|
-
key =
|
137
|
+
key = deserialize_cstr(@buf)
|
135
138
|
doc[key] = nil
|
136
|
-
when
|
139
|
+
when REF
|
140
|
+
key = deserialize_cstr(@buf)
|
141
|
+
doc[key] = deserialize_dbref_data(@buf)
|
142
|
+
when BINARY, UNDEFINED, SYMBOL, CODE_W_SCOPE
|
137
143
|
# TODO
|
138
144
|
raise "unimplemented type #{type}"
|
139
145
|
when EOO
|
@@ -191,8 +197,8 @@ class BSON
|
|
191
197
|
end
|
192
198
|
|
193
199
|
def deserialize_regex_data(buf)
|
194
|
-
str =
|
195
|
-
options_str =
|
200
|
+
str = deserialize_cstr(buf)
|
201
|
+
options_str = deserialize_cstr(buf)
|
196
202
|
options = 0
|
197
203
|
options |= Regexp::IGNORECASE if options_str.include?('i')
|
198
204
|
options |= Regexp::MULTILINE if options_str.include?('m')
|
@@ -210,6 +216,14 @@ class BSON
|
|
210
216
|
XGen::Mongo::Driver::ObjectID.new(buf.get(12))
|
211
217
|
end
|
212
218
|
|
219
|
+
def deserialize_dbref_data(buf)
|
220
|
+
ns = deserialize_cstr(buf)
|
221
|
+
oid = deserialize_oid_data(buf)
|
222
|
+
# TODO fix parent, field_name, db of DBRef. Does that need to be done here
|
223
|
+
# or by the caller?
|
224
|
+
XGen::Mongo::Driver::DBRef.new(nil, nil, nil, ns, oid)
|
225
|
+
end
|
226
|
+
|
213
227
|
def serialize_eoo_element(buf)
|
214
228
|
buf.put(EOO)
|
215
229
|
end
|
@@ -219,6 +233,13 @@ class BSON
|
|
219
233
|
self.class.serialize_cstr(buf, key)
|
220
234
|
end
|
221
235
|
|
236
|
+
def serialize_dbref_element(buf, key, val)
|
237
|
+
buf.put(REF)
|
238
|
+
self.class.serialize_cstr(buf, key)
|
239
|
+
self.class.serialize_cstr(buf, val.namespace)
|
240
|
+
buf.put_array(val.object_id.to_a)
|
241
|
+
end
|
242
|
+
|
222
243
|
def serialize_boolean_element(buf, key, val)
|
223
244
|
buf.put(BOOLEAN)
|
224
245
|
self.class.serialize_cstr(buf, key)
|
@@ -298,7 +319,7 @@ class BSON
|
|
298
319
|
buf.position = end_pos
|
299
320
|
end
|
300
321
|
|
301
|
-
def
|
322
|
+
def deserialize_cstr(buf)
|
302
323
|
chars = ""
|
303
324
|
while 1
|
304
325
|
b = buf.get
|
@@ -325,6 +346,8 @@ class BSON
|
|
325
346
|
REGEX
|
326
347
|
when XGen::Mongo::Driver::ObjectID
|
327
348
|
OID
|
349
|
+
when XGen::Mongo::Driver::DBRef
|
350
|
+
REF
|
328
351
|
when true, false
|
329
352
|
BOOLEAN
|
330
353
|
when Time
|
data/tests/test_bson.rb
CHANGED
@@ -76,4 +76,14 @@ class BSONTest < Test::Unit::TestCase
|
|
76
76
|
|
77
77
|
def test_null
|
78
78
|
end
|
79
|
+
|
80
|
+
def test_dbref
|
81
|
+
oid = ObjectID.new
|
82
|
+
doc = {'dbref' => DBRef.new(nil, nil, nil, 'namespace', oid)}
|
83
|
+
@b.serialize(doc)
|
84
|
+
doc2 = @b.deserialize
|
85
|
+
assert_equal 'namespace', doc2['dbref'].namespace
|
86
|
+
assert_equal oid, doc2['dbref'].object_id
|
87
|
+
end
|
88
|
+
|
79
89
|
end
|
data/tests/test_objectid.rb
CHANGED
@@ -65,4 +65,12 @@ class ObjectIDTest < Test::Unit::TestCase
|
|
65
65
|
assert_equal @o, row['_id']
|
66
66
|
end
|
67
67
|
|
68
|
+
def test_from_string
|
69
|
+
hex_str = @o.to_s
|
70
|
+
o2 = ObjectID.from_string(hex_str)
|
71
|
+
assert_equal hex_str, o2.to_s
|
72
|
+
assert_equal @o, o2
|
73
|
+
assert_equal @o.to_s, o2.to_s
|
74
|
+
end
|
75
|
+
|
68
76
|
end
|
data/tests/test_ordered_hash.rb
CHANGED
@@ -0,0 +1,132 @@
|
|
1
|
+
HERE = File.dirname(__FILE__)
|
2
|
+
$LOAD_PATH[0,0] = File.join(HERE, '..', 'lib')
|
3
|
+
require 'mongo'
|
4
|
+
require 'rexml/document'
|
5
|
+
require 'test/unit'
|
6
|
+
|
7
|
+
# For each xml/bson file in the data subdirectory, we turn the XML into an
|
8
|
+
# OrderedHash and then test both Ruby-to-BSON and BSON-to-Ruby translations.
|
9
|
+
class RoundTripTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
include XGen::Mongo::Driver
|
12
|
+
|
13
|
+
@@ruby = nil
|
14
|
+
|
15
|
+
def setup
|
16
|
+
unless @@ruby
|
17
|
+
names = Dir[File.join(HERE, 'data', '*.xml')].collect {|f| File.basename(f).sub(/\.xml$/, '') }
|
18
|
+
@@ruby = {}
|
19
|
+
names.each { |name| @@ruby[name] = xml_to_ruby(name) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def xml_to_ruby(name)
|
24
|
+
File.open(File.join(HERE, 'data', "#{name}.xml")) { |f|
|
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
|
59
|
+
end
|
60
|
+
|
61
|
+
def doc_to_ruby(element)
|
62
|
+
oh = OrderedHash.new
|
63
|
+
element.elements.each { |e| oh[e.attributes['name']] = element_to_ruby(e) }
|
64
|
+
oh
|
65
|
+
end
|
66
|
+
|
67
|
+
def array_to_ruby(elements)
|
68
|
+
a = []
|
69
|
+
elements.each { |e|
|
70
|
+
index_str = e.attributes['name']
|
71
|
+
a[index_str.to_i] = element_to_ruby(e)
|
72
|
+
}
|
73
|
+
a
|
74
|
+
end
|
75
|
+
|
76
|
+
def regex_to_ruby(elements)
|
77
|
+
pattern = elements['pattern'].text
|
78
|
+
options_str = elements['options'].text || ''
|
79
|
+
|
80
|
+
options = 0
|
81
|
+
options |= Regexp::IGNORECASE if options_str.include?('i')
|
82
|
+
options |= Regexp::MULTILINE if options_str.include?('m')
|
83
|
+
options |= Regexp::EXTENDED if options_str.include?('x')
|
84
|
+
Regexp.new(pattern, options)
|
85
|
+
end
|
86
|
+
|
87
|
+
def dbref_to_ruby(elements)
|
88
|
+
ns = elements['ns'].text
|
89
|
+
oid_str = elements['oid'].text
|
90
|
+
DBRef.new(nil, nil, nil, ns, ObjectID.from_string(oid_str))
|
91
|
+
end
|
92
|
+
|
93
|
+
# DEBUG
|
94
|
+
# def test_foo
|
95
|
+
# $stderr.puts "#{@@ruby['smorgasbord'].class.name}" # DEBUG
|
96
|
+
# $stderr.puts "@@ruby['smorgasbord'] = #{@@ruby['smorgasbord'].inspect.gsub(/, /, ",\n")}" # DEBUG
|
97
|
+
# end
|
98
|
+
|
99
|
+
def test_ruby_to_bson
|
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
|
109
|
+
assert_equal bson.length, bson_from_ruby.length
|
110
|
+
assert_equal bson, bson_from_ruby
|
111
|
+
}
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
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
|
+
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.2
|
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-09 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -27,6 +27,7 @@ files:
|
|
27
27
|
- lib/mongo/collection.rb
|
28
28
|
- lib/mongo/cursor.rb
|
29
29
|
- lib/mongo/db.rb
|
30
|
+
- lib/mongo/dbref.rb
|
30
31
|
- lib/mongo/message/get_more_message.rb
|
31
32
|
- lib/mongo/message/insert_message.rb
|
32
33
|
- lib/mongo/message/kill_cursors_message.rb
|
@@ -53,6 +54,7 @@ files:
|
|
53
54
|
- tests/test_message.rb
|
54
55
|
- tests/test_objectid.rb
|
55
56
|
- tests/test_ordered_hash.rb
|
57
|
+
- tests/test_round_trip.rb
|
56
58
|
- Rakefile
|
57
59
|
- README.rdoc
|
58
60
|
has_rdoc: true
|