diff --git a/examples/demo.rb b/examples/demo.rb
index a777862..3f8fb12 100644
--- a/examples/demo.rb
+++ b/examples/demo.rb
@@ -16,5 +16,5 @@ coll.insert(doc)
doc = {'a' => 3}
coll.insert(doc)
-puts coll.count()
+puts "There are #{coll.count()} records in the test collection. Here they are:"
coll.find().each { |doc| puts doc.inspect }
diff --git a/lib/mongo.rb b/lib/mongo.rb
index a35e535..6b300b4 100644
--- a/lib/mongo.rb
+++ b/lib/mongo.rb
@@ -1,4 +1,5 @@
require 'mongo/mongo'
+require 'mongo/objectid'
require 'mongo/message'
require 'mongo/db'
require 'mongo/cursor'
diff --git a/lib/mongo/objectid.rb b/lib/mongo/objectid.rb
new file mode 100644
index 0000000..bdbf310
--- /dev/null
+++ b/lib/mongo/objectid.rb
@@ -0,0 +1,37 @@
+# Copyright (C) 2008 10gen Inc.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License, version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+require 'mongo/util/uuid'
+
+module XGen
+ module Mongo
+ module Driver
+
+ class ObjectID
+
+ # String UUID
+ attr_reader :uuid
+
+ # uuid is a string
+ def initialize(uuid=nil)
+ @uuid ||= UUID.new.generate
+ end
+
+ def to_s
+ @uuid
+ end
+ end
+ end
+ end
+end
diff --git a/lib/mongo/query.rb b/lib/mongo/query.rb
index 259e762..e95ab23 100644
--- a/lib/mongo/query.rb
+++ b/lib/mongo/query.rb
@@ -32,7 +32,7 @@ module XGen
end
def selector=(sel)
- @selector = case selector
+ @selector = case sel
when nil
{}
when String
diff --git a/lib/mongo/util/bson.rb b/lib/mongo/util/bson.rb
index 84ffbe6..07d2bed 100644
--- a/lib/mongo/util/bson.rb
+++ b/lib/mongo/util/bson.rb
@@ -1,4 +1,5 @@
require 'mongo/util/byte_buffer'
+require 'mongo/objectid'
class BSON
@@ -152,7 +153,7 @@ class BSON
end
def deserialize_oid_data
- ObjectID.new(buf.get(12).pack("C*"))
+ XGen::Mongo::Driver::ObjectID.new(buf.get(12).pack("C*"))
end
def serialize_eoo_element(buf)
@@ -189,7 +190,7 @@ class BSON
def serialize_object_element(buf, key, val)
buf.put(OBJECT)
self.class.serialize_cstr(buf, key)
- BSON.new(buf).serialize(val)
+ buf.put_array(BSON.new.serialize(val).to_a)
end
def serialize_oid_element(buf, key, val)
@@ -241,7 +242,7 @@ class BSON
key == "$where" ? CODE : STRING
when Array
ARRAY
- when ObjectID
+ when XGen::Mongo::Driver::ObjectID
OID
when true, false
Boolean
diff --git a/lib/mongo/util/macaddr.rb b/lib/mongo/util/macaddr.rb
new file mode 100644
index 0000000..11a0fcd
--- /dev/null
+++ b/lib/mongo/util/macaddr.rb
@@ -0,0 +1,71 @@
+# http://codeforpeople.com/lib/ruby/macaddr/macaddr-1.0.0/lib/macaddr.rb
+#
+##
+# Cross platform MAC address determination. Works for:
+# * /sbin/ifconfig
+# * /bin/ifconfig
+# * ifconfig
+# * ipconfig /all
+#
+# To return the first MAC address on the system:
+#
+# Mac.address
+#
+# To return an array of all MAC addresses:
+#
+# Mac.address.list
+
+module Mac
+ VERSION = '1.0.0'
+
+ def Mac.version() ::Mac::VERSION end
+
+ class << self
+
+ ##
+ # Accessor for the system's first MAC address, requires a call to #address
+ # first
+
+ attr_accessor "mac_address"
+
+ ##
+ # Discovers and returns the system's MAC addresses. Returns the first
+ # MAC address, and includes an accessor #list for the remaining addresses:
+ #
+ # Mac.addr # => first address
+ # Mac.addr.list # => all addresses
+
+ def address
+ return @mac_address if defined? @mac_address and @mac_address
+ re = %r/[^:\-](?:[0-9A-F][0-9A-F][:\-]){5}[0-9A-F][0-9A-F][^:\-]/io
+ cmds = '/sbin/ifconfig', '/bin/ifconfig', 'ifconfig', 'ipconfig /all'
+
+ null = test(?e, '/dev/null') ? '/dev/null' : 'NUL'
+
+ lines = nil
+ cmds.each do |cmd|
+ stdout = IO.popen("#{ cmd } 2> #{ null }"){|fd| fd.readlines} rescue next
+ next unless stdout and stdout.size > 0
+ lines = stdout and break
+ end
+ raise "all of #{ cmds.join ' ' } failed" unless lines
+
+ candidates = lines.select{|line| line =~ re}
+ raise 'no mac address candidates' unless candidates.first
+ candidates.map!{|c| c[re].strip}
+
+ maddr = candidates.first
+ raise 'no mac address found' unless maddr
+
+ maddr.strip!
+ maddr.instance_eval{ @list = candidates; def list() @list end }
+
+ @mac_address = maddr
+ end
+
+ ##
+ # Shorter alias for #address
+
+ alias_method "addr", "address"
+ end
+end
diff --git a/lib/mongo/util/uuid.rb b/lib/mongo/util/uuid.rb
new file mode 100644
index 0000000..e170643
--- /dev/null
+++ b/lib/mongo/util/uuid.rb
@@ -0,0 +1,281 @@
+#
+# = uuid.rb - UUID generator
+#
+# Author:: Assaf Arkin assaf@labnotes.org
+# Eric Hodel drbrain@segment7.net
+# Copyright:: Copyright (c) 2005-2008 Assaf Arkin, Eric Hodel
+# License:: MIT and/or Creative Commons Attribution-ShareAlike
+
+require 'fileutils'
+require 'thread'
+require 'tmpdir'
+
+# require 'rubygems'
+require 'mongo/util/macaddr'
+
+
+##
+# = Generating UUIDs
+#
+# Call #generate to generate a new UUID. The method returns a string in one of
+# three formats. The default format is 36 characters long, and contains the 32
+# hexadecimal octets and hyphens separating the various value parts. The
+# :compact format omits the hyphens, while the :urn format
+# adds the :urn:uuid prefix.
+#
+# For example:
+#
+# uuid = UUID.new
+#
+# 10.times do
+# p uuid.generate
+# end
+#
+# = UUIDs in Brief
+#
+# UUID (universally unique identifier) are guaranteed to be unique across time
+# and space.
+#
+# A UUID is 128 bit long, and consists of a 60-bit time value, a 16-bit
+# sequence number and a 48-bit node identifier.
+#
+# The time value is taken from the system clock, and is monotonically
+# incrementing. However, since it is possible to set the system clock
+# backward, a sequence number is added. The sequence number is incremented
+# each time the UUID generator is started. The combination guarantees that
+# identifiers created on the same machine are unique with a high degree of
+# probability.
+#
+# Note that due to the structure of the UUID and the use of sequence number,
+# there is no guarantee that UUID values themselves are monotonically
+# incrementing. The UUID value cannot itself be used to sort based on order
+# of creation.
+#
+# To guarantee that UUIDs are unique across all machines in the network,
+# the IEEE 802 MAC address of the machine's network interface card is used as
+# the node identifier.
+#
+# For more information see {RFC 4122}[http://www.ietf.org/rfc/rfc4122.txt].
+
+class UUID
+
+ VERSION = '2.0.1'
+
+ ##
+ # Clock multiplier. Converts Time (resolution: seconds) to UUID clock
+ # (resolution: 10ns)
+ CLOCK_MULTIPLIER = 10000000
+
+ ##
+ # Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time
+ # ticks.
+ CLOCK_GAPS = 100000
+
+ ##
+ # Version number stamped into the UUID to identify it as time-based.
+ VERSION_CLOCK = 0x0100
+
+ ##
+ # Formats supported by the UUID generator.
+ #
+ # :default:: Produces 36 characters, including hyphens separating
+ # the UUID value parts
+ # :compact:: Produces a 32 digits (hexadecimal) value with no
+ # hyphens
+ # :urn:: Adds the prefix urn:uuid: to the default format
+ FORMATS = {
+ :compact => '%08x%04x%04x%04x%012x',
+ :default => '%08x-%04x-%04x-%04x-%012x',
+ :urn => 'urn:uuid:%08x-%04x-%04x-%04x-%012x',
+ }
+
+ ##
+ # MAC address (48 bits), sequence number and last clock
+ STATE_FILE_FORMAT = 'SLLQ'
+
+ @state_file = nil
+ @mode = nil
+ @uuid = nil
+
+ ##
+ # The access mode of the state file. Set it with state_file.
+
+ def self.mode
+ @mode
+ end
+
+ ##
+ # Generates a new UUID string using +format+. See FORMATS for a list of
+ # supported formats.
+
+ def self.generate(format = :default)
+ @uuid ||= new
+ @uuid.generate format
+ end
+
+ ##
+ # Creates an empty state file in /var/tmp/ruby-uuid or the windows common
+ # application data directory using mode 0644. Call with a different mode
+ # before creating a UUID generator if you want to open access beyond your
+ # user by default.
+ #
+ # If the default state dir is not writable, UUID falls back to ~/.ruby-uuid.
+ #
+ # State files are not portable across machines.
+ def self.state_file(mode = 0644)
+ return @state_file if @state_file
+
+ @mode = mode
+
+ begin
+ require 'Win32API'
+
+ csidl_common_appdata = 0x0023
+ path = 0.chr * 260
+ get_folder_path = Win32API.new('shell32', 'SHGetFolderPath', 'LLLLP', 'L')
+ get_folder_path.call 0, csidl_common_appdata, 0, 1, path
+
+ state_dir = File.join(path.strip)
+ rescue LoadError
+ state_dir = File.join('', 'var', 'tmp')
+ end
+
+ if File.writable?(state_dir) then
+ @state_file = File.join(state_dir, 'ruby-uuid')
+ else
+ @state_file = File.expand_path(File.join('~', '.ruby-uuid'))
+ end
+
+ @state_file
+ end
+
+ ##
+ # Create a new UUID generator. You really only need to do this once.
+ def initialize
+ @drift = 0
+ @last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i
+ @mutex = Mutex.new
+
+ if File.exist?(self.class.state_file) then
+ next_sequence
+ else
+ @mac = Mac.addr.gsub(/:|-/, '').hex & 0x7FFFFFFFFFFF
+ fail "Cannot determine MAC address from any available interface, tried with #{Mac.addr}" if @mac == 0
+ @sequence = rand 0x10000
+
+ open_lock 'w' do |io|
+ write_state io
+ end
+ end
+ end
+
+ ##
+ # Generates a new UUID string using +format+. See FORMATS for a list of
+ # supported formats.
+ def generate(format = :default)
+ template = FORMATS[format]
+
+ raise ArgumentError, "invalid UUID format #{format.inspect}" unless template
+
+ # The clock must be monotonically increasing. The clock resolution is at
+ # best 100 ns (UUID spec), but practically may be lower (on my setup,
+ # around 1ms). If this method is called too fast, we don't have a
+ # monotonically increasing clock, so the solution is to just wait.
+ #
+ # It is possible for the clock to be adjusted backwards, in which case we
+ # would end up blocking for a long time. When backward clock is detected,
+ # we prevent duplicates by asking for a new sequence number and continue
+ # with the new clock.
+
+ clock = @mutex.synchronize do
+ clock = (Time.new.to_f * CLOCK_MULTIPLIER).to_i & 0xFFFFFFFFFFFFFFF0
+
+ if clock > @last_clock then
+ @drift = 0
+ @last_clock = clock
+ elsif clock == @last_clock then
+ drift = @drift += 1
+
+ if drift < 10000 then
+ @last_clock += 1
+ else
+ Thread.pass
+ nil
+ end
+ else
+ next_sequence
+ @last_clock = clock
+ end
+ end until clock
+
+ template % [
+ clock & 0xFFFFFFFF,
+ (clock >> 32) & 0xFFFF,
+ ((clock >> 48) & 0xFFFF | VERSION_CLOCK),
+ @sequence & 0xFFFF,
+ @mac & 0xFFFFFFFFFFFF
+ ]
+ end
+
+ ##
+ # Updates the state file with a new sequence number.
+ def next_sequence
+ open_lock 'r+' do |io|
+ @mac, @sequence, @last_clock = read_state(io)
+
+ io.rewind
+ io.truncate 0
+
+ @sequence += 1
+
+ write_state io
+ end
+ rescue Errno::ENOENT
+ open_lock 'w' do |io|
+ write_state io
+ end
+ ensure
+ @last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i
+ @drift = 0
+ end
+
+ def inspect
+ mac = ("%012x" % @mac).scan(/[0-9a-f]{2}/).join(':')
+ "MAC: #{mac} Sequence: #{@sequence}"
+ end
+
+protected
+
+ ##
+ # Open the state file with an exclusive lock and access mode +mode+.
+ def open_lock(mode)
+ File.open self.class.state_file, mode, self.class.mode do |io|
+ begin
+ io.flock File::LOCK_EX
+ yield io
+ ensure
+ io.flock File::LOCK_UN
+ end
+ end
+ end
+
+ ##
+ # Read the state from +io+
+ def read_state(io)
+ mac1, mac2, seq, last_clock = io.read(32).unpack(STATE_FILE_FORMAT)
+ mac = (mac1 << 32) + mac2
+
+ return mac, seq, last_clock
+ end
+
+
+ ##
+ # Write that state to +io+
+ def write_state(io)
+ mac2 = @mac & 0xffffffff
+ mac1 = (@mac >> 32) & 0xffff
+
+ io.write [mac1, mac2, @sequence, @last_clock].pack(STATE_FILE_FORMAT)
+ end
+
+end