Support binary subtypes in Ruby driver. Bumped patch version number.

Binary type is now a subclass of ByteArray with an additional subtype value
that defaults to 2. BSON special-cases subtype 2 to write out the extra length
int.
This commit is contained in:
Jim Menard 2009-02-02 10:07:01 -05:00
parent b1ef64c81f
commit 28daeb6600
5 changed files with 62 additions and 46 deletions

View File

@ -14,21 +14,29 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
require 'mongo/util/byte_buffer'
module XGen
module Mongo
module Driver
# An array of binary bytes. The only reason this exists is so that the
# BSON encoder will know to output the Mongo BINARY type.
class Binary < String; end
# An array of binary bytes with a Mongo subtype value.
class Binary < ByteBuffer
SUBTYPE_BYTES = 0x02
SUBTYPE_UUID = 0x03
SUBTYPE_MD5 = 0x05
SUBTYPE_USER_DEFINED = 0x80
# One of the SUBTYPE_* constants. Default is SUBTYPE_BYTES.
attr_accessor :subtype
def initialize(initial_data=[], subtype=SUBTYPE_BYTES)
super(initial_data)
@subtype = subtype
end
end
end
end
end
class String
# Convert a string into a XGen::Mongo::Driver::Binary
def to_mongo_binary
XGen::Mongo::Driver::Binary.new(self)
end
end

View File

@ -46,8 +46,6 @@ class BSON
NUMBER_INT = 16
MAXKEY = 127
BYTE_TYPE = 2
if RUBY_VERSION >= '1.9'
def self.to_utf8(str)
str.encode("utf-8")
@ -266,13 +264,10 @@ class BSON
end
def deserialize_binary_data(buf)
buf.get_int # length + 4; ignored
buf.get # byte type; ignored
len = buf.get_int
bytes = buf.get(len)
str = ''
bytes.each { |c| str << c.chr }
str.to_mongo_binary
type = buf.get
len = buf.get_int if type == XGen::Mongo::Driver::Binary::SUBTYPE_BYTES
XGen::Mongo::Driver::Binary.new(buf.get(len), type)
end
def serialize_eoo_element(buf)
@ -293,24 +288,19 @@ class BSON
buf.put(BINARY)
self.class.serialize_cstr(buf, key)
bytes = case val
when ByteBuffer
val.to_a
else
if RUBY_VERSION >= '1.9'
val.bytes.to_a
else
a = []
val.each_byte { |byte| a << byte }
a
end
end
bytes = val.to_a
num_bytes = bytes.length
buf.put_int(num_bytes + 4)
buf.put(BYTE_TYPE)
buf.put_int(num_bytes)
buf.put_array(bytes)
subtype = val.respond_to?(:subtype) ? val.subtype : XGen::Mongo::Driver::Binary::SUBTYPE_BYTES
if subtype == XGen::Mongo::Driver::Binary::SUBTYPE_BYTES
buf.put_int(num_bytes + 4)
buf.put(subtype)
buf.put_int(num_bytes)
buf.put_array(bytes)
else
buf.put_int(num_bytes)
buf.put(subtype)
buf.put_array(bytes)
end
end
def serialize_undefined_element(buf, key)
@ -420,7 +410,7 @@ class BSON
NUMBER_INT
when Numeric
NUMBER
when XGen::Mongo::Driver::Binary, ByteBuffer # must be before String
when ByteBuffer
BINARY
when String
# magic awful stuff - the DB requires that a where clause is sent as CODE

View File

@ -45,7 +45,10 @@ class XMLToRuby
when 'string', 'code'
e.text.to_s
when 'binary'
Base64.decode64(e.text.to_s).to_mongo_binary
bin = Binary.new
decoded = Base64.decode64(e.text.to_s)
decoded.each_byte { |b| bin.put(b) }
bin
when 'symbol'
e.text.to_s.intern
when 'boolean'

View File

@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = 'mongo'
s.version = '0.4.4'
s.version = '0.4.5'
s.platform = Gem::Platform::RUBY
s.summary = 'Simple pure-Ruby driver for the 10gen Mongo DB'
s.description = 'A pure-Ruby driver for the 10gen Mongo DB. For more information about Mongo, see http://www.mongodb.org.'

View File

@ -113,25 +113,40 @@ class BSONTest < Test::Unit::TestCase
end
def test_binary
bin = 'binstring'.to_mongo_binary
assert_kind_of Binary, bin
bin = Binary.new
'binstring'.each_byte { |b| bin.put(b) }
doc = {'bin' => bin}
@b.serialize(doc)
doc2 = @b.deserialize
assert_equal 'binstring', doc2['bin']
bin2 = doc2['bin']
assert_kind_of Binary, bin2
assert_equal 'binstring', bin2.to_s
end
def test_binary_type
bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_USER_DEFINED)
doc = {'bin' => bin}
@b.serialize(doc)
doc2 = @b.deserialize
bin2 = doc2['bin']
assert_kind_of Binary, bin2
assert_equal [1, 2, 3, 4, 5], bin2.to_a
assert_equal Binary::SUBTYPE_USER_DEFINED, bin2.subtype
end
def test_binary_byte_buffer
bb = ByteBuffer.new
10.times { |i| bb.put(i) }
5.times { |i| bb.put(i + 1) }
doc = {'bin' => bb}
@b.serialize(doc)
doc2 = @b.deserialize
doc2_bytes = []
doc2['bin'].each_byte { |b| doc2_bytes << b }
assert_equal bb.to_a, doc2_bytes
bin2 = doc2['bin']
assert_kind_of Binary, bin2
assert_equal [1, 2, 3, 4, 5], bin2.to_a
assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
end
def test_undefined