merge new bson commits

This commit is contained in:
Kyle Banker 2010-03-19 15:23:24 -04:00
commit f2bc05f51c
33 changed files with 858 additions and 769 deletions

View File

@ -27,6 +27,7 @@ namespace :test do
ENV['C_EXT'] = 'TRUE' ENV['C_EXT'] = 'TRUE'
Rake::Task['test:unit'].invoke Rake::Task['test:unit'].invoke
Rake::Task['test:functional'].invoke Rake::Task['test:functional'].invoke
Rake::Task['test:bson'].invoke
Rake::Task['test:pooled_threading'].invoke Rake::Task['test:pooled_threading'].invoke
Rake::Task['test:drop_databases'].invoke Rake::Task['test:drop_databases'].invoke
ENV['C_EXT'] = nil ENV['C_EXT'] = nil
@ -37,10 +38,11 @@ namespace :test do
ENV['C_EXT'] = nil ENV['C_EXT'] = nil
Rake::Task['test:unit'].invoke Rake::Task['test:unit'].invoke
Rake::Task['test:functional'].invoke Rake::Task['test:functional'].invoke
Rake::Task['test:bson'].invoke
Rake::Task['test:pooled_threading'].invoke Rake::Task['test:pooled_threading'].invoke
Rake::Task['test:drop_databases'].invoke Rake::Task['test:drop_databases'].invoke
end end
Rake::TestTask.new(:unit) do |t| Rake::TestTask.new(:unit) do |t|
t.test_files = FileList['test/unit/*_test.rb'] t.test_files = FileList['test/unit/*_test.rb']
t.verbose = true t.verbose = true
@ -91,6 +93,11 @@ namespace :test do
t.verbose = true t.verbose = true
end end
Rake::TestTask.new(:bson) do |t|
t.test_files = FileList['test/mongo_bson/*_test.rb']
t.verbose = true
end
task :drop_databases do |t| task :drop_databases do |t|
puts "Dropping test database..." puts "Dropping test database..."
require File.join(File.dirname(__FILE__), 'lib', 'mongo') require File.join(File.dirname(__FILE__), 'lib', 'mongo')

View File

@ -9,9 +9,9 @@ TRIALS = 100000
def encode(doc) def encode(doc)
t0 = Time.new t0 = Time.new
b = BSON.new b = Mongo::BSON_CODER.new
TRIALS.times { |i| TRIALS.times { |i|
b = BSON.new b = Mongo::BSON_CODER.new
b.serialize doc b.serialize doc
} }
print "took: #{Time.now.to_f - t0.to_f}\n" print "took: #{Time.now.to_f - t0.to_f}\n"

View File

@ -128,11 +128,15 @@ static void write_utf8(buffer_t buffer, VALUE string, char check_null) {
#define INT2STRING(buffer, i) asprintf(buffer, "%d", i); #define INT2STRING(buffer, i) asprintf(buffer, "%d", i);
#endif #endif
// this sucks too.
#ifndef RREGEXP_SRC #ifndef RREGEXP_SRC
#define RREGEXP_SRC(r) rb_str_new(RREGEXP((r))->str, RREGEXP((r))->len) #define RREGEXP_SRC(r) rb_str_new(RREGEXP((r))->str, RREGEXP((r))->len)
#endif #endif
// rubinius compatibility
#ifndef RREGEXP_OPTIONS
#define RREGEXP_OPTIONS(r) RREGEXP(value)->ptr->options
#endif
static char zero = 0; static char zero = 0;
static char one = 1; static char one = 1;
@ -275,7 +279,7 @@ static int write_element(VALUE key, VALUE value, VALUE extra, int allow_id) {
} }
case T_STRING: case T_STRING:
{ {
if (strcmp(rb_class2name(RBASIC(value)->klass), if (strcmp(rb_obj_classname(value),
"Mongo::Code") == 0) { "Mongo::Code") == 0) {
buffer_position length_location, start_position, total_length; buffer_position length_location, start_position, total_length;
int length; int length;
@ -319,7 +323,7 @@ static int write_element(VALUE key, VALUE value, VALUE extra, int allow_id) {
case T_OBJECT: case T_OBJECT:
{ {
// TODO there has to be a better way to do these checks... // TODO there has to be a better way to do these checks...
const char* cls = rb_class2name(RBASIC(value)->klass); const char* cls = rb_obj_classname(value);
if (strcmp(cls, "Mongo::Binary") == 0 || if (strcmp(cls, "Mongo::Binary") == 0 ||
strcmp(cls, "ByteBuffer") == 0) { strcmp(cls, "ByteBuffer") == 0) {
const char subtype = strcmp(cls, "ByteBuffer") ? const char subtype = strcmp(cls, "ByteBuffer") ?
@ -397,7 +401,7 @@ static int write_element(VALUE key, VALUE value, VALUE extra, int allow_id) {
} }
case T_DATA: case T_DATA:
{ {
const char* cls = rb_class2name(RBASIC(value)->klass); const char* cls = rb_obj_classname(value);
if (strcmp(cls, "Time") == 0) { if (strcmp(cls, "Time") == 0) {
double t = NUM2DBL(rb_funcall(value, rb_intern("to_f"), 0)); double t = NUM2DBL(rb_funcall(value, rb_intern("to_f"), 0));
long long time_since_epoch = (long long)round(t * 1000); long long time_since_epoch = (long long)round(t * 1000);
@ -417,7 +421,7 @@ static int write_element(VALUE key, VALUE value, VALUE extra, int allow_id) {
case T_REGEXP: case T_REGEXP:
{ {
VALUE pattern = RREGEXP_SRC(value); VALUE pattern = RREGEXP_SRC(value);
long flags = RREGEXP(value)->ptr->options; long flags = RREGEXP_OPTIONS(value);
VALUE has_extra; VALUE has_extra;
write_name_and_type(buffer, key, 0x0B); write_name_and_type(buffer, key, 0x0B);
@ -452,7 +456,7 @@ static int write_element(VALUE key, VALUE value, VALUE extra, int allow_id) {
} }
default: default:
{ {
const char* cls = rb_class2name(RBASIC(value)->klass); const char* cls = rb_obj_classname(value);
buffer_free(buffer); buffer_free(buffer);
rb_raise(InvalidDocument, "Cannot serialize an object of class %s (type %d) into BSON.", cls, TYPE(value)); rb_raise(InvalidDocument, "Cannot serialize an object of class %s (type %d) into BSON.", cls, TYPE(value));
break; break;
@ -495,7 +499,7 @@ static void write_doc(buffer_t buffer, VALUE hash, VALUE check_keys, VALUE move_
} }
else { else {
allow_id = 1; allow_id = 1;
if (strcmp(rb_class2name(RBASIC(hash)->klass), "Hash") == 0) { if (strcmp(rb_obj_classname(hash), "Hash") == 0) {
if ((rb_funcall(hash, rb_intern("has_key?"), 1, id_str) == Qtrue) && if ((rb_funcall(hash, rb_intern("has_key?"), 1, id_str) == Qtrue) &&
(rb_funcall(hash, rb_intern("has_key?"), 1, id_sym) == Qtrue)) { (rb_funcall(hash, rb_intern("has_key?"), 1, id_sym) == Qtrue)) {
VALUE oid_sym = rb_hash_delete(hash, id_sym); VALUE oid_sym = rb_hash_delete(hash, id_sym);
@ -512,7 +516,7 @@ static void write_doc(buffer_t buffer, VALUE hash, VALUE check_keys, VALUE move_
} }
// we have to check for an OrderedHash and handle that specially // we have to check for an OrderedHash and handle that specially
if (strcmp(rb_class2name(RBASIC(hash)->klass), "OrderedHash") == 0) { if (strcmp(rb_obj_classname(hash), "OrderedHash") == 0) {
VALUE keys = rb_funcall(hash, rb_intern("keys"), 0); VALUE keys = rb_funcall(hash, rb_intern("keys"), 0);
int i; int i;
for(i = 0; i < RARRAY_LEN(keys); i++) { for(i = 0; i < RARRAY_LEN(keys); i++) {
@ -883,25 +887,25 @@ void Init_cbson() {
Time = rb_const_get(rb_cObject, rb_intern("Time")); Time = rb_const_get(rb_cObject, rb_intern("Time"));
mongo = rb_const_get(rb_cObject, rb_intern("Mongo")); mongo = rb_const_get(rb_cObject, rb_intern("Mongo"));
rb_require("mongo/types/binary"); rb_require("mongo_bson/types/binary");
Binary = rb_const_get(mongo, rb_intern("Binary")); Binary = rb_const_get(mongo, rb_intern("Binary"));
rb_require("mongo/types/objectid"); rb_require("mongo_bson/types/objectid");
ObjectID = rb_const_get(mongo, rb_intern("ObjectID")); ObjectID = rb_const_get(mongo, rb_intern("ObjectID"));
rb_require("mongo/types/dbref"); rb_require("mongo_bson/types/dbref");
DBRef = rb_const_get(mongo, rb_intern("DBRef")); DBRef = rb_const_get(mongo, rb_intern("DBRef"));
rb_require("mongo/types/code"); rb_require("mongo_bson/types/code");
Code = rb_const_get(mongo, rb_intern("Code")); Code = rb_const_get(mongo, rb_intern("Code"));
rb_require("mongo/types/min_max_keys"); rb_require("mongo_bson/types/min_max_keys");
MinKey = rb_const_get(mongo, rb_intern("MinKey")); MinKey = rb_const_get(mongo, rb_intern("MinKey"));
MaxKey = rb_const_get(mongo, rb_intern("MaxKey")); MaxKey = rb_const_get(mongo, rb_intern("MaxKey"));
rb_require("mongo/types/regexp_of_holding"); rb_require("mongo_bson/types/regexp_of_holding");
Regexp = rb_const_get(rb_cObject, rb_intern("Regexp")); Regexp = rb_const_get(rb_cObject, rb_intern("Regexp"));
RegexpOfHolding = rb_const_get(mongo, rb_intern("RegexpOfHolding")); RegexpOfHolding = rb_const_get(mongo, rb_intern("RegexpOfHolding"));
rb_require("mongo/exceptions"); rb_require("mongo_bson/exceptions");
InvalidName = rb_const_get(mongo, rb_intern("InvalidName")); InvalidName = rb_const_get(mongo, rb_intern("InvalidName"));
InvalidStringEncoding = rb_const_get(mongo, rb_intern("InvalidStringEncoding")); InvalidStringEncoding = rb_const_get(mongo, rb_intern("InvalidStringEncoding"));
InvalidDocument = rb_const_get(mongo, rb_intern("InvalidDocument")); InvalidDocument = rb_const_get(mongo, rb_intern("InvalidDocument"));
rb_require("mongo/util/ordered_hash"); rb_require("mongo_bson/ordered_hash");
OrderedHash = rb_const_get(rb_cObject, rb_intern("OrderedHash")); OrderedHash = rb_const_get(rb_cObject, rb_intern("OrderedHash"));
CBson = rb_define_module("CBson"); CBson = rb_define_module("CBson");

View File

@ -4,21 +4,8 @@ module Mongo
VERSION = "0.19.2" VERSION = "0.19.2"
end end
begin require 'mongo_bson'
# Need this for running test with and without c ext in Ruby 1.9.
raise LoadError if ENV['TEST_MODE'] && !ENV['C_EXT']
require 'mongo_ext/cbson'
raise LoadError unless defined?(CBson::VERSION) && CBson::VERSION == Mongo::VERSION
require 'mongo/util/bson_c'
BSON = BSON_C
rescue LoadError
require 'mongo/util/bson_ruby'
BSON = BSON_RUBY
warn "\n**Notice: C extension not loaded. This is required for optimum MongoDB Ruby driver performance."
warn " You can install the extension as follows:\n gem install mongo_ext\n"
warn " If you continue to receive this message after installing, make sure that the"
warn " mongo_ext gem is in your load path and that the mongo_ext and mongo gems are of the same version.\n"
end
module Mongo module Mongo
ASCENDING = 1 ASCENDING = 1
@ -41,18 +28,10 @@ module Mongo
end end
require 'mongo/types/binary'
require 'mongo/types/code'
require 'mongo/types/dbref'
require 'mongo/types/objectid'
require 'mongo/types/regexp_of_holding'
require 'mongo/types/min_max_keys'
require 'mongo/util/support' require 'mongo/util/support'
require 'mongo/util/core_ext' require 'mongo/util/core_ext'
require 'mongo/util/conversions' require 'mongo/util/conversions'
require 'mongo/util/server_version' require 'mongo/util/server_version'
require 'mongo/util/bson_ruby'
require 'mongo/collection' require 'mongo/collection'
require 'mongo/connection' require 'mongo/connection'

View File

@ -261,7 +261,7 @@ module Mongo
message = ByteBuffer.new([0, 0, 0, 0]) message = ByteBuffer.new([0, 0, 0, 0])
BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}") BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
message.put_int(0) message.put_int(0)
message.put_array(BSON.serialize(selector, false, true).to_a) message.put_array(BSON_CODER.serialize(selector, false, true).to_a)
if opts[:safe] if opts[:safe]
@connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message, @db.name, @connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message, @db.name,
@ -303,8 +303,8 @@ module Mongo
update_options += 1 if options[:upsert] update_options += 1 if options[:upsert]
update_options += 2 if options[:multi] update_options += 2 if options[:multi]
message.put_int(update_options) message.put_int(update_options)
message.put_array(BSON.serialize(selector, false, true).to_a) message.put_array(BSON_CODER.serialize(selector, false, true).to_a)
message.put_array(BSON.serialize(document, false, true).to_a) message.put_array(BSON_CODER.serialize(document, false, true).to_a)
if options[:safe] if options[:safe]
@connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name, @connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name,
"db.#{@name}.update(#{selector.inspect}, #{document.inspect})") "db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
@ -631,7 +631,7 @@ module Mongo
# Initial byte is 0. # Initial byte is 0.
message = ByteBuffer.new([0, 0, 0, 0]) message = ByteBuffer.new([0, 0, 0, 0])
BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{collection_name}") BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{collection_name}")
documents.each { |doc| message.put_array(BSON.serialize(doc, check_keys, true).to_a) } documents.each { |doc| message.put_array(BSON_CODER.serialize(doc, check_keys, true).to_a) }
if safe if safe
@connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name, @connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name,
"db.#{collection_name}.insert(#{documents.inspect})") "db.#{collection_name}.insert(#{documents.inspect})")

View File

@ -664,7 +664,7 @@ module Mongo
buf.put_array(receive_message_on_socket(size - 4, sock).unpack("C*"), 4) buf.put_array(receive_message_on_socket(size - 4, sock).unpack("C*"), 4)
number_remaining -= 1 number_remaining -= 1
buf.rewind buf.rewind
docs << BSON.deserialize(buf) docs << BSON_CODER.deserialize(buf)
end end
[docs, number_received, cursor_id] [docs, number_received, cursor_id]
end end
@ -675,7 +675,7 @@ module Mongo
BSON_RUBY.serialize_cstr(message, "#{db_name}.$cmd") BSON_RUBY.serialize_cstr(message, "#{db_name}.$cmd")
message.put_int(0) message.put_int(0)
message.put_int(-1) message.put_int(-1)
message.put_array(BSON.serialize({:getlasterror => 1}, false).unpack("C*")) message.put_array(BSON_CODER.serialize({:getlasterror => 1}, false).unpack("C*"))
add_message_headers(Mongo::Constants::OP_QUERY, message) add_message_headers(Mongo::Constants::OP_QUERY, message)
end end

View File

@ -356,8 +356,8 @@ module Mongo
if query_contains_special_fields? if query_contains_special_fields?
selector = selector_with_special_query_fields selector = selector_with_special_query_fields
end end
message.put_array(BSON.serialize(selector, false).to_a) message.put_array(BSON_CODER.serialize(selector, false).to_a)
message.put_array(BSON.serialize(@fields, false).to_a) if @fields message.put_array(BSON_CODER.serialize(@fields, false).to_a) if @fields
message message
end end

View File

@ -14,9 +14,9 @@
# limitations under the License. # limitations under the License.
# ++ # ++
require 'mongo/types/objectid' require 'mongo_bson/types/objectid'
require 'mongo/util/byte_buffer' require 'mongo_bson/byte_buffer'
require 'mongo/util/ordered_hash' require 'mongo_bson/ordered_hash'
module GridFS module GridFS

View File

@ -14,8 +14,8 @@
# limitations under the License. # limitations under the License.
# ++ # ++
require 'mongo/types/objectid' require 'mongo_bson/types/objectid'
require 'mongo/util/ordered_hash' require 'mongo_bson/ordered_hash'
require 'mongo/gridfs/chunk' require 'mongo/gridfs/chunk'
module GridFS module GridFS

View File

@ -1,18 +0,0 @@
# A thin wrapper for the CBson class
class BSON_C
def self.serialize(obj, check_keys=false, move_id=false)
ByteBuffer.new(CBson.serialize(obj, check_keys, move_id))
end
def self.deserialize(buf=nil)
if buf.is_a? String
to_deserialize = ByteBuffer.new(buf) if buf
else
buf = ByteBuffer.new(buf.to_a) if buf
end
buf.rewind
CBson.deserialize(buf.to_s)
end
end

View File

@ -1,606 +0,0 @@
# --
# Copyright (C) 2008-2010 10gen Inc.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License, version 3, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
# for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
require 'base64'
require 'mongo/util/byte_buffer'
require 'mongo/util/ordered_hash'
require 'mongo/types/binary'
require 'mongo/types/dbref'
require 'mongo/types/objectid'
require 'mongo/types/regexp_of_holding'
# A BSON seralizer/deserializer in pure Ruby.
class BSON_RUBY
include Mongo
MINKEY = -1
EOO = 0
NUMBER = 1
STRING = 2
OBJECT = 3
ARRAY = 4
BINARY = 5
UNDEFINED = 6
OID = 7
BOOLEAN = 8
DATE = 9
NULL = 10
REGEX = 11
REF = 12
CODE = 13
SYMBOL = 14
CODE_W_SCOPE = 15
NUMBER_INT = 16
TIMESTAMP = 17
NUMBER_LONG = 18
MAXKEY = 127
def initialize
@buf = ByteBuffer.new
end
if RUBY_VERSION >= '1.9'
def self.to_utf8(str)
str.encode("utf-8")
end
else
def self.to_utf8(str)
begin
str.unpack("U*")
rescue => ex
raise InvalidStringEncoding, "String not valid utf-8: #{str}"
end
str
end
end
def self.serialize_cstr(buf, val)
buf.put_array(to_utf8(val.to_s).unpack("C*") << 0)
end
def self.serialize_key(buf, key)
raise InvalidDocument, "Key names / regex patterns must not contain the NULL byte" if key.include? "\x00"
self.serialize_cstr(buf, key)
end
def to_a
@buf.to_a
end
def to_s
@buf.to_s
end
# Serializes an object.
# Implemented to ensure an API compatible with BSON extension.
def self.serialize(obj, check_keys=false, move_id=false)
new.serialize(obj, check_keys, move_id)
end
def self.deserialize(buf=nil)
new.deserialize(buf)
end
def serialize(obj, check_keys=false, move_id=false)
raise "Document is null" unless obj
@buf.rewind
# put in a placeholder for the total size
@buf.put_int(0)
# Write key/value pairs. Always write _id first if it exists.
if move_id
if obj.has_key? '_id'
serialize_key_value('_id', obj['_id'], false)
elsif obj.has_key? :_id
serialize_key_value('_id', obj[:_id], false)
end
obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
else
if obj.has_key?('_id') && obj.has_key?(:_id)
obj['_id'] = obj.delete(:_id)
end
obj.each {|k, v| serialize_key_value(k, v, check_keys) }
end
serialize_eoo_element(@buf)
if @buf.size > 4 * 1024 * 1024
raise InvalidDocument, "Document is too large (#{@buf.size}). BSON documents are limited to 4MB (#{4 * 1024 * 1024})."
end
@buf.put_int(@buf.size, 0)
self
end
# Returns the array stored in the buffer.
# Implemented to ensure an API compatible with BSON extension.
def unpack(arg)
@buf.to_a
end
def serialize_key_value(k, v, check_keys)
k = k.to_s
if check_keys
if k[0] == ?$
raise InvalidName.new("key #{k} must not start with '$'")
end
if k.include? ?.
raise InvalidName.new("key #{k} must not contain '.'")
end
end
type = bson_type(v)
case type
when STRING, SYMBOL
serialize_string_element(@buf, k, v, type)
when NUMBER, NUMBER_INT
serialize_number_element(@buf, k, v, type)
when OBJECT
serialize_object_element(@buf, k, v, check_keys)
when OID
serialize_oid_element(@buf, k, v)
when ARRAY
serialize_array_element(@buf, k, v, check_keys)
when REGEX
serialize_regex_element(@buf, k, v)
when BOOLEAN
serialize_boolean_element(@buf, k, v)
when DATE
serialize_date_element(@buf, k, v)
when NULL
serialize_null_element(@buf, k)
when REF
serialize_dbref_element(@buf, k, v)
when BINARY
serialize_binary_element(@buf, k, v)
when UNDEFINED
serialize_null_element(@buf, k)
when CODE_W_SCOPE
serialize_code_w_scope(@buf, k, v)
when MAXKEY
serialize_max_key_element(@buf, k)
when MINKEY
serialize_min_key_element(@buf, k)
else
raise "unhandled type #{type}"
end
end
def deserialize(buf=nil)
# If buf is nil, use @buf, assumed to contain already-serialized BSON.
# This is only true during testing.
if buf.is_a? String
@buf = ByteBuffer.new(buf) if buf
else
@buf = ByteBuffer.new(buf.to_a) if buf
end
@buf.rewind
@buf.get_int # eat message size
doc = OrderedHash.new
while @buf.more?
type = @buf.get
case type
when STRING, CODE
key = deserialize_cstr(@buf)
doc[key] = deserialize_string_data(@buf)
when SYMBOL
key = deserialize_cstr(@buf)
doc[key] = deserialize_string_data(@buf).intern
when NUMBER
key = deserialize_cstr(@buf)
doc[key] = deserialize_number_data(@buf)
when NUMBER_INT
key = deserialize_cstr(@buf)
doc[key] = deserialize_number_int_data(@buf)
when NUMBER_LONG
key = deserialize_cstr(@buf)
doc[key] = deserialize_number_long_data(@buf)
when OID
key = deserialize_cstr(@buf)
doc[key] = deserialize_oid_data(@buf)
when ARRAY
key = deserialize_cstr(@buf)
doc[key] = deserialize_array_data(@buf)
when REGEX
key = deserialize_cstr(@buf)
doc[key] = deserialize_regex_data(@buf)
when OBJECT
key = deserialize_cstr(@buf)
doc[key] = deserialize_object_data(@buf)
when BOOLEAN
key = deserialize_cstr(@buf)
doc[key] = deserialize_boolean_data(@buf)
when DATE
key = deserialize_cstr(@buf)
doc[key] = deserialize_date_data(@buf)
when NULL
key = deserialize_cstr(@buf)
doc[key] = nil
when UNDEFINED
key = deserialize_cstr(@buf)
doc[key] = nil
when REF
key = deserialize_cstr(@buf)
doc[key] = deserialize_dbref_data(@buf)
when BINARY
key = deserialize_cstr(@buf)
doc[key] = deserialize_binary_data(@buf)
when CODE_W_SCOPE
key = deserialize_cstr(@buf)
doc[key] = deserialize_code_w_scope_data(@buf)
when TIMESTAMP
key = deserialize_cstr(@buf)
doc[key] = [deserialize_number_int_data(@buf),
deserialize_number_int_data(@buf)]
when MAXKEY
key = deserialize_cstr(@buf)
doc[key] = MaxKey.new
when MINKEY, 255 # This is currently easier than unpack the type byte as an unsigned char.
key = deserialize_cstr(@buf)
doc[key] = MinKey.new
when EOO
break
else
raise "Unknown type #{type}, key = #{key}"
end
end
@buf.rewind
doc
end
# For debugging.
def hex_dump
str = ''
@buf.to_a.each_with_index { |b,i|
if (i % 8) == 0
str << "\n" if i > 0
str << '%4d: ' % i
else
str << ' '
end
str << '%02X' % b
}
str
end
def deserialize_date_data(buf)
unsigned = buf.get_long()
# see note for deserialize_number_long_data below
milliseconds = unsigned >= 2 ** 64 / 2 ? unsigned - 2**64 : unsigned
Time.at(milliseconds.to_f / 1000.0).utc # at() takes fractional seconds
end
def deserialize_boolean_data(buf)
buf.get == 1
end
def deserialize_number_data(buf)
buf.get_double
end
def deserialize_number_int_data(buf)
# sometimes ruby makes me angry... why would the same code pack as signed
# but unpack as unsigned
unsigned = buf.get_int
unsigned >= 2**32 / 2 ? unsigned - 2**32 : unsigned
end
def deserialize_number_long_data(buf)
# same note as above applies here...
unsigned = buf.get_long
unsigned >= 2 ** 64 / 2 ? unsigned - 2**64 : unsigned
end
def deserialize_object_data(buf)
size = buf.get_int
buf.position -= 4
object = BSON.new().deserialize(buf.get(size))
if object.has_key? "$ref"
DBRef.new(object["$ref"], object["$id"])
else
object
end
end
def deserialize_array_data(buf)
h = deserialize_object_data(buf)
a = []
h.each { |k, v| a[k.to_i] = v }
a
end
def deserialize_regex_data(buf)
str = deserialize_cstr(buf)
options_str = deserialize_cstr(buf)
options = 0
options |= Regexp::IGNORECASE if options_str.include?('i')
options |= Regexp::MULTILINE if options_str.include?('m')
options |= Regexp::EXTENDED if options_str.include?('x')
options_str.gsub!(/[imx]/, '') # Now remove the three we understand
if options_str == ''
Regexp.new(str, options)
else
warn("Using deprecated Regexp options #{options_str}; future versions of this MongoDB driver will support only i, m, and x. See deprecated class RegexpOfHolding for more info.")
RegexpOfHolding.new(str, options, options_str)
end
end
def deserialize_string_data(buf)
len = buf.get_int
bytes = buf.get(len)
str = bytes[0..-2]
if str.respond_to? "pack"
str = str.pack("C*")
end
if RUBY_VERSION >= '1.9'
str.force_encoding("utf-8")
end
str
end
def deserialize_code_w_scope_data(buf)
buf.get_int
len = buf.get_int
code = buf.get(len)[0..-2]
if code.respond_to? "pack"
code = code.pack("C*")
end
if RUBY_VERSION >= '1.9'
code.force_encoding("utf-8")
end
scope_size = buf.get_int
buf.position -= 4
scope = BSON.new().deserialize(buf.get(scope_size))
Code.new(code, scope)
end
def deserialize_oid_data(buf)
ObjectID.new(buf.get(12))
end
def deserialize_dbref_data(buf)
ns = deserialize_string_data(buf)
oid = deserialize_oid_data(buf)
DBRef.new(ns, oid)
end
def deserialize_binary_data(buf)
len = buf.get_int
type = buf.get
len = buf.get_int if type == Binary::SUBTYPE_BYTES
Binary.new(buf.get(len), type)
end
def serialize_eoo_element(buf)
buf.put(EOO)
end
def serialize_null_element(buf, key)
buf.put(NULL)
self.class.serialize_key(buf, key)
end
def serialize_dbref_element(buf, key, val)
oh = OrderedHash.new
oh['$ref'] = val.namespace
oh['$id'] = val.object_id
serialize_object_element(buf, key, oh, false)
end
def serialize_binary_element(buf, key, val)
buf.put(BINARY)
self.class.serialize_key(buf, key)
bytes = val.to_a
num_bytes = bytes.length
subtype = val.respond_to?(:subtype) ? val.subtype : Binary::SUBTYPE_BYTES
if subtype == 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_boolean_element(buf, key, val)
buf.put(BOOLEAN)
self.class.serialize_key(buf, key)
buf.put(val ? 1 : 0)
end
def serialize_date_element(buf, key, val)
buf.put(DATE)
self.class.serialize_key(buf, key)
millisecs = (val.to_f * 1000).to_i
buf.put_long(millisecs)
end
def serialize_number_element(buf, key, val, type)
if type == NUMBER
buf.put(type)
self.class.serialize_key(buf, key)
buf.put_double(val)
else
if val > 2**64 / 2 - 1 or val < -2**64 / 2
raise RangeError.new("MongoDB can only handle 8-byte ints")
end
if val > 2**32 / 2 - 1 or val < -2**32 / 2
buf.put(NUMBER_LONG)
self.class.serialize_key(buf, key)
buf.put_long(val)
else
buf.put(type)
self.class.serialize_key(buf, key)
buf.put_int(val)
end
end
end
def serialize_object_element(buf, key, val, check_keys, opcode=OBJECT)
buf.put(opcode)
self.class.serialize_key(buf, key)
buf.put_array(BSON.new.serialize(val, check_keys).to_a)
end
def serialize_array_element(buf, key, val, check_keys)
# Turn array into hash with integer indices as keys
h = OrderedHash.new
i = 0
val.each { |v| h[i] = v; i += 1 }
serialize_object_element(buf, key, h, check_keys, ARRAY)
end
def serialize_regex_element(buf, key, val)
buf.put(REGEX)
self.class.serialize_key(buf, key)
str = val.source
# We use serialize_key here since regex patterns aren't prefixed with
# length (can't contain the NULL byte).
self.class.serialize_key(buf, str)
options = val.options
options_str = ''
options_str << 'i' if ((options & Regexp::IGNORECASE) != 0)
options_str << 'm' if ((options & Regexp::MULTILINE) != 0)
options_str << 'x' if ((options & Regexp::EXTENDED) != 0)
options_str << val.extra_options_str if val.respond_to?(:extra_options_str)
# Must store option chars in alphabetical order
self.class.serialize_cstr(buf, options_str.split(//).sort.uniq.join)
end
def serialize_max_key_element(buf, key)
buf.put(MAXKEY)
self.class.serialize_key(buf, key)
end
def serialize_min_key_element(buf, key)
buf.put(MINKEY)
self.class.serialize_key(buf, key)
end
def serialize_oid_element(buf, key, val)
buf.put(OID)
self.class.serialize_key(buf, key)
buf.put_array(val.to_a)
end
def serialize_string_element(buf, key, val, type)
buf.put(type)
self.class.serialize_key(buf, key)
# Make a hole for the length
len_pos = buf.position
buf.put_int(0)
# Save the string
start_pos = buf.position
self.class.serialize_cstr(buf, val)
end_pos = buf.position
# Put the string size in front
buf.put_int(end_pos - start_pos, len_pos)
# Go back to where we were
buf.position = end_pos
end
def serialize_code_w_scope(buf, key, val)
buf.put(CODE_W_SCOPE)
self.class.serialize_key(buf, key)
# Make a hole for the length
len_pos = buf.position
buf.put_int(0)
buf.put_int(val.length + 1)
self.class.serialize_cstr(buf, val)
buf.put_array(BSON.new.serialize(val.scope).to_a)
end_pos = buf.position
buf.put_int(end_pos - len_pos, len_pos)
buf.position = end_pos
end
def deserialize_cstr(buf)
chars = ""
while true
b = buf.get
break if b == 0
chars << b.chr
end
if RUBY_VERSION >= '1.9'
chars.force_encoding("utf-8") # Mongo stores UTF-8
end
chars
end
def bson_type(o)
case o
when nil
NULL
when Integer
NUMBER_INT
when Float
NUMBER
when ByteBuffer
BINARY
when Code
CODE_W_SCOPE
when String
STRING
when Array
ARRAY
when Regexp
REGEX
when ObjectID
OID
when DBRef
REF
when true, false
BOOLEAN
when Time
DATE
when Hash
OBJECT
when Symbol
SYMBOL
when MaxKey
MAXKEY
when MinKey
MINKEY
when Numeric
raise InvalidDocument, "Cannot serialize the Numeric type #{o.class} as BSON; only Fixum, Bignum, and Float are supported."
when Date, DateTime
raise InvalidDocument, "#{o.class} is not currently supported; " +
"use a UTC Time instance instead."
else
if defined?(ActiveSupport::TimeWithZone) && o.is_a?(ActiveSupport::TimeWithZone)
raise InvalidDocument, "ActiveSupport::TimeWithZone is not currently supported; " +
"use a UTC Time instance instead."
else
raise InvalidDocument, "Cannot serialize #{o.class} as a BSON type; it either isn't supported or won't translate to BSON."
end
end
end
end

40
lib/mongo_bson.rb Normal file
View File

@ -0,0 +1,40 @@
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
module Mongo
module BSON
VERSION = "0.19.2"
end
end
begin
# Need this for running test with and without c ext in Ruby 1.9.
raise LoadError if ENV['TEST_MODE'] && !ENV['C_EXT']
require 'mongo_ext/cbson'
raise LoadError unless defined?(CBson::VERSION) && CBson::VERSION == Mongo::BSON::VERSION
require 'mongo_bson/bson_c'
module Mongo
BSON_CODER = BSON_C
end
rescue LoadError
require 'mongo_bson/bson_ruby'
module Mongo
BSON_CODER = BSON_RUBY
end
warn "\n**Notice: C extension not loaded. This is required for optimum MongoDB Ruby driver performance."
warn " You can install the extension as follows:\n gem install mongo_ext\n"
warn " If you continue to receive this message after installing, make sure that the"
warn " mongo_ext gem is in your load path and that the mongo_ext and mongo gems are of the same version.\n"
end
require 'mongo_bson/types/binary'
require 'mongo_bson/types/code'
require 'mongo_bson/types/dbref'
require 'mongo_bson/types/objectid'
require 'mongo_bson/types/regexp_of_holding'
require 'mongo_bson/types/min_max_keys'
require 'base64'
require 'mongo_bson/ordered_hash'
require 'mongo_bson/byte_buffer'
require 'mongo_bson/bson_ruby'
require 'mongo_bson/exceptions'

20
lib/mongo_bson/bson_c.rb Normal file
View File

@ -0,0 +1,20 @@
# A thin wrapper for the CBson class
module Mongo
class BSON_C
def self.serialize(obj, check_keys=false, move_id=false)
ByteBuffer.new(CBson.serialize(obj, check_keys, move_id))
end
def self.deserialize(buf=nil)
if buf.is_a? String
to_deserialize = ByteBuffer.new(buf) if buf
else
buf = ByteBuffer.new(buf.to_a) if buf
end
buf.rewind
CBson.deserialize(buf.to_s)
end
end
end

601
lib/mongo_bson/bson_ruby.rb Normal file
View File

@ -0,0 +1,601 @@
# --
# Copyright (C) 2008-2010 10gen Inc.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License, version 3, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
# for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
module Mongo
# A BSON seralizer/deserializer in pure Ruby.
class BSON_RUBY
# why was this necessary?
#include Mongo
MINKEY = -1
EOO = 0
NUMBER = 1
STRING = 2
OBJECT = 3
ARRAY = 4
BINARY = 5
UNDEFINED = 6
OID = 7
BOOLEAN = 8
DATE = 9
NULL = 10
REGEX = 11
REF = 12
CODE = 13
SYMBOL = 14
CODE_W_SCOPE = 15
NUMBER_INT = 16
TIMESTAMP = 17
NUMBER_LONG = 18
MAXKEY = 127
def initialize
@buf = ByteBuffer.new
end
if RUBY_VERSION >= '1.9'
def self.to_utf8(str)
str.encode("utf-8")
end
else
def self.to_utf8(str)
begin
str.unpack("U*")
rescue => ex
raise InvalidStringEncoding, "String not valid utf-8: #{str}"
end
str
end
end
def self.serialize_cstr(buf, val)
buf.put_array(to_utf8(val.to_s).unpack("C*") << 0)
end
def self.serialize_key(buf, key)
raise InvalidDocument, "Key names / regex patterns must not contain the NULL byte" if key.include? "\x00"
self.serialize_cstr(buf, key)
end
def to_a
@buf.to_a
end
def to_s
@buf.to_s
end
# Serializes an object.
# Implemented to ensure an API compatible with BSON extension.
def self.serialize(obj, check_keys=false, move_id=false)
new.serialize(obj, check_keys, move_id)
end
def self.deserialize(buf=nil)
new.deserialize(buf)
end
def serialize(obj, check_keys=false, move_id=false)
raise "Document is null" unless obj
@buf.rewind
# put in a placeholder for the total size
@buf.put_int(0)
# Write key/value pairs. Always write _id first if it exists.
if move_id
if obj.has_key? '_id'
serialize_key_value('_id', obj['_id'], false)
elsif obj.has_key? :_id
serialize_key_value('_id', obj[:_id], false)
end
obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
else
if obj.has_key?('_id') && obj.has_key?(:_id)
obj['_id'] = obj.delete(:_id)
end
obj.each {|k, v| serialize_key_value(k, v, check_keys) }
end
serialize_eoo_element(@buf)
if @buf.size > 4 * 1024 * 1024
raise InvalidDocument, "Document is too large (#{@buf.size}). BSON documents are limited to 4MB (#{4 * 1024 * 1024})."
end
@buf.put_int(@buf.size, 0)
self
end
# Returns the array stored in the buffer.
# Implemented to ensure an API compatible with BSON extension.
def unpack(arg)
@buf.to_a
end
def serialize_key_value(k, v, check_keys)
k = k.to_s
if check_keys
if k[0] == ?$
raise InvalidName.new("key #{k} must not start with '$'")
end
if k.include? ?.
raise InvalidName.new("key #{k} must not contain '.'")
end
end
type = bson_type(v)
case type
when STRING, SYMBOL
serialize_string_element(@buf, k, v, type)
when NUMBER, NUMBER_INT
serialize_number_element(@buf, k, v, type)
when OBJECT
serialize_object_element(@buf, k, v, check_keys)
when OID
serialize_oid_element(@buf, k, v)
when ARRAY
serialize_array_element(@buf, k, v, check_keys)
when REGEX
serialize_regex_element(@buf, k, v)
when BOOLEAN
serialize_boolean_element(@buf, k, v)
when DATE
serialize_date_element(@buf, k, v)
when NULL
serialize_null_element(@buf, k)
when REF
serialize_dbref_element(@buf, k, v)
when BINARY
serialize_binary_element(@buf, k, v)
when UNDEFINED
serialize_null_element(@buf, k)
when CODE_W_SCOPE
serialize_code_w_scope(@buf, k, v)
when MAXKEY
serialize_max_key_element(@buf, k)
when MINKEY
serialize_min_key_element(@buf, k)
else
raise "unhandled type #{type}"
end
end
def deserialize(buf=nil)
# If buf is nil, use @buf, assumed to contain already-serialized BSON.
# This is only true during testing.
if buf.is_a? String
@buf = ByteBuffer.new(buf) if buf
else
@buf = ByteBuffer.new(buf.to_a) if buf
end
@buf.rewind
@buf.get_int # eat message size
doc = OrderedHash.new
while @buf.more?
type = @buf.get
case type
when STRING, CODE
key = deserialize_cstr(@buf)
doc[key] = deserialize_string_data(@buf)
when SYMBOL
key = deserialize_cstr(@buf)
doc[key] = deserialize_string_data(@buf).intern
when NUMBER
key = deserialize_cstr(@buf)
doc[key] = deserialize_number_data(@buf)
when NUMBER_INT
key = deserialize_cstr(@buf)
doc[key] = deserialize_number_int_data(@buf)
when NUMBER_LONG
key = deserialize_cstr(@buf)
doc[key] = deserialize_number_long_data(@buf)
when OID
key = deserialize_cstr(@buf)
doc[key] = deserialize_oid_data(@buf)
when ARRAY
key = deserialize_cstr(@buf)
doc[key] = deserialize_array_data(@buf)
when REGEX
key = deserialize_cstr(@buf)
doc[key] = deserialize_regex_data(@buf)
when OBJECT
key = deserialize_cstr(@buf)
doc[key] = deserialize_object_data(@buf)
when BOOLEAN
key = deserialize_cstr(@buf)
doc[key] = deserialize_boolean_data(@buf)
when DATE
key = deserialize_cstr(@buf)
doc[key] = deserialize_date_data(@buf)
when NULL
key = deserialize_cstr(@buf)
doc[key] = nil
when UNDEFINED
key = deserialize_cstr(@buf)
doc[key] = nil
when REF
key = deserialize_cstr(@buf)
doc[key] = deserialize_dbref_data(@buf)
when BINARY
key = deserialize_cstr(@buf)
doc[key] = deserialize_binary_data(@buf)
when CODE_W_SCOPE
key = deserialize_cstr(@buf)
doc[key] = deserialize_code_w_scope_data(@buf)
when TIMESTAMP
key = deserialize_cstr(@buf)
doc[key] = [deserialize_number_int_data(@buf),
deserialize_number_int_data(@buf)]
when MAXKEY
key = deserialize_cstr(@buf)
doc[key] = MaxKey.new
when MINKEY, 255 # This is currently easier than unpack the type byte as an unsigned char.
key = deserialize_cstr(@buf)
doc[key] = MinKey.new
when EOO
break
else
raise "Unknown type #{type}, key = #{key}"
end
end
@buf.rewind
doc
end
# For debugging.
def hex_dump
str = ''
@buf.to_a.each_with_index { |b,i|
if (i % 8) == 0
str << "\n" if i > 0
str << '%4d: ' % i
else
str << ' '
end
str << '%02X' % b
}
str
end
def deserialize_date_data(buf)
unsigned = buf.get_long()
# see note for deserialize_number_long_data below
milliseconds = unsigned >= 2 ** 64 / 2 ? unsigned - 2**64 : unsigned
Time.at(milliseconds.to_f / 1000.0).utc # at() takes fractional seconds
end
def deserialize_boolean_data(buf)
buf.get == 1
end
def deserialize_number_data(buf)
buf.get_double
end
def deserialize_number_int_data(buf)
# sometimes ruby makes me angry... why would the same code pack as signed
# but unpack as unsigned
unsigned = buf.get_int
unsigned >= 2**32 / 2 ? unsigned - 2**32 : unsigned
end
def deserialize_number_long_data(buf)
# same note as above applies here...
unsigned = buf.get_long
unsigned >= 2 ** 64 / 2 ? unsigned - 2**64 : unsigned
end
def deserialize_object_data(buf)
size = buf.get_int
buf.position -= 4
object = BSON_CODER.new().deserialize(buf.get(size))
if object.has_key? "$ref"
DBRef.new(object["$ref"], object["$id"])
else
object
end
end
def deserialize_array_data(buf)
h = deserialize_object_data(buf)
a = []
h.each { |k, v| a[k.to_i] = v }
a
end
def deserialize_regex_data(buf)
str = deserialize_cstr(buf)
options_str = deserialize_cstr(buf)
options = 0
options |= Regexp::IGNORECASE if options_str.include?('i')
options |= Regexp::MULTILINE if options_str.include?('m')
options |= Regexp::EXTENDED if options_str.include?('x')
options_str.gsub!(/[imx]/, '') # Now remove the three we understand
if options_str == ''
Regexp.new(str, options)
else
warn("Using deprecated Regexp options #{options_str}; future versions of this MongoDB driver will support only i, m, and x. See deprecated class RegexpOfHolding for more info.")
RegexpOfHolding.new(str, options, options_str)
end
end
def deserialize_string_data(buf)
len = buf.get_int
bytes = buf.get(len)
str = bytes[0..-2]
if str.respond_to? "pack"
str = str.pack("C*")
end
if RUBY_VERSION >= '1.9'
str.force_encoding("utf-8")
end
str
end
def deserialize_code_w_scope_data(buf)
buf.get_int
len = buf.get_int
code = buf.get(len)[0..-2]
if code.respond_to? "pack"
code = code.pack("C*")
end
if RUBY_VERSION >= '1.9'
code.force_encoding("utf-8")
end
scope_size = buf.get_int
buf.position -= 4
scope = BSON_CODER.new().deserialize(buf.get(scope_size))
Code.new(code, scope)
end
def deserialize_oid_data(buf)
ObjectID.new(buf.get(12))
end
def deserialize_dbref_data(buf)
ns = deserialize_string_data(buf)
oid = deserialize_oid_data(buf)
DBRef.new(ns, oid)
end
def deserialize_binary_data(buf)
len = buf.get_int
type = buf.get
len = buf.get_int if type == Binary::SUBTYPE_BYTES
Binary.new(buf.get(len), type)
end
def serialize_eoo_element(buf)
buf.put(EOO)
end
def serialize_null_element(buf, key)
buf.put(NULL)
self.class.serialize_key(buf, key)
end
def serialize_dbref_element(buf, key, val)
oh = OrderedHash.new
oh['$ref'] = val.namespace
oh['$id'] = val.object_id
serialize_object_element(buf, key, oh, false)
end
def serialize_binary_element(buf, key, val)
buf.put(BINARY)
self.class.serialize_key(buf, key)
bytes = val.to_a
num_bytes = bytes.length
subtype = val.respond_to?(:subtype) ? val.subtype : Binary::SUBTYPE_BYTES
if subtype == 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_boolean_element(buf, key, val)
buf.put(BOOLEAN)
self.class.serialize_key(buf, key)
buf.put(val ? 1 : 0)
end
def serialize_date_element(buf, key, val)
buf.put(DATE)
self.class.serialize_key(buf, key)
millisecs = (val.to_f * 1000).to_i
buf.put_long(millisecs)
end
def serialize_number_element(buf, key, val, type)
if type == NUMBER
buf.put(type)
self.class.serialize_key(buf, key)
buf.put_double(val)
else
if val > 2**64 / 2 - 1 or val < -2**64 / 2
raise RangeError.new("MongoDB can only handle 8-byte ints")
end
if val > 2**32 / 2 - 1 or val < -2**32 / 2
buf.put(NUMBER_LONG)
self.class.serialize_key(buf, key)
buf.put_long(val)
else
buf.put(type)
self.class.serialize_key(buf, key)
buf.put_int(val)
end
end
end
def serialize_object_element(buf, key, val, check_keys, opcode=OBJECT)
buf.put(opcode)
self.class.serialize_key(buf, key)
buf.put_array(BSON_CODER.new.serialize(val, check_keys).to_a)
end
def serialize_array_element(buf, key, val, check_keys)
# Turn array into hash with integer indices as keys
h = OrderedHash.new
i = 0
val.each { |v| h[i] = v; i += 1 }
serialize_object_element(buf, key, h, check_keys, ARRAY)
end
def serialize_regex_element(buf, key, val)
buf.put(REGEX)
self.class.serialize_key(buf, key)
str = val.source
# We use serialize_key here since regex patterns aren't prefixed with
# length (can't contain the NULL byte).
self.class.serialize_key(buf, str)
options = val.options
options_str = ''
options_str << 'i' if ((options & Regexp::IGNORECASE) != 0)
options_str << 'm' if ((options & Regexp::MULTILINE) != 0)
options_str << 'x' if ((options & Regexp::EXTENDED) != 0)
options_str << val.extra_options_str if val.respond_to?(:extra_options_str)
# Must store option chars in alphabetical order
self.class.serialize_cstr(buf, options_str.split(//).sort.uniq.join)
end
def serialize_max_key_element(buf, key)
buf.put(MAXKEY)
self.class.serialize_key(buf, key)
end
def serialize_min_key_element(buf, key)
buf.put(MINKEY)
self.class.serialize_key(buf, key)
end
def serialize_oid_element(buf, key, val)
buf.put(OID)
self.class.serialize_key(buf, key)
buf.put_array(val.to_a)
end
def serialize_string_element(buf, key, val, type)
buf.put(type)
self.class.serialize_key(buf, key)
# Make a hole for the length
len_pos = buf.position
buf.put_int(0)
# Save the string
start_pos = buf.position
self.class.serialize_cstr(buf, val)
end_pos = buf.position
# Put the string size in front
buf.put_int(end_pos - start_pos, len_pos)
# Go back to where we were
buf.position = end_pos
end
def serialize_code_w_scope(buf, key, val)
buf.put(CODE_W_SCOPE)
self.class.serialize_key(buf, key)
# Make a hole for the length
len_pos = buf.position
buf.put_int(0)
buf.put_int(val.length + 1)
self.class.serialize_cstr(buf, val)
buf.put_array(BSON_CODER.new.serialize(val.scope).to_a)
end_pos = buf.position
buf.put_int(end_pos - len_pos, len_pos)
buf.position = end_pos
end
def deserialize_cstr(buf)
chars = ""
while true
b = buf.get
break if b == 0
chars << b.chr
end
if RUBY_VERSION >= '1.9'
chars.force_encoding("utf-8") # Mongo stores UTF-8
end
chars
end
def bson_type(o)
case o
when nil
NULL
when Integer
NUMBER_INT
when Float
NUMBER
when ByteBuffer
BINARY
when Code
CODE_W_SCOPE
when String
STRING
when Array
ARRAY
when Regexp
REGEX
when ObjectID
OID
when DBRef
REF
when true, false
BOOLEAN
when Time
DATE
when Hash
OBJECT
when Symbol
SYMBOL
when MaxKey
MAXKEY
when MinKey
MINKEY
when Numeric
raise InvalidDocument, "Cannot serialize the Numeric type #{o.class} as BSON; only Fixum, Bignum, and Float are supported."
when Date, DateTime
raise InvalidDocument, "#{o.class} is not currently supported; " +
"use a UTC Time instance instead."
else
if defined?(ActiveSupport::TimeWithZone) && o.is_a?(ActiveSupport::TimeWithZone)
raise InvalidDocument, "ActiveSupport::TimeWithZone is not currently supported; " +
"use a UTC Time instance instead."
else
raise InvalidDocument, "Cannot serialize #{o.class} as a BSON type; it either isn't supported or won't translate to BSON."
end
end
end
end
end

View File

@ -0,0 +1,36 @@
# --
# Copyright (C) 2008-2010 10gen Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ++
module Mongo
# Generic Mongo Ruby Driver exception class.
class MongoRubyError < StandardError; end
# Raised when MongoDB itself has returned an error.
class MongoDBError < RuntimeError; end
# Raised when given a string is not valid utf-8 (Ruby 1.8 only).
class InvalidStringEncoding < MongoRubyError; end
# Raised when attempting to initialize an invalid ObjectID.
class InvalidObjectID < MongoRubyError; end
# Raised when trying to insert a document that exceeds the 4MB limit or
# when the document contains objects that can't be serialized as BSON.
class InvalidDocument < MongoDBError; end
# Raised when an invalid name is used.
class InvalidName < RuntimeError; end
end

View File

@ -14,7 +14,7 @@
# limitations under the License. # limitations under the License.
# ++ # ++
require 'mongo/util/byte_buffer' require 'mongo_bson/byte_buffer'
module Mongo module Mongo

23
mongo-bson.gemspec Normal file
View File

@ -0,0 +1,23 @@
require "lib/mongo_bson"
Gem::Specification.new do |s|
s.name = 'mongo_bson'
s.version = Mongo::BSON::VERSION
s.platform = Gem::Platform::RUBY
s.summary = 'Ruby implementation of BSON'
s.description = 'A Ruby BSON implementation for MongoDB. For more information about Mongo, see http://www.mongodb.org. For more information on BSON, see http://www.bsonspec.org.'
s.require_paths = ['lib']
s.files = ['Rakefile', 'mongo-bson.gemspec', 'LICENSE.txt']
s.files += ['lib/mongo_bson.rb'] + Dir['lib/mongo_bson/**/*.rb']
s.test_files = Dir['test/mongo_bson/*.rb']
s.has_rdoc = true
s.authors = ['Jim Menard', 'Mike Dirolf', 'Kyle Banker']
s.email = 'mongodb-dev@googlegroups.com'
s.homepage = 'http://www.mongodb.org'
end

View File

@ -12,11 +12,13 @@ Gem::Specification.new do |s|
s.require_paths = ['lib'] s.require_paths = ['lib']
s.files = ['README.rdoc', 'Rakefile', 'mongo-ruby-driver.gemspec', 'LICENSE.txt'] s.files = ['README.rdoc', 'Rakefile', 'mongo-ruby-driver.gemspec', 'LICENSE.txt']
s.files += Dir['lib/**/*.rb'] + Dir['examples/**/*.rb'] + Dir['bin/**/*.rb'] s.files += ['lib/mongo.rb'] + Dir['lib/mongo/**/*.rb']
s.files += Dir['examples/**/*.rb'] + Dir['bin/**/*.rb']
s.test_files = Dir['test/**/*.rb'] s.test_files = Dir['test/**/*.rb']
s.has_rdoc = true s.has_rdoc = true
s.test_files = Dir['test/**/*.rb'] s.test_files = Dir['test/**/*.rb']
s.test_files -= Dir['test/mongo_bson/*.rb'] # remove these files from the manifest
s.has_rdoc = true s.has_rdoc = true
s.rdoc_options = ['--main', 'README.rdoc', '--inline-source'] s.rdoc_options = ['--main', 'README.rdoc', '--inline-source']

View File

@ -1,3 +1,4 @@
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'test/test_helper' require 'test/test_helper'
require 'mongo/gridfs' require 'mongo/gridfs'

View File

@ -1,7 +1,7 @@
require 'test/test_helper' require 'test/test_helper'
require 'mongo/exceptions' require 'mongo/exceptions'
require 'mongo/util/conversions' require 'mongo/util/conversions'
require 'mongo/util/ordered_hash' require 'mongo_bson/ordered_hash'
class ConversionsTest < Test::Unit::TestCase class ConversionsTest < Test::Unit::TestCase
include Mongo::Conversions include Mongo::Conversions

View File

@ -24,26 +24,26 @@ class BSONTest < Test::Unit::TestCase
def test_string def test_string
doc = {'doc' => 'hello, world'} doc = {'doc' => 'hello, world'}
bson = bson = BSON.serialize(doc) bson = bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_valid_utf8_string def test_valid_utf8_string
doc = {'doc' => 'aé'} doc = {'doc' => 'aé'}
bson = bson = BSON.serialize(doc) bson = bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_valid_utf8_key def test_valid_utf8_key
doc = {'aé' => 'hello'} doc = {'aé' => 'hello'}
bson = bson = BSON.serialize(doc) bson = bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_document_length def test_document_length
doc = {'name' => 'a' * 5 * 1024 * 1024} doc = {'name' => 'a' * 5 * 1024 * 1024}
assert_raise InvalidDocument do assert_raise InvalidDocument do
assert BSON.serialize(doc) assert Mongo::BSON_CODER.serialize(doc)
end end
end end
@ -55,7 +55,7 @@ class BSONTest < Test::Unit::TestCase
string = Iconv.conv('iso-8859-1', 'utf-8', 'aé') string = Iconv.conv('iso-8859-1', 'utf-8', 'aé')
doc = {'doc' => string} doc = {'doc' => string}
assert_raise InvalidStringEncoding do assert_raise InvalidStringEncoding do
BSON.serialize(doc) Mongo::BSON_CODER.serialize(doc)
end end
end end
@ -63,51 +63,51 @@ class BSONTest < Test::Unit::TestCase
key = Iconv.conv('iso-8859-1', 'utf-8', 'aé') key = Iconv.conv('iso-8859-1', 'utf-8', 'aé')
doc = {key => 'hello'} doc = {key => 'hello'}
assert_raise InvalidStringEncoding do assert_raise InvalidStringEncoding do
BSON.serialize(doc) Mongo::BSON_CODER.serialize(doc)
end end
end end
else else
def test_non_utf8_string def test_non_utf8_string
bson = BSON.serialize({'str' => 'aé'.encode('iso-8859-1')}) bson = Mongo::BSON_CODER.serialize({'str' => 'aé'.encode('iso-8859-1')})
result = BSON.deserialize(bson)['str'] result = Mongo::BSON_CODER.deserialize(bson)['str']
assert_equal 'aé', result assert_equal 'aé', result
assert_equal 'UTF-8', result.encoding.name assert_equal 'UTF-8', result.encoding.name
end end
def test_non_utf8_key def test_non_utf8_key
bson = BSON.serialize({'aé'.encode('iso-8859-1') => 'hello'}) bson = Mongo::BSON_CODER.serialize({'aé'.encode('iso-8859-1') => 'hello'})
assert_equal 'hello', BSON.deserialize(bson)['aé'] assert_equal 'hello', Mongo::BSON_CODER.deserialize(bson)['aé']
end end
end end
def test_code def test_code
doc = {'$where' => Code.new('this.a.b < this.b')} doc = {'$where' => Code.new('this.a.b < this.b')}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_number def test_number
doc = {'doc' => 41.99} doc = {'doc' => 41.99}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_int def test_int
doc = {'doc' => 42} doc = {'doc' => 42}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
doc = {"doc" => -5600} doc = {"doc" => -5600}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
doc = {"doc" => 2147483647} doc = {"doc" => 2147483647}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
doc = {"doc" => -2147483648} doc = {"doc" => -2147483648}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_ordered_hash def test_ordered_hash
@ -116,32 +116,32 @@ class BSONTest < Test::Unit::TestCase
doc["a"] = 2 doc["a"] = 2
doc["c"] = 3 doc["c"] = 3
doc["d"] = 4 doc["d"] = 4
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_object def test_object
doc = {'doc' => {'age' => 42, 'name' => 'Spongebob', 'shoe_size' => 9.5}} doc = {'doc' => {'age' => 42, 'name' => 'Spongebob', 'shoe_size' => 9.5}}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_oid def test_oid
doc = {'doc' => ObjectID.new} doc = {'doc' => ObjectID.new}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_array def test_array
doc = {'doc' => [1, 2, 'a', 'b']} doc = {'doc' => [1, 2, 'a', 'b']}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_regex def test_regex
doc = {'doc' => /foobar/i} doc = {'doc' => /foobar/i}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
doc2 = BSON.deserialize(bson) doc2 = Mongo::BSON_CODER.deserialize(bson)
assert_equal doc, doc2 assert_equal doc, doc2
r = doc2['doc'] r = doc2['doc']
@ -151,9 +151,9 @@ class BSONTest < Test::Unit::TestCase
assert_equal 'zywcab', r.extra_options_str assert_equal 'zywcab', r.extra_options_str
doc = {'doc' => r} doc = {'doc' => r}
bson_doc = BSON.serialize(doc) bson_doc = Mongo::BSON_CODER.serialize(doc)
doc2 = nil doc2 = nil
doc2 = BSON.deserialize(bson_doc) doc2 = Mongo::BSON_CODER.deserialize(bson_doc)
assert_equal doc, doc2 assert_equal doc, doc2
r = doc2['doc'] r = doc2['doc']
@ -163,30 +163,30 @@ class BSONTest < Test::Unit::TestCase
def test_boolean def test_boolean
doc = {'doc' => true} doc = {'doc' => true}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
assert_equal doc, BSON.deserialize(bson) assert_equal doc, Mongo::BSON_CODER.deserialize(bson)
end end
def test_date def test_date
doc = {'date' => Time.now} doc = {'date' => Time.now}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
doc2 = BSON.deserialize(bson) doc2 = Mongo::BSON_CODER.deserialize(bson)
# Mongo only stores up to the millisecond # Mongo only stores up to the millisecond
assert_in_delta doc['date'], doc2['date'], 0.001 assert_in_delta doc['date'], doc2['date'], 0.001
end end
def test_date_returns_as_utc def test_date_returns_as_utc
doc = {'date' => Time.now} doc = {'date' => Time.now}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
doc2 = BSON.deserialize(bson) doc2 = Mongo::BSON_CODER.deserialize(bson)
assert doc2['date'].utc? assert doc2['date'].utc?
end end
def test_date_before_epoch def test_date_before_epoch
begin begin
doc = {'date' => Time.utc(1600)} doc = {'date' => Time.utc(1600)}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
doc2 = BSON.deserialize(bson) doc2 = Mongo::BSON_CODER.deserialize(bson)
# Mongo only stores up to the millisecond # Mongo only stores up to the millisecond
assert_in_delta doc['date'], doc2['date'], 0.001 assert_in_delta doc['date'], doc2['date'], 0.001
rescue ArgumentError rescue ArgumentError
@ -201,7 +201,7 @@ class BSONTest < Test::Unit::TestCase
[DateTime.now, Date.today, Zone].each do |invalid_date| [DateTime.now, Date.today, Zone].each do |invalid_date|
doc = {:date => invalid_date} doc = {:date => invalid_date}
begin begin
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
rescue => e rescue => e
ensure ensure
assert_equal InvalidDocument, e.class assert_equal InvalidDocument, e.class
@ -214,16 +214,16 @@ class BSONTest < Test::Unit::TestCase
oid = ObjectID.new oid = ObjectID.new
doc = {} doc = {}
doc['dbref'] = DBRef.new('namespace', oid) doc['dbref'] = DBRef.new('namespace', oid)
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
doc2 = BSON.deserialize(bson) doc2 = Mongo::BSON_CODER.deserialize(bson)
assert_equal 'namespace', doc2['dbref'].namespace assert_equal 'namespace', doc2['dbref'].namespace
assert_equal oid, doc2['dbref'].object_id assert_equal oid, doc2['dbref'].object_id
end end
def test_symbol def test_symbol
doc = {'sym' => :foo} doc = {'sym' => :foo}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
doc2 = BSON.deserialize(bson) doc2 = Mongo::BSON_CODER.deserialize(bson)
assert_equal :foo, doc2['sym'] assert_equal :foo, doc2['sym']
end end
@ -232,8 +232,8 @@ class BSONTest < Test::Unit::TestCase
'binstring'.each_byte { |b| bin.put(b) } 'binstring'.each_byte { |b| bin.put(b) }
doc = {'bin' => bin} doc = {'bin' => bin}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
doc2 = BSON.deserialize(bson) doc2 = Mongo::BSON_CODER.deserialize(bson)
bin2 = doc2['bin'] bin2 = doc2['bin']
assert_kind_of Binary, bin2 assert_kind_of Binary, bin2
assert_equal 'binstring', bin2.to_s assert_equal 'binstring', bin2.to_s
@ -244,8 +244,8 @@ class BSONTest < Test::Unit::TestCase
bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_USER_DEFINED) bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_USER_DEFINED)
doc = {'bin' => bin} doc = {'bin' => bin}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
doc2 = BSON.deserialize(bson) doc2 = Mongo::BSON_CODER.deserialize(bson)
bin2 = doc2['bin'] bin2 = doc2['bin']
assert_kind_of Binary, bin2 assert_kind_of Binary, bin2
assert_equal [1, 2, 3, 4, 5], bin2.to_a assert_equal [1, 2, 3, 4, 5], bin2.to_a
@ -257,8 +257,8 @@ class BSONTest < Test::Unit::TestCase
5.times { |i| bb.put(i + 1) } 5.times { |i| bb.put(i + 1) }
doc = {'bin' => bb} doc = {'bin' => bb}
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
doc2 = BSON.deserialize(bson) doc2 = Mongo::BSON_CODER.deserialize(bson)
bin2 = doc2['bin'] bin2 = doc2['bin']
assert_kind_of Binary, bin2 assert_kind_of Binary, bin2
assert_equal [1, 2, 3, 4, 5], bin2.to_a assert_equal [1, 2, 3, 4, 5], bin2.to_a
@ -269,24 +269,24 @@ class BSONTest < Test::Unit::TestCase
val = OrderedHash.new val = OrderedHash.new
val['not_id'] = 1 val['not_id'] = 1
val['_id'] = 2 val['_id'] = 2
roundtrip = BSON.deserialize(BSON.serialize(val, false, true).to_a) roundtrip = Mongo::BSON_CODER.deserialize(Mongo::BSON_CODER.serialize(val, false, true).to_a)
assert_kind_of OrderedHash, roundtrip assert_kind_of OrderedHash, roundtrip
assert_equal '_id', roundtrip.keys.first assert_equal '_id', roundtrip.keys.first
val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'} val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
roundtrip = BSON.deserialize(BSON.serialize(val, false, true).to_a) roundtrip = Mongo::BSON_CODER.deserialize(Mongo::BSON_CODER.serialize(val, false, true).to_a)
assert_kind_of OrderedHash, roundtrip assert_kind_of OrderedHash, roundtrip
assert_equal '_id', roundtrip.keys.first assert_equal '_id', roundtrip.keys.first
end end
def test_nil_id def test_nil_id
doc = {"_id" => nil} doc = {"_id" => nil}
assert_equal doc, BSON.deserialize(bson = BSON.serialize(doc, false, true).to_a) assert_equal doc, Mongo::BSON_CODER.deserialize(bson = Mongo::BSON_CODER.serialize(doc, false, true).to_a)
end end
def test_timestamp def test_timestamp
val = {"test" => [4, 20]} val = {"test" => [4, 20]}
assert_equal val, BSON.deserialize([0x13, 0x00, 0x00, 0x00, assert_equal val, Mongo::BSON_CODER.deserialize([0x13, 0x00, 0x00, 0x00,
0x11, 0x74, 0x65, 0x73, 0x11, 0x74, 0x65, 0x73,
0x74, 0x00, 0x04, 0x00, 0x74, 0x00, 0x04, 0x00,
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00,
@ -296,29 +296,29 @@ class BSONTest < Test::Unit::TestCase
def test_overflow def test_overflow
doc = {"x" => 2**75} doc = {"x" => 2**75}
assert_raise RangeError do assert_raise RangeError do
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
end end
doc = {"x" => 9223372036854775} doc = {"x" => 9223372036854775}
assert_equal doc, BSON.deserialize(BSON.serialize(doc).to_a) assert_equal doc, Mongo::BSON_CODER.deserialize(Mongo::BSON_CODER.serialize(doc).to_a)
doc = {"x" => 9223372036854775807} doc = {"x" => 9223372036854775807}
assert_equal doc, BSON.deserialize(BSON.serialize(doc).to_a) assert_equal doc, Mongo::BSON_CODER.deserialize(Mongo::BSON_CODER.serialize(doc).to_a)
doc["x"] = doc["x"] + 1 doc["x"] = doc["x"] + 1
assert_raise RangeError do assert_raise RangeError do
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
end end
doc = {"x" => -9223372036854775} doc = {"x" => -9223372036854775}
assert_equal doc, BSON.deserialize(BSON.serialize(doc).to_a) assert_equal doc, Mongo::BSON_CODER.deserialize(Mongo::BSON_CODER.serialize(doc).to_a)
doc = {"x" => -9223372036854775808} doc = {"x" => -9223372036854775808}
assert_equal doc, BSON.deserialize(BSON.serialize(doc).to_a) assert_equal doc, Mongo::BSON_CODER.deserialize(Mongo::BSON_CODER.serialize(doc).to_a)
doc["x"] = doc["x"] - 1 doc["x"] = doc["x"] - 1
assert_raise RangeError do assert_raise RangeError do
bson = BSON.serialize(doc) bson = Mongo::BSON_CODER.serialize(doc)
end end
end end
@ -326,7 +326,7 @@ class BSONTest < Test::Unit::TestCase
[BigDecimal.new("1.0"), Complex(0, 1), Rational(2, 3)].each do |type| [BigDecimal.new("1.0"), Complex(0, 1), Rational(2, 3)].each do |type|
doc = {"x" => type} doc = {"x" => type}
begin begin
BSON.serialize(doc) Mongo::BSON_CODER.serialize(doc)
rescue => e rescue => e
ensure ensure
assert_equal InvalidDocument, e.class assert_equal InvalidDocument, e.class
@ -340,12 +340,12 @@ class BSONTest < Test::Unit::TestCase
val['not_id'] = 1 val['not_id'] = 1
val['_id'] = 2 val['_id'] = 2
assert val.keys.include?('_id') assert val.keys.include?('_id')
BSON.serialize(val) Mongo::BSON_CODER.serialize(val)
assert val.keys.include?('_id') assert val.keys.include?('_id')
val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'} val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
assert val.keys.include?(:_id) assert val.keys.include?(:_id)
BSON.serialize(val) Mongo::BSON_CODER.serialize(val)
assert val.keys.include?(:_id) assert val.keys.include?(:_id)
end end
@ -360,50 +360,50 @@ class BSONTest < Test::Unit::TestCase
dup = {"_id" => "foo", :_id => "foo"} dup = {"_id" => "foo", :_id => "foo"}
one = {"_id" => "foo"} one = {"_id" => "foo"}
assert_equal BSON.serialize(one).to_a, BSON.serialize(dup).to_a assert_equal Mongo::BSON_CODER.serialize(one).to_a, Mongo::BSON_CODER.serialize(dup).to_a
end end
def test_no_duplicate_id_when_moving_id def test_no_duplicate_id_when_moving_id
dup = {"_id" => "foo", :_id => "foo"} dup = {"_id" => "foo", :_id => "foo"}
one = {:_id => "foo"} one = {:_id => "foo"}
assert_equal BSON.serialize(one, false, true).to_s, BSON.serialize(dup, false, true).to_s assert_equal Mongo::BSON_CODER.serialize(one, false, true).to_s, Mongo::BSON_CODER.serialize(dup, false, true).to_s
end end
def test_null_character def test_null_character
doc = {"a" => "\x00"} doc = {"a" => "\x00"}
assert_equal doc, BSON.deserialize(BSON.serialize(doc).to_a) assert_equal doc, Mongo::BSON_CODER.deserialize(Mongo::BSON_CODER.serialize(doc).to_a)
assert_raise InvalidDocument do assert_raise InvalidDocument do
BSON.serialize({"\x00" => "a"}) Mongo::BSON_CODER.serialize({"\x00" => "a"})
end end
assert_raise InvalidDocument do assert_raise InvalidDocument do
BSON.serialize({"a" => (Regexp.compile "ab\x00c")}) Mongo::BSON_CODER.serialize({"a" => (Regexp.compile "ab\x00c")})
end end
end end
def test_max_key def test_max_key
doc = {"a" => MaxKey.new} doc = {"a" => MaxKey.new}
assert_equal doc, BSON.deserialize(BSON.serialize(doc).to_a) assert_equal doc, Mongo::BSON_CODER.deserialize(Mongo::BSON_CODER.serialize(doc).to_a)
end end
def test_min_key def test_min_key
doc = {"a" => MinKey.new} doc = {"a" => MinKey.new}
assert_equal doc, BSON.deserialize(BSON.serialize(doc).to_a) assert_equal doc, Mongo::BSON_CODER.deserialize(Mongo::BSON_CODER.serialize(doc).to_a)
end end
def test_invalid_object def test_invalid_object
o = Object.new o = Object.new
assert_raise InvalidDocument do assert_raise InvalidDocument do
BSON.serialize({:foo => o}) Mongo::BSON_CODER.serialize({:foo => o})
end end
assert_raise InvalidDocument do assert_raise InvalidDocument do
BSON.serialize({:foo => Date.today}) Mongo::BSON_CODER.serialize({:foo => Date.today})
end end
end end
@ -416,10 +416,10 @@ class BSONTest < Test::Unit::TestCase
assert_equal ")\000\000\000\020_id\000\001\000\000\000\002text" + assert_equal ")\000\000\000\020_id\000\001\000\000\000\002text" +
"\000\004\000\000\000abc\000\002key\000\004\000\000\000abc\000\000", "\000\004\000\000\000abc\000\002key\000\004\000\000\000abc\000\000",
BSON.serialize(a, false, true).to_s Mongo::BSON_CODER.serialize(a, false, true).to_s
assert_equal ")\000\000\000\002text\000\004\000\000\000abc\000\002key" + assert_equal ")\000\000\000\002text\000\004\000\000\000abc\000\002key" +
"\000\004\000\000\000abc\000\020_id\000\001\000\000\000\000", "\000\004\000\000\000abc\000\020_id\000\001\000\000\000\000",
BSON.serialize(a, false, false).to_s Mongo::BSON_CODER.serialize(a, false, false).to_s
end end
def test_move_id_with_nested_doc def test_move_id_with_nested_doc
@ -433,11 +433,11 @@ class BSONTest < Test::Unit::TestCase
assert_equal ">\000\000\000\020_id\000\003\000\000\000\002text" + assert_equal ">\000\000\000\020_id\000\003\000\000\000\002text" +
"\000\004\000\000\000abc\000\003hash\000\034\000\000" + "\000\004\000\000\000abc\000\003hash\000\034\000\000" +
"\000\002text\000\004\000\000\000abc\000\020_id\000\002\000\000\000\000\000", "\000\002text\000\004\000\000\000abc\000\020_id\000\002\000\000\000\000\000",
BSON.serialize(c, false, true).to_s Mongo::BSON_CODER.serialize(c, false, true).to_s
assert_equal ">\000\000\000\002text\000\004\000\000\000abc\000\003hash" + assert_equal ">\000\000\000\002text\000\004\000\000\000abc\000\003hash" +
"\000\034\000\000\000\002text\000\004\000\000\000abc\000\020_id" + "\000\034\000\000\000\002text\000\004\000\000\000abc\000\020_id" +
"\000\002\000\000\000\000\020_id\000\003\000\000\000\000", "\000\002\000\000\000\000\020_id\000\003\000\000\000\000",
BSON.serialize(c, false, false).to_s Mongo::BSON_CODER.serialize(c, false, false).to_s
end end
if defined?(HashWithIndifferentAccess) if defined?(HashWithIndifferentAccess)
@ -447,12 +447,12 @@ class BSONTest < Test::Unit::TestCase
embedded['_id'] = ObjectID.new embedded['_id'] = ObjectID.new
doc['_id'] = ObjectID.new doc['_id'] = ObjectID.new
doc['embedded'] = [embedded] doc['embedded'] = [embedded]
BSON.serialize(doc, false, true).to_a Mongo::BSON_CODER.serialize(doc, false, true).to_a
assert doc.has_key?("_id") assert doc.has_key?("_id")
assert doc['embedded'][0].has_key?("_id") assert doc['embedded'][0].has_key?("_id")
doc['_id'] = ObjectID.new doc['_id'] = ObjectID.new
BSON.serialize(doc, false, true).to_a Mongo::BSON_CODER.serialize(doc, false, true).to_a
assert doc.has_key?("_id") assert doc.has_key?("_id")
end end
end end

View File

@ -7,8 +7,8 @@ class DBTest < Test::Unit::TestCase
documents = [documents] unless documents.is_a?(Array) documents = [documents] unless documents.is_a?(Array)
message = ByteBuffer.new message = ByteBuffer.new
message.put_int(0) message.put_int(0)
BSON.serialize_cstr(message, "#{db.name}.test") Mongo::BSON_CODER..serialize_cstr(message, "#{db.name}.test")
documents.each { |doc| message.put_array(BSON.new.serialize(doc, true).to_a) } documents.each { |doc| message.put_array(Mongo::BSON_CODER.new.serialize(doc, true).to_a) }
message = db.add_message_headers(Mongo::Constants::OP_INSERT, message) message = db.add_message_headers(Mongo::Constants::OP_INSERT, message)
end end
end end