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 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