From 95d9d6b4f69cae08de132c7d116208b6f27b2574 Mon Sep 17 00:00:00 2001 From: Kyle Banker Date: Thu, 21 Jan 2010 14:49:20 -0500 Subject: [PATCH] Handle unsupported types: Complex, Rational, and BigDecimal RUBY-85 --- ext/cbson/cbson.c | 25 ++++++++++++++++++------- lib/mongo/util/bson_ruby.rb | 14 ++++++++------ test/test_bson.rb | 19 ++++++++++++++++++- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/ext/cbson/cbson.c b/ext/cbson/cbson.c index 80a0c3f..619e343 100644 --- a/ext/cbson/cbson.c +++ b/ext/cbson/cbson.c @@ -274,7 +274,7 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow case T_STRING: { if (strcmp(rb_class2name(RBASIC(value)->klass), - "Mongo::Code") == 0) { + "Mongo::Code") == 0) { buffer_position length_location, start_position, total_length; int length; write_name_and_type(buffer, key, 0x0F); @@ -380,10 +380,14 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow break; } if (strcmp(cls, "DateTime") == 0 || strcmp(cls, "Date") == 0 || strcmp(cls, "ActiveSupport::TimeWithZone") == 0) { - buffer_free(buffer); - rb_raise(InvalidDocument, - "Trying to serialize and instance of Date, DateTime, or TimeWithZone; the MongoDB Ruby driver currently supports Time objects only.", - TYPE(value)); + buffer_free(buffer); + rb_raise(InvalidDocument, "%s is not currently supported; use a UTC Time instance instead.", cls); + break; + } + if(strcmp(cls, "Complex") == 0 || strcmp(cls, "Rational") == 0 || strcmp(cls, "BigDecimal") == 0) { + buffer_free(buffer); + rb_raise(InvalidDocument, "The Numeric type %s cannot be encoded as BSON; only Bignum, Fixnum, and Float are supported.", cls); + break; } buffer_free(buffer); rb_raise(InvalidDocument, "Cannot serialize an object of class %s into BSON.", cls); @@ -391,7 +395,6 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow } case T_DATA: { - // TODO again, is this really the only way to do this? const char* cls = rb_class2name(RBASIC(value)->klass); if (strcmp(cls, "Time") == 0) { double t = NUM2DBL(rb_funcall(value, rb_intern("to_f"), 0)); @@ -400,6 +403,14 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow SAFE_WRITE(buffer, (const char*)&time_since_epoch, 8); break; } + if(strcmp(cls, "BigDecimal") == 0) { + buffer_free(buffer); + rb_raise(InvalidDocument, "The Numeric type %s cannot be encoded as BSON; only Bignum, Fixnum, and Float are supported.", cls); + break; + } + buffer_free(buffer); + rb_raise(InvalidDocument, "Cannot serialize an object of class %s into BSON.", cls); + break; } case T_REGEXP: { @@ -441,7 +452,7 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow { const char* cls = rb_class2name(RBASIC(value)->klass); buffer_free(buffer); - rb_raise(InvalidDocument, "Cannot serialize an object of class %s into BSON.", cls); + rb_raise(InvalidDocument, "Cannot serialize an object of class %s (type %d) into BSON.", cls, TYPE(value)); break; } } diff --git a/lib/mongo/util/bson_ruby.rb b/lib/mongo/util/bson_ruby.rb index edc88a7..4f85303 100644 --- a/lib/mongo/util/bson_ruby.rb +++ b/lib/mongo/util/bson_ruby.rb @@ -549,7 +549,7 @@ class BSON_RUBY NULL when Integer NUMBER_INT - when Numeric + when Float NUMBER when ByteBuffer BINARY @@ -577,15 +577,17 @@ class BSON_RUBY MAXKEY when MinKey MINKEY + when Numeric + raise InvalidDocument, "The Numeric type #{o.class} cannot be encoded as BSON; only Fixum, Bignum, and Float are supported." when Date, DateTime - raise InvalidDocument, "Trying to serialize an instance of #{o.class}; " + - "the MongoDB Ruby driver currently supports Time objects only." + 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, "Trying to serialize an instance of ActiveSupport::TimeWithZone; " + - "the MongoDB Ruby driver currently supports Time objects only." + raise InvalidDocument, "ActiveSupport::TimeWithZone is not currently supported; " + + "use a UTC Time instance instead." else - raise InvalidDocument, "Unknown type of object: #{o.class.name}" + raise InvalidDocument, "#{o.class} isn't supported as a BSON type." end end end diff --git a/test/test_bson.rb b/test/test_bson.rb index 5c9f14f..a876788 100644 --- a/test/test_bson.rb +++ b/test/test_bson.rb @@ -1,5 +1,8 @@ # encoding:utf-8 require 'test/test_helper' +require 'complex' +require 'bigdecimal' +require 'rational' # Need to simulating this class # without actually loading it. @@ -197,7 +200,7 @@ class BSONTest < Test::Unit::TestCase ensure puts e.message assert_equal InvalidDocument, e.class - assert_match /Time objects only/, e.message + assert_match /UTC Time/, e.message end end end @@ -314,6 +317,20 @@ class BSONTest < Test::Unit::TestCase end end + def test_invalid_numeric_types + [BigDecimal.new("1.0"), Complex.new(0, 1), Rational.new!(2, 3)].each do |type| + print type.class + doc = {"x" => type} + begin + BSON.serialize(doc) + rescue => e + ensure + assert_equal InvalidDocument, e.class + assert_match /Numeric type #{type.class}/, e.message + end + end + end + def test_do_not_change_original_object val = OrderedHash.new val['not_id'] = 1