mongo 0.0.1 → 0.0.2

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 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.1'
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'
@@ -1,5 +1,6 @@
1
1
  require 'mongo/mongo'
2
2
  require 'mongo/objectid'
3
+ require 'mongo/dbref'
3
4
  require 'mongo/message'
4
5
  require 'mongo/db'
5
6
  require 'mongo/cursor'
@@ -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
@@ -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)
@@ -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 BINARY, UNDEFINED, REF, SYMBOL, CODE_W_SCOPE
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 = deserialize_element_name(@buf)
110
+ key = deserialize_cstr(@buf)
108
111
  doc[key] = deserialize_string_data(@buf)
109
112
  when NUMBER
110
- key = deserialize_element_name(@buf)
113
+ key = deserialize_cstr(@buf)
111
114
  doc[key] = deserialize_number_data(@buf)
112
115
  when NUMBER_INT
113
- key = deserialize_element_name(@buf)
116
+ key = deserialize_cstr(@buf)
114
117
  doc[key] = deserialize_number_int_data(@buf)
115
118
  when OID
116
- key = deserialize_element_name(@buf)
119
+ key = deserialize_cstr(@buf)
117
120
  doc[key] = deserialize_oid_data(@buf)
118
121
  when ARRAY
119
- key = deserialize_element_name(@buf)
122
+ key = deserialize_cstr(@buf)
120
123
  doc[key] = deserialize_array_data(@buf)
121
124
  when REGEX
122
- key = deserialize_element_name(@buf)
125
+ key = deserialize_cstr(@buf)
123
126
  doc[key] = deserialize_regex_data(@buf)
124
127
  when OBJECT
125
- key = deserialize_element_name(@buf)
128
+ key = deserialize_cstr(@buf)
126
129
  doc[key] = deserialize_object_data(@buf)
127
130
  when BOOLEAN
128
- key = deserialize_element_name(@buf)
131
+ key = deserialize_cstr(@buf)
129
132
  doc[key] = deserialize_boolean_data(@buf)
130
133
  when DATE
131
- key = deserialize_element_name(@buf)
134
+ key = deserialize_cstr(@buf)
132
135
  doc[key] = deserialize_date_data(@buf)
133
136
  when NULL
134
- key = deserialize_element_name(@buf)
137
+ key = deserialize_cstr(@buf)
135
138
  doc[key] = nil
136
- when BINARY, UNDEFINED, REF, SYMBOL, CODE_W_SCOPE
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 = deserialize_element_name(buf)
195
- options_str = deserialize_element_name(buf)
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 deserialize_element_name(buf)
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
@@ -51,4 +51,10 @@ class OrderedHash < Hash
51
51
  super(other)
52
52
  end
53
53
 
54
+ def inspect
55
+ str = '{'
56
+ str << @ordered_keys.collect { |k| "\"#{k}\"=>#{self.[](k).inspect}" }.join(", ")
57
+ str << '}'
58
+ end
59
+
54
60
  end
@@ -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
@@ -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
@@ -79,4 +79,8 @@ class OrderedHashTest < Test::Unit::TestCase
79
79
  assert_equal ['crab', 'apple', 3, 'foo'], @oh.values
80
80
  end
81
81
 
82
+ def test_inspect_retains_order
83
+ assert_equal '{"c"=>1, "a"=>2, "z"=>3}', @oh.inspect
84
+ end
85
+
82
86
  end
@@ -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.1
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-08 00:00:00 -05:00
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