Skip to content
This repository has been archived by the owner on Jan 15, 2024. It is now read-only.

Commit

Permalink
Add unique support to object ids.
Browse files Browse the repository at this point in the history
[ fix #41 ]
  • Loading branch information
durran committed Feb 3, 2013
1 parent 9a67bf6 commit 3faf1b7
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 13 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -12,6 +12,11 @@
* \#137 `IOError` exceptions during connection go through reconnect
process properly. (Peter Kieltyka)

* \#41 `Moped::BSON::ObjectId.from_time` now accepts a `unique` option to
ensure the generated id is unique.

Moped::BSON::ObjectId.from_time(time, unique: true)

* mongoid/mongoid\#2738 Ensure that delete operations don't include
special selectors, like $query.

Expand Down
62 changes: 55 additions & 7 deletions lib/moped/bson/object_id.rb
Expand Up @@ -7,19 +7,68 @@ class ObjectId
include Comparable

class << self

# Create a new object id from a string.
#
# @example Create an object id from the string.
# Moped::BSON::ObjectId.from_string(id)
#
# @param [ String ] string The string to create from.
#
# @return [ ObjectId ] The new object id.
#
# @since 1.0.0
def from_string(string)
raise Errors::InvalidObjectId.new(string) unless legal?(string)
from_data [string].pack("H*")
end

def from_time(time)
from_data [time.to_i].pack("Nx8")
# Create a new object id from a time.
#
# @example Create an object id from a time.
# Moped::BSON::ObjectId.from_id(time)
#
# @example Create an object id from a time, ensuring uniqueness.
# Moped::BSON::ObjectId.from_id(time, unique: true)
#
# @param [ Time ] time The time to generate from.
# @param [ Hash ] options The options.
#
# @option options [ true, false ] :unique Whether the id should be
# unique.
#
# @return [ ObjectId ] The new object id.
#
# @since 1.0.0
def from_time(time, options = nil)
unique = (options || {})[:unique]
from_data(unique ? @@generator.next(time.to_i) : [ time.to_i ].pack("Nx8"))
end

def legal?(str)
/\A\h{24}\Z/ === str.to_s
# Determine if the string is a legal object id.
#
# @example Is the string a legal object id?
# Moped::BSON::ObjectId.legal?(string)
#
# @param [ String ] The string to test.
#
# @return [ true, false ] If the string is legal.
#
# @since 1.0.0
def legal?(string)
/\A\h{24}\Z/ === string.to_s
end

# Create a new object id from some raw data.
#
# @example Create an object id from raw data.
# Moped::BSON::ObjectId.from_data(data)
#
# @param [ String ] data The raw bytes.
#
# @return [ ObjectId ] The new object id.
#
# @since 1.0.0
def from_data(data)
id = allocate
id.send(:data=, data)
Expand All @@ -36,7 +85,6 @@ def data
# If @data is defined, then we know we've been loaded in some
# non-standard way, so we attempt to repair the data.
repair! @data if defined? @data

@raw_data ||= @@generator.next
end

Expand Down Expand Up @@ -98,15 +146,15 @@ def initialize

# Return object id data based on the current time, incrementing the
# object id counter.
def next
def next(time = nil)
@mutex.lock
begin
counter = @counter = (@counter + 1) % 0xFFFFFF
ensure
@mutex.unlock rescue nil
end

generate(Time.new.to_i, counter)
generate(time || Time.new.to_i, counter)
end

# Generate object id data for a given time using the provided +counter+.
Expand Down
38 changes: 32 additions & 6 deletions spec/moped/bson/object_id_spec.rb
Expand Up @@ -193,14 +193,40 @@
end

describe ".from_time" do
it "sets the generation time" do
time = Time.at((Time.now.utc - 64800).to_i).utc
described_class.from_time(time).generation_time.should == time

context "when no unique option is provided" do

let(:time) do
Time.at((Time.now.utc - 64800).to_i).utc
end

it "sets the generation time" do
described_class.from_time(time).generation_time.should eq(time)
end

it "does not include process or sequence information" do
id = described_class.from_time(Time.now)
id.to_s.should =~ /\A\h{8}0{16}\Z/
end
end

it "does not include process or sequence information" do
id = described_class.from_time(Time.now)
id.to_s.should =~ /\A\h{8}0{16}\Z/
context "when a unique option is provided" do

let(:time) do
Time.at((Time.now.utc - 64800).to_i).utc
end

let(:object_id) do
described_class.from_time(time, unique: true)
end

let(:non_unique) do
described_class.from_time(time, unique: true)
end

it "creates a new unique object id" do
object_id.should_not eq(non_unique)
end
end
end

Expand Down

0 comments on commit 3faf1b7

Please sign in to comment.