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 Code;
|
||||||
static VALUE MinKey;
|
static VALUE MinKey;
|
||||||
static VALUE MaxKey;
|
static VALUE MaxKey;
|
||||||
|
static VALUE Timestamp;
|
||||||
static VALUE Regexp;
|
static VALUE Regexp;
|
||||||
static VALUE OrderedHash;
|
static VALUE OrderedHash;
|
||||||
static VALUE InvalidKeyName;
|
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);
|
write_name_and_type(buffer, key, 0xff);
|
||||||
break;
|
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) {
|
if (strcmp(cls, "DateTime") == 0 || strcmp(cls, "Date") == 0 || strcmp(cls, "ActiveSupport::TimeWithZone") == 0) {
|
||||||
buffer_free(buffer);
|
buffer_free(buffer);
|
||||||
rb_raise(InvalidDocument, "%s is not currently supported; use a UTC Time instance instead.", cls);
|
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:
|
case 17:
|
||||||
{
|
{
|
||||||
int i;
|
int sec, inc;
|
||||||
int j;
|
VALUE argv[2];
|
||||||
memcpy(&i, buffer + *position, 4);
|
memcpy(&inc, buffer + *position, 4);
|
||||||
memcpy(&j, buffer + *position + 4, 4);
|
memcpy(&sec, buffer + *position + 4, 4);
|
||||||
value = rb_ary_new3(2, LL2NUM(i), LL2NUM(j));
|
argv[0] = INT2FIX(sec);
|
||||||
|
argv[1] = INT2FIX(inc);
|
||||||
|
value = rb_class_new_instance(2, argv, Timestamp);
|
||||||
*position += 8;
|
*position += 8;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -940,6 +954,8 @@ void Init_cbson() {
|
|||||||
rb_require("bson/types/min_max_keys");
|
rb_require("bson/types/min_max_keys");
|
||||||
MinKey = rb_const_get(bson, rb_intern("MinKey"));
|
MinKey = rb_const_get(bson, rb_intern("MinKey"));
|
||||||
MaxKey = rb_const_get(bson, rb_intern("MaxKey"));
|
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"));
|
Regexp = rb_const_get(rb_cObject, rb_intern("Regexp"));
|
||||||
rb_require("bson/exceptions");
|
rb_require("bson/exceptions");
|
||||||
InvalidKeyName = rb_const_get(bson, rb_intern("InvalidKeyName"));
|
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 _rbclsBinary;
|
||||||
private RubyModule _rbclsMinKey;
|
private RubyModule _rbclsMinKey;
|
||||||
private RubyModule _rbclsMaxKey;
|
private RubyModule _rbclsMaxKey;
|
||||||
|
private RubyModule _rbclsTimestamp;
|
||||||
private RubyModule _rbclsDBRef;
|
private RubyModule _rbclsDBRef;
|
||||||
private RubyModule _rbclsCode;
|
private RubyModule _rbclsCode;
|
||||||
private final LinkedList<RubyObject> _stack = new LinkedList<RubyObject>();
|
private final LinkedList<RubyObject> _stack = new LinkedList<RubyObject>();
|
||||||
@ -47,6 +48,7 @@ public class RubyBSONCallback implements BSONCallback {
|
|||||||
_rbclsCode = _lookupConstant( _runtime, "BSON::Code" );
|
_rbclsCode = _lookupConstant( _runtime, "BSON::Code" );
|
||||||
_rbclsMinKey = _lookupConstant( _runtime, "BSON::MinKey" );
|
_rbclsMinKey = _lookupConstant( _runtime, "BSON::MinKey" );
|
||||||
_rbclsMaxKey = _lookupConstant( _runtime, "BSON::MaxKey" );
|
_rbclsMaxKey = _lookupConstant( _runtime, "BSON::MaxKey" );
|
||||||
|
_rbclsTimestamp = _lookupConstant( _runtime, "BSON::Timestamp" );
|
||||||
_rbclsObjectId = _lookupConstant( _runtime, "BSON::ObjectId");
|
_rbclsObjectId = _lookupConstant( _runtime, "BSON::ObjectId");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,17 +252,16 @@ public class RubyBSONCallback implements BSONCallback {
|
|||||||
_put( name , symbol );
|
_put( name , symbol );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timestamp is currently rendered in Ruby as a two-element array.
|
|
||||||
public void gotTimestamp( String name , int time , int inc ){
|
public void gotTimestamp( String name , int time , int inc ){
|
||||||
RubyFixnum rtime = RubyFixnum.newFixnum( _runtime, time );
|
RubyFixnum rtime = RubyFixnum.newFixnum( _runtime, time );
|
||||||
RubyFixnum rinc = RubyFixnum.newFixnum( _runtime, inc );
|
RubyFixnum rinc = RubyFixnum.newFixnum( _runtime, inc );
|
||||||
RubyObject[] args = new RubyObject[2];
|
RubyObject[] args = new RubyObject[2];
|
||||||
args[0] = rinc;
|
args[0] = rtime;
|
||||||
args[1] = 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 ){
|
public void gotObjectId( String name , ObjectId id ){
|
||||||
|
@ -384,6 +384,9 @@ public class RubyBSONEncoder extends BSONEncoder {
|
|||||||
else if ( klass.equals("BSON::MaxKey") ) {
|
else if ( klass.equals("BSON::MaxKey") ) {
|
||||||
_put( MAXKEY, name );
|
_put( MAXKEY, name );
|
||||||
}
|
}
|
||||||
|
else if ( klass.equals("BSON::Timestamp") ) {
|
||||||
|
putRubyTimestamp( name, (RubyObject)val );
|
||||||
|
}
|
||||||
else if ( klass.equals("BSON::DBRef") ) {
|
else if ( klass.equals("BSON::DBRef") ) {
|
||||||
RubyHash ref = (RubyHash)JavaEmbedUtils.invokeMethod(_runtime, val,
|
RubyHash ref = (RubyHash)JavaEmbedUtils.invokeMethod(_runtime, val,
|
||||||
"to_hash", new Object[] {}, Object.class);
|
"to_hash", new Object[] {}, Object.class);
|
||||||
@ -504,6 +507,18 @@ public class RubyBSONEncoder extends BSONEncoder {
|
|||||||
_buf.writeInt( ts.getTime() );
|
_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 ){
|
protected void putRubyCodeWScope( String name , RubyObject code ){
|
||||||
_put( CODE_W_SCOPE , name );
|
_put( CODE_W_SCOPE , name );
|
||||||
int temp = _buf.getPosition();
|
int temp = _buf.getPosition();
|
||||||
|
@ -100,6 +100,7 @@ require 'bson/types/code'
|
|||||||
require 'bson/types/dbref'
|
require 'bson/types/dbref'
|
||||||
require 'bson/types/object_id'
|
require 'bson/types/object_id'
|
||||||
require 'bson/types/min_max_keys'
|
require 'bson/types/min_max_keys'
|
||||||
|
require 'bson/types/timestamp'
|
||||||
|
|
||||||
require 'base64'
|
require 'base64'
|
||||||
require 'bson/ordered_hash'
|
require 'bson/ordered_hash'
|
||||||
|
@ -192,6 +192,8 @@ module BSON
|
|||||||
serialize_max_key_element(@buf, k)
|
serialize_max_key_element(@buf, k)
|
||||||
when MINKEY
|
when MINKEY
|
||||||
serialize_min_key_element(@buf, k)
|
serialize_min_key_element(@buf, k)
|
||||||
|
when TIMESTAMP
|
||||||
|
serialize_timestamp_element(@buf, k, v)
|
||||||
else
|
else
|
||||||
raise "unhandled type #{type}"
|
raise "unhandled type #{type}"
|
||||||
end
|
end
|
||||||
@ -261,8 +263,7 @@ module BSON
|
|||||||
doc[key] = deserialize_code_w_scope_data(@buf)
|
doc[key] = deserialize_code_w_scope_data(@buf)
|
||||||
when TIMESTAMP
|
when TIMESTAMP
|
||||||
key = deserialize_cstr(@buf)
|
key = deserialize_cstr(@buf)
|
||||||
doc[key] = [deserialize_number_int_data(@buf),
|
doc[key] = deserialize_timestamp_data(@buf)
|
||||||
deserialize_number_int_data(@buf)]
|
|
||||||
when MAXKEY
|
when MAXKEY
|
||||||
key = deserialize_cstr(@buf)
|
key = deserialize_cstr(@buf)
|
||||||
doc[key] = MaxKey.new
|
doc[key] = MaxKey.new
|
||||||
@ -346,6 +347,12 @@ module BSON
|
|||||||
Regexp.new(str, opts)
|
Regexp.new(str, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def deserialize_timestamp_data(buf)
|
||||||
|
increment = buf.get_int
|
||||||
|
seconds = buf.get_int
|
||||||
|
Timestamp.new(seconds, increment)
|
||||||
|
end
|
||||||
|
|
||||||
def encoded_str(str)
|
def encoded_str(str)
|
||||||
if RUBY_VERSION >= '1.9'
|
if RUBY_VERSION >= '1.9'
|
||||||
str.force_encoding("utf-8")
|
str.force_encoding("utf-8")
|
||||||
@ -510,6 +517,14 @@ module BSON
|
|||||||
self.class.serialize_key(buf, key)
|
self.class.serialize_key(buf, key)
|
||||||
end
|
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)
|
def serialize_oid_element(buf, key, val)
|
||||||
buf.put(OID)
|
buf.put(OID)
|
||||||
self.class.serialize_key(buf, key)
|
self.class.serialize_key(buf, key)
|
||||||
@ -598,6 +613,8 @@ module BSON
|
|||||||
MAXKEY
|
MAXKEY
|
||||||
when MinKey
|
when MinKey
|
||||||
MINKEY
|
MINKEY
|
||||||
|
when Timestamp
|
||||||
|
TIMESTAMP
|
||||||
when Numeric
|
when Numeric
|
||||||
raise InvalidDocument, "Cannot serialize the Numeric type #{o.class} as BSON; only Fixum, Bignum, and Float are supported."
|
raise InvalidDocument, "Cannot serialize the Numeric type #{o.class} as BSON; only Fixum, Bignum, and Float are supported."
|
||||||
when Date, DateTime
|
when Date, DateTime
|
||||||
|
76
lib/bson/types/timestamp.rb
Normal file
76
lib/bson/types/timestamp.rb
Normal file
@ -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/)
|
if !(RUBY_PLATFORM =~ /java/)
|
||||||
def test_timestamp
|
def test_timestamp
|
||||||
val = {"test" => [4, 20]}
|
val = {"test" => [4, 20]}
|
||||||
assert_equal val, @encoder.deserialize([0x13, 0x00, 0x00, 0x00,
|
result = @encoder.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,
|
||||||
0x00, 0x00, 0x00])
|
0x00, 0x00, 0x00])
|
||||||
|
|
||||||
|
assert_equal 4, result["test"][0]
|
||||||
|
assert_equal 20, result["test"][1]
|
||||||
end
|
end
|
||||||
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
|
def test_overflow
|
||||||
doc = {"x" => 2**75}
|
doc = {"x" => 2**75}
|
||||||
assert_raise RangeError do
|
assert_raise RangeError do
|
||||||
|
24
test/bson/timestamp_test.rb
Normal file
24
test/bson/timestamp_test.rb
Normal file
@ -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
Block a user