RUBY-255 Support BSON Timestamp
This commit is contained in:
parent
baa1d0b802
commit
05bf234bb8
|
@ -86,6 +86,7 @@ static VALUE DBRef;
|
|||
static VALUE Code;
|
||||
static VALUE MinKey;
|
||||
static VALUE MaxKey;
|
||||
static VALUE Timestamp;
|
||||
static VALUE Regexp;
|
||||
static VALUE OrderedHash;
|
||||
static VALUE InvalidKeyName;
|
||||
|
@ -433,6 +434,17 @@ static int write_element(VALUE key, VALUE value, VALUE extra, int allow_id) {
|
|||
write_name_and_type(buffer, key, 0xff);
|
||||
break;
|
||||
}
|
||||
if (strcmp(cls, "BSON::Timestamp") == 0) {
|
||||
write_name_and_type(buffer, key, 0x11);
|
||||
int seconds = FIX2INT(
|
||||
rb_funcall(value, rb_intern("seconds"), 0));
|
||||
int increment = FIX2INT(
|
||||
rb_funcall(value, rb_intern("increment"), 0));
|
||||
|
||||
SAFE_WRITE(buffer, (const char*)&increment, 4);
|
||||
SAFE_WRITE(buffer, (const char*)&seconds, 4);
|
||||
break;
|
||||
}
|
||||
if (strcmp(cls, "DateTime") == 0 || strcmp(cls, "Date") == 0 || strcmp(cls, "ActiveSupport::TimeWithZone") == 0) {
|
||||
buffer_free(buffer);
|
||||
rb_raise(InvalidDocument, "%s is not currently supported; use a UTC Time instance instead.", cls);
|
||||
|
@ -813,11 +825,13 @@ static VALUE get_value(const char* buffer, int* position, int type) {
|
|||
}
|
||||
case 17:
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
memcpy(&i, buffer + *position, 4);
|
||||
memcpy(&j, buffer + *position + 4, 4);
|
||||
value = rb_ary_new3(2, LL2NUM(i), LL2NUM(j));
|
||||
int sec, inc;
|
||||
VALUE argv[2];
|
||||
memcpy(&inc, buffer + *position, 4);
|
||||
memcpy(&sec, buffer + *position + 4, 4);
|
||||
argv[0] = INT2FIX(sec);
|
||||
argv[1] = INT2FIX(inc);
|
||||
value = rb_class_new_instance(2, argv, Timestamp);
|
||||
*position += 8;
|
||||
break;
|
||||
}
|
||||
|
@ -940,6 +954,8 @@ void Init_cbson() {
|
|||
rb_require("bson/types/min_max_keys");
|
||||
MinKey = rb_const_get(bson, rb_intern("MinKey"));
|
||||
MaxKey = rb_const_get(bson, rb_intern("MaxKey"));
|
||||
rb_require("bson/types/timestamp");
|
||||
Timestamp = rb_const_get(bson, rb_intern("Timestamp"));
|
||||
Regexp = rb_const_get(rb_cObject, rb_intern("Regexp"));
|
||||
rb_require("bson/exceptions");
|
||||
InvalidKeyName = rb_const_get(bson, rb_intern("InvalidKeyName"));
|
||||
|
|
Binary file not shown.
|
@ -32,6 +32,7 @@ public class RubyBSONCallback implements BSONCallback {
|
|||
private RubyModule _rbclsBinary;
|
||||
private RubyModule _rbclsMinKey;
|
||||
private RubyModule _rbclsMaxKey;
|
||||
private RubyModule _rbclsTimestamp;
|
||||
private RubyModule _rbclsDBRef;
|
||||
private RubyModule _rbclsCode;
|
||||
private final LinkedList<RubyObject> _stack = new LinkedList<RubyObject>();
|
||||
|
@ -47,6 +48,7 @@ public class RubyBSONCallback implements BSONCallback {
|
|||
_rbclsCode = _lookupConstant( _runtime, "BSON::Code" );
|
||||
_rbclsMinKey = _lookupConstant( _runtime, "BSON::MinKey" );
|
||||
_rbclsMaxKey = _lookupConstant( _runtime, "BSON::MaxKey" );
|
||||
_rbclsTimestamp = _lookupConstant( _runtime, "BSON::Timestamp" );
|
||||
_rbclsObjectId = _lookupConstant( _runtime, "BSON::ObjectId");
|
||||
}
|
||||
|
||||
|
@ -250,17 +252,16 @@ public class RubyBSONCallback implements BSONCallback {
|
|||
_put( name , symbol );
|
||||
}
|
||||
|
||||
// Timestamp is currently rendered in Ruby as a two-element array.
|
||||
public void gotTimestamp( String name , int time , int inc ){
|
||||
RubyFixnum rtime = RubyFixnum.newFixnum( _runtime, time );
|
||||
RubyFixnum rinc = RubyFixnum.newFixnum( _runtime, inc );
|
||||
RubyObject[] args = new RubyObject[2];
|
||||
args[0] = rinc;
|
||||
args[1] = rtime;
|
||||
args[0] = rtime;
|
||||
args[1] = rinc;
|
||||
|
||||
RubyArray result = RubyArray.newArray( _runtime, args );
|
||||
Object result = JavaEmbedUtils.invokeMethod(_runtime, _rbclsTimestamp, "new", args, Object.class);
|
||||
|
||||
_put ( name , result );
|
||||
_put ( name , (RubyObject)result );
|
||||
}
|
||||
|
||||
public void gotObjectId( String name , ObjectId id ){
|
||||
|
|
|
@ -384,6 +384,9 @@ public class RubyBSONEncoder extends BSONEncoder {
|
|||
else if ( klass.equals("BSON::MaxKey") ) {
|
||||
_put( MAXKEY, name );
|
||||
}
|
||||
else if ( klass.equals("BSON::Timestamp") ) {
|
||||
putRubyTimestamp( name, (RubyObject)val );
|
||||
}
|
||||
else if ( klass.equals("BSON::DBRef") ) {
|
||||
RubyHash ref = (RubyHash)JavaEmbedUtils.invokeMethod(_runtime, val,
|
||||
"to_hash", new Object[] {}, Object.class);
|
||||
|
@ -504,6 +507,18 @@ public class RubyBSONEncoder extends BSONEncoder {
|
|||
_buf.writeInt( ts.getTime() );
|
||||
}
|
||||
|
||||
protected void putRubyTimestamp(String name, RubyObject ts ){
|
||||
_put( TIMESTAMP , name );
|
||||
|
||||
Number inc = (Number)JavaEmbedUtils.invokeMethod(_runtime, ts,
|
||||
"increment", new Object[] {}, Object.class);
|
||||
Number sec = (Number)JavaEmbedUtils.invokeMethod(_runtime, ts,
|
||||
"seconds", new Object[] {}, Object.class);
|
||||
|
||||
_buf.writeInt( (int)inc.longValue() );
|
||||
_buf.writeInt( (int)sec.longValue() );
|
||||
}
|
||||
|
||||
protected void putRubyCodeWScope( String name , RubyObject code ){
|
||||
_put( CODE_W_SCOPE , name );
|
||||
int temp = _buf.getPosition();
|
||||
|
|
|
@ -100,6 +100,7 @@ require 'bson/types/code'
|
|||
require 'bson/types/dbref'
|
||||
require 'bson/types/object_id'
|
||||
require 'bson/types/min_max_keys'
|
||||
require 'bson/types/timestamp'
|
||||
|
||||
require 'base64'
|
||||
require 'bson/ordered_hash'
|
||||
|
|
|
@ -192,6 +192,8 @@ module BSON
|
|||
serialize_max_key_element(@buf, k)
|
||||
when MINKEY
|
||||
serialize_min_key_element(@buf, k)
|
||||
when TIMESTAMP
|
||||
serialize_timestamp_element(@buf, k, v)
|
||||
else
|
||||
raise "unhandled type #{type}"
|
||||
end
|
||||
|
@ -261,8 +263,7 @@ module BSON
|
|||
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)]
|
||||
doc[key] = deserialize_timestamp_data(@buf)
|
||||
when MAXKEY
|
||||
key = deserialize_cstr(@buf)
|
||||
doc[key] = MaxKey.new
|
||||
|
@ -346,6 +347,12 @@ module BSON
|
|||
Regexp.new(str, opts)
|
||||
end
|
||||
|
||||
def deserialize_timestamp_data(buf)
|
||||
increment = buf.get_int
|
||||
seconds = buf.get_int
|
||||
Timestamp.new(seconds, increment)
|
||||
end
|
||||
|
||||
def encoded_str(str)
|
||||
if RUBY_VERSION >= '1.9'
|
||||
str.force_encoding("utf-8")
|
||||
|
@ -510,6 +517,14 @@ module BSON
|
|||
self.class.serialize_key(buf, key)
|
||||
end
|
||||
|
||||
def serialize_timestamp_element(buf, key, val)
|
||||
buf.put(TIMESTAMP)
|
||||
self.class.serialize_key(buf, key)
|
||||
|
||||
buf.put_int(val.increment)
|
||||
buf.put_int(val.seconds)
|
||||
end
|
||||
|
||||
def serialize_oid_element(buf, key, val)
|
||||
buf.put(OID)
|
||||
self.class.serialize_key(buf, key)
|
||||
|
@ -598,6 +613,8 @@ module BSON
|
|||
MAXKEY
|
||||
when MinKey
|
||||
MINKEY
|
||||
when Timestamp
|
||||
TIMESTAMP
|
||||
when Numeric
|
||||
raise InvalidDocument, "Cannot serialize the Numeric type #{o.class} as BSON; only Fixum, Bignum, and Float are supported."
|
||||
when Date, DateTime
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# encoding: UTF-8
|
||||
|
||||
# --
|
||||
# Copyright (C) 2008-2011 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 BSON
|
||||
|
||||
# A class for representing BSON Timestamps. The Timestamp type is used
|
||||
# by MongoDB internally; thus, it should be used by application developers
|
||||
# for diagnostic purposes only.
|
||||
class Timestamp
|
||||
include Enumerable
|
||||
|
||||
attr_reader :seconds, :increment
|
||||
|
||||
# Create a new BSON Timestamp.
|
||||
#
|
||||
# @param [Integer] seconds The number of seconds
|
||||
# @param increment
|
||||
def initialize(seconds, increment)
|
||||
@seconds = seconds
|
||||
@increment = increment
|
||||
end
|
||||
|
||||
def to_s
|
||||
"seconds: #{seconds}, increment: #{increment}"
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
self.seconds == other.seconds &&
|
||||
self.increment == other.increment
|
||||
end
|
||||
|
||||
# This is for backward-compatibility. Timestamps in the Ruby
|
||||
# driver used to deserialize as arrays. So we provide
|
||||
# an equivalent interface.
|
||||
#
|
||||
# @deprecated
|
||||
def [](index)
|
||||
warn "Timestamps are no longer deserialized as arrays. If you're working " +
|
||||
"with BSON Timestamp objects, see the BSON::Timestamp class. This usage will " +
|
||||
"be deprecated in Ruby Driver v2.0."
|
||||
if index == 0
|
||||
self.increment
|
||||
elsif index == 1
|
||||
self.seconds
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# This method exists only for backward-compatibility.
|
||||
#
|
||||
# @deprecated
|
||||
def each
|
||||
i = 0
|
||||
while i < 2
|
||||
yield self[i]
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -410,15 +410,24 @@ class BSONTest < Test::Unit::TestCase
|
|||
if !(RUBY_PLATFORM =~ /java/)
|
||||
def test_timestamp
|
||||
val = {"test" => [4, 20]}
|
||||
assert_equal val, @encoder.deserialize([0x13, 0x00, 0x00, 0x00,
|
||||
0x11, 0x74, 0x65, 0x73,
|
||||
0x74, 0x00, 0x04, 0x00,
|
||||
0x00, 0x00, 0x14, 0x00,
|
||||
0x00, 0x00, 0x00])
|
||||
result = @encoder.deserialize([0x13, 0x00, 0x00, 0x00,
|
||||
0x11, 0x74, 0x65, 0x73,
|
||||
0x74, 0x00, 0x04, 0x00,
|
||||
0x00, 0x00, 0x14, 0x00,
|
||||
0x00, 0x00, 0x00])
|
||||
|
||||
assert_equal 4, result["test"][0]
|
||||
assert_equal 20, result["test"][1]
|
||||
end
|
||||
end
|
||||
|
||||
def test_timestamp_type
|
||||
ts = Timestamp.new(5000, 100)
|
||||
doc = {:ts => ts}
|
||||
bson = @encoder.serialize(doc)
|
||||
assert_equal ts, @encoder.deserialize(bson)["ts"]
|
||||
end
|
||||
|
||||
def test_overflow
|
||||
doc = {"x" => 2**75}
|
||||
assert_raise RangeError do
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
require './test/test_helper'
|
||||
|
||||
class TiumestampTest < Test::Unit::TestCase
|
||||
include Mongo
|
||||
|
||||
def test_timestamp_equality
|
||||
t1 = Timestamp.new(5000, 200)
|
||||
t2 = Timestamp.new(5000, 200)
|
||||
assert_equal t2, t1
|
||||
end
|
||||
|
||||
def test_implements_array_for_backward_compatibility
|
||||
ts = Timestamp.new(5000, 200)
|
||||
assert_equal 200, ts[0]
|
||||
assert_equal 5000, ts[1]
|
||||
|
||||
array = ts.map {|t| t }
|
||||
assert_equal 2, array.length
|
||||
|
||||
assert_equal 200, array[0]
|
||||
assert_equal 5000, array[1]
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue