Change string representation of ObjectID - add 'legacy' methods for interacting with old strings
This commit is contained in:
parent
e05c9fc5da
commit
47c34266cf
|
@ -17,38 +17,17 @@
|
|||
require 'mutex_m'
|
||||
require 'socket'
|
||||
require 'digest/md5'
|
||||
require 'mongo/util/byte_buffer'
|
||||
|
||||
module Mongo
|
||||
|
||||
# Implementation of the Babble OID. Object ids are not required by
|
||||
# Mongo, but they make certain operations more efficient.
|
||||
#
|
||||
# The driver does not automatically assign ids to records that are
|
||||
# inserted. (An upcoming feature will allow you to give an id "factory"
|
||||
# to a database and/or a collection.)
|
||||
#
|
||||
# 12 bytes
|
||||
# ---
|
||||
# 0 time
|
||||
# 1
|
||||
# 2
|
||||
# 3
|
||||
# 4 machine
|
||||
# 5
|
||||
# 6
|
||||
# 7 pid
|
||||
# 8
|
||||
# 9 inc
|
||||
# 10
|
||||
# 11
|
||||
# Representation of an ObjectId for Mongo.
|
||||
class ObjectID
|
||||
# The string representation of an OID is different than its internal
|
||||
# and BSON byte representations. The BYTE_ORDER here maps
|
||||
# internal/BSON byte position (the index in BYTE_ORDER) to the
|
||||
# position of the two hex characters representing that byte in the
|
||||
# string representation. For example, the 0th BSON byte corresponds to
|
||||
# the (0-based) 7th pair of hex chars in the string.
|
||||
# This is the legacy byte ordering for Babble. Versions of the Ruby
|
||||
# driver prior to 0.14 used this byte ordering when converting ObjectID
|
||||
# instances to and from strings. If you have string representations of
|
||||
# ObjectIDs using the legacy byte ordering make sure to use the
|
||||
# to_s_legacy and from_string_legacy methods, or convert your strings
|
||||
# with ObjectID#legacy_string_convert
|
||||
BYTE_ORDER = [7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 9, 8]
|
||||
|
||||
LOCK = Object.new
|
||||
|
@ -56,17 +35,6 @@ module Mongo
|
|||
|
||||
@@index = 0
|
||||
|
||||
# Given a string representation of an ObjectID, return a new ObjectID
|
||||
# with that value.
|
||||
def self.from_string(str)
|
||||
raise "illegal ObjectID format" unless legal?(str)
|
||||
data = []
|
||||
BYTE_ORDER.each_with_index { |string_position, data_index|
|
||||
data[data_index] = str[string_position * 2, 2].to_i(16)
|
||||
}
|
||||
self.new(data)
|
||||
end
|
||||
|
||||
def self.legal?(str)
|
||||
len = BYTE_ORDER.length * 2
|
||||
str =~ /([0-9a-f]+)/i
|
||||
|
@ -88,7 +56,42 @@ module Mongo
|
|||
@data.dup
|
||||
end
|
||||
|
||||
# Given a string representation of an ObjectID, return a new ObjectID
|
||||
# with that value.
|
||||
def self.from_string(str)
|
||||
raise "illegal ObjectID format" unless legal?(str)
|
||||
data = []
|
||||
12.times do |i|
|
||||
data[i] = str[i * 2, 2].to_i(16)
|
||||
end
|
||||
self.new(data)
|
||||
end
|
||||
|
||||
# Create a new ObjectID given a string representation of an ObjectID
|
||||
# using the legacy byte ordering. This method may eventually be
|
||||
# removed. If you are not sure that you need this method you should be
|
||||
# using the regular from_string.
|
||||
def self.from_string_legacy(str)
|
||||
raise "illegal ObjectID format" unless legal?(str)
|
||||
data = []
|
||||
BYTE_ORDER.each_with_index { |string_position, data_index|
|
||||
data[data_index] = str[string_position * 2, 2].to_i(16)
|
||||
}
|
||||
self.new(data)
|
||||
end
|
||||
|
||||
def to_s
|
||||
str = ' ' * 24
|
||||
12.times do |i|
|
||||
str[i * 2, 2] = '%02x' % @data[i]
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
# Get a string representation of this ObjectID using the legacy byte
|
||||
# ordering. This method may eventually be removed. If you are not sure
|
||||
# that you need this method you should be using the regular to_s.
|
||||
def to_s_legacy
|
||||
str = ' ' * 24
|
||||
BYTE_ORDER.each_with_index { |string_position, data_index|
|
||||
str[string_position * 2, 2] = '%02x' % @data[data_index]
|
||||
|
@ -96,33 +99,37 @@ module Mongo
|
|||
str
|
||||
end
|
||||
|
||||
# Convert a string representation of an ObjectID using the legacy byte
|
||||
# ordering to the proper byte ordering. This method may eventually be
|
||||
# removed. If you are not sure that you need this method it is probably
|
||||
# unnecessary.
|
||||
def self.legacy_string_convert(str)
|
||||
legacy = ' ' * 24
|
||||
BYTE_ORDER.each_with_index do |legacy_pos, pos|
|
||||
legacy[legacy_pos * 2, 2] = str[pos * 2, 2]
|
||||
end
|
||||
legacy
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate
|
||||
oid = ''
|
||||
|
||||
# 4 bytes current time
|
||||
time = Time.new.to_i
|
||||
buf = ByteBuffer.new
|
||||
buf.put_int(time & 0xFFFFFFFF)
|
||||
oid += [time].pack("N")
|
||||
|
||||
# 3 bytes machine
|
||||
machine_hash = Digest::MD5.digest(Socket.gethostname)
|
||||
buf.put(machine_hash[0])
|
||||
buf.put(machine_hash[1])
|
||||
buf.put(machine_hash[2])
|
||||
oid += Digest::MD5.digest(Socket.gethostname)[0, 3]
|
||||
|
||||
# 2 bytes pid
|
||||
pid = Process.pid % 0xFFFF
|
||||
buf.put(pid & 0xFF)
|
||||
buf.put((pid >> 8) & 0xFF)
|
||||
oid += [Process.pid % 0xFFFF].pack("n")
|
||||
|
||||
# 3 bytes inc
|
||||
inc = get_inc
|
||||
buf.put(inc & 0xFF)
|
||||
buf.put((inc >> 8) & 0xFF)
|
||||
buf.put((inc >> 16) & 0xFF)
|
||||
oid += [get_inc].pack("N")[1, 3]
|
||||
|
||||
buf.rewind
|
||||
buf.to_a.dup
|
||||
oid.unpack("C12")
|
||||
end
|
||||
|
||||
def get_inc
|
||||
|
|
|
@ -29,6 +29,15 @@ class ObjectIDTest < Test::Unit::TestCase
|
|||
assert_equal 24, $1.length
|
||||
end
|
||||
|
||||
def test_to_s_legacy
|
||||
s = @o.to_s_legacy
|
||||
assert_equal 24, s.length
|
||||
s =~ /^([0-9a-f]+)$/
|
||||
assert_equal 24, $1.length
|
||||
|
||||
assert_not_equal s, @o.to_s
|
||||
end
|
||||
|
||||
def test_save_and_restore
|
||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
|
||||
|
@ -51,6 +60,14 @@ class ObjectIDTest < Test::Unit::TestCase
|
|||
assert_equal @o.to_s, o2.to_s
|
||||
end
|
||||
|
||||
def test_from_string_legacy
|
||||
hex_str = @o.to_s_legacy
|
||||
o2 = ObjectID.from_string_legacy(hex_str)
|
||||
assert_equal hex_str, o2.to_s_legacy
|
||||
assert_equal @o, o2
|
||||
assert_equal @o.to_s, o2.to_s
|
||||
end
|
||||
|
||||
def test_legal
|
||||
assert !ObjectID.legal?(nil)
|
||||
assert !ObjectID.legal?("fred")
|
||||
|
@ -62,7 +79,7 @@ class ObjectIDTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_from_string_leading_zeroes
|
||||
hex_str = '000000000000000000abcdef'
|
||||
hex_str = '000000000000000000000000'
|
||||
o = ObjectID.from_string(hex_str)
|
||||
assert_equal hex_str, o.to_s
|
||||
end
|
||||
|
@ -70,7 +87,19 @@ class ObjectIDTest < Test::Unit::TestCase
|
|||
def test_byte_order
|
||||
hex_str = '000102030405060708090A0B'
|
||||
o = ObjectID.from_string(hex_str)
|
||||
assert_equal [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b], o.to_a
|
||||
end
|
||||
|
||||
def test_legacy_byte_order
|
||||
hex_str = '000102030405060708090A0B'
|
||||
o = ObjectID.from_string_legacy(hex_str)
|
||||
assert_equal [0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0b, 0x0a, 0x09, 0x08], o.to_a
|
||||
end
|
||||
|
||||
def test_legacy_string_convert
|
||||
l = @o.to_s_legacy
|
||||
s = @o.to_s
|
||||
assert_equal s, ObjectID.legacy_string_convert(l)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue