diff --git a/lib/uuid.rb b/lib/uuid.rb
index 8659ae0..347d8ef 100644
--- a/lib/uuid.rb
+++ b/lib/uuid.rb
@@ -6,390 +6,279 @@
# Copyright:: Copyright (c) 2005,2007 Assaf Arkin
# License:: MIT and/or Creative Commons Attribution-ShareAlike
-
+require 'fileutils'
require 'thread'
-require 'yaml'
-require 'singleton'
-require 'logger'
-
+require 'tmpdir'
-# == Generating UUIDs
+##
+# = Generating UUIDs
#
-# Call UUID.new to generate and return 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.
+# 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:
-# 10.times do
-# p UUID.new
-# end
#
-# ---
-# == UUIDs in Brief
+# 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.
+# 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.
+# 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
+# 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.
+# 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, use the IEEE 802
-# MAC address of the machine's network interface card as the node identifier. Network interface
-# cards have unique MAC address that are 47-bit long (the last bit acts as a broadcast flag).
-# Use +ipconfig+ (Windows), or +ifconfig+ (Unix) to find the MAC address (aka physical address)
-# of a network card. It takes the form of six pairs of hexadecimal digits, separated by hypen or
-# colon, e.g. '08-0E-46-21-4B-35'
+# 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].
-#
-# == Configuring the UUID generator
-#
-# The UUID generator requires a state file which maintains the MAC address and next sequence
-# number to use. By default, the UUID generator will use the file uuid.state contained
-# in the current directory.
-#
-# Use UUID.config to specify a different location for the UUID state file. If the UUID state file
-# does not exist, you can create one manually, or use UUID.config with the options :sequence
-# and :mac_addr.
-#
-# A UUID state file looks like:
-# ---
-# last_clock: "0x28227f76122d80"
-# mac_addr: 08-0E-46-21-4B-35
-# sequence: "0x1639"
-#
-#--
-# === Time-based UUID
-#
-# The UUID specification prescribes the following format for representing UUIDs. Four octets encode
-# the low field of the time stamp, two octects encode the middle field of the timestamp, and two
-# octets encode the high field of the timestamp with the version number. Two octets encode the
-# clock sequence number and six octets encode the unique node identifier.
-#
-# The timestamp is a 60 bit value holding UTC time as a count of 100 nanosecond intervals since
-# October 15, 1582. UUIDs generated in this manner are guaranteed not to roll over until 3400 AD.
-#
-# The clock sequence is used to help avoid duplicates that could arise when the clock is set backward
-# in time or if the node ID changes. Although the system clock is guaranteed to be monotonic, the
-# system clock is not guaranteed to be monotonic across system failures. The UUID cannot be sure
-# that no UUIDs were generated with timestamps larger than the current timestamp.
-#
-# If the clock sequence can be determined at initialization, it is incremented by one. The clock sequence
-# MUST be originally (i.e. once in the lifetime of a system) initialized to a random number to minimize the
-# correlation across systems. The initial value must not be correlated to the node identifier.
-#
-# The node identifier must be unique for each UUID generator. This is accomplished using the IEEE 802
-# network card address. For systems with multiple IEEE 802 addresses, any available address can be used.
-# For systems with no IEEE address, a 47 bit random value is used and the multicast bit is set so it will
-# never conflict with addresses obtained from network cards.
-#
-# === UUID state file
-#
-# The UUID state is contained in the UUID state file. The file name can be specified when configuring
-# the UUID generator with UUID.config. The default is to use the file +uuid.state+ in the current directory.
-#
-# The UUID state file is read once when the UUID generator is first used (or configured). The sequence
-# number contained in the UUID is read and used, and the state file is updated to the next sequence
-# number. The MAC address is also read from the state file. The current clock time (in 100ns resolution)
-# is stored in the state file whenever the sequence number is updated, but is never read.
-#
-# If the UUID generator detects that the system clock has been moved backwards, it will obtain a new
-# sequence in the same manner. So it is possible that the UUID state file will be updated while the
-# application is running.
-#++
-module UUID
-
- VERSION = '1.0.4'
- # Regular expression to identify a 36 character UUID. Can be used for a partial match.
- REGEXP = /[[:xdigit:]]{8}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{12}/
+class UUID
- # Regular expression to identify a 36 character UUID. Can only be used for a full match.
- REGEXP_FULL = /^[[:xdigit:]]{8}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{4}[:-][[:xdigit:]]{12}$/
+ VERSION = '2.0.0'
- # Regular expression to identify a 32 character UUID, no hyphens (compact) full match.
- REGEXP_COMPACT = /^[[:xdigit:]]{32}$/
+ ##
+ # Clock multiplier. Converts Time (resolution: seconds) to UUID clock
+ # (resolution: 10ns)
- # Default state file.
- STATE_FILE = 'uuid.state'
+ CLOCK_MULTIPLIER = 10000000
- # Clock multiplier. Converts Time (resolution: seconds) to UUID clock (resolution: 10ns)
- CLOCK_MULTIPLIER = 10000000 #:nodoc:
+ ##
+ # Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time
+ # ticks.
- # Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time ticks.
- CLOCK_GAPS = 100000 #:nodoc:
+ CLOCK_GAPS = 100000
+ ##
# Version number stamped into the UUID to identify it as time-based.
- VERSION_CLOCK = 0x0100 #:nodoc:
+ 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' } #:nodoc:
+ :compact => '%08x%04x%04x%04x%012x',
+ :default => '%08x-%04x-%04x-%04x-%012x',
+ :urn => 'urn:uuid:%08x-%04x-%04x-%04x-%012x',
+ }
- # Length (in characters) of UUIDs generated for each of the formats.
- FORMATS_LENGTHS = { :compact=>32, :default=>36, :urn=>45 } #:nodoc:
+ ##
+ # MAC address (48 bits), sequence number and last clock
- ERROR_INVALID_SEQUENCE = "Invalid sequence number: found '%s', expected 4 hexdecimal digits" #:nodoc:
+ STATE_FILE_FORMAT = 'SLLQ'
- ERROR_NOT_A_SEQUENCE = "Not a sequence number: expected integer between 0 and 0xFFFF" #:nodoc:
+ @state_file = nil
+ @mode = nil
- ERROR_INVALID_MAC_ADDR = "Invalid MAC address: found '%s', expected a number in the format XX-XX-XX-XX-XX-XX" #:nodoc:
+ ##
+ # The access mode of the state file. Set it with state_file.
- INFO_INITIALIZED = "Initialized UUID generator with sequence number 0x%04x and MAC address %s" #:nodoc:
+ def self.mode
+ @mode
+ end
- ERROR_INITIALIZED_RANDOM_1 = "Initialized UUID generator with random sequence number/MAC address." #:nodoc:
+ ##
+ # 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.
- ERROR_INITIALIZED_RANDOM_2 = "UUIDs are not guaranteed to be unique. Please create a uuid.state file as soon as possible." #:nodoc:
+ def self.state_file(mode = 0644)
+ return @state_file if @state_file
- IFCONFIG_PATTERN = /[^:\-](?:[0-9A-Za-z][0-9A-Za-z][:\-]){5}[0-9A-Za-z][0-9A-Za-z][^:\-]/ #:nodoc:
+ @mode = mode
- class << self
+ begin
+ require 'Win32API'
- # Configures the UUID generator. Use this method to specify the UUID state file, logger, etc.
- #
- # The method accepts the following options:
- # * :state_file -- Specifies the location of the state file. If missing, the default
- # is uuid.state
- # * :logger -- The UUID generator will use this logger to report the state information (optional).
- # * :sequence -- Specifies the sequence number (0 to 0xFFFF) to use. Required to
- # create a new state file, ignored if the state file already exists.
- # * :mac_addr -- Specifies the MAC address (xx-xx-xx-xx-xx) to use. Required to
- # create a new state file, ignored if the state file already exists.
- #
- # For example, to create a new state file:
- # UUID.config :state_file=>'my-uuid.state', :sequence=>rand(0x10000), :mac_addr=>'0C-0E-35-41-60-65'
- # To use an existing state file and log to +STDOUT+:
- # UUID.config :state_file=>'my-uuid.state', :logger=>Logger.new(STDOUT)
- #
- # :call-seq:
- # UUID.config(config)
- #
- def config(options)
- options ||= {}
- @@mutex.synchronize do
- @@logger = options[:logger]
- next_sequence options
- end
+ 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
- # Create a uuid.state file by finding the IEEE 802 NIC MAC address for this machine.
- # Works for UNIX (ifconfig) and Windows (ipconfig). Creates the uuid.state file in the
- # current directory.
- def setup()
- file = File.expand_path(File.join(Dir.pwd, STATE_FILE))
- if File.exist? file
- puts "UUID: Found an existing UUID state file: #{file}"
- else
- puts "UUID: No UUID state file found, attempting to create one for you:"
- # Run ifconfig for UNIX, or ipconfig for Windows.
- config = ""
- Kernel.open("|ifconfig") { |input| input.each_line { |line| config << line } } rescue nil
- Kernel.open("|ipconfig /all") { |input| input.each_line { |line| config << line } } rescue nil
-
- addresses = config.scan(IFCONFIG_PATTERN).map { |addr| addr[1..-2] }
- if addresses.empty?
- puts "Could not find any IEEE 802 NIC MAC addresses for this machine."
- puts "You need to create the uuid.state file manually."
- else
- puts "Found the following IEEE 802 NIC MAC addresses on your computer:"
- addresses.each { |addr| puts " #{addr}" }
- puts "Selecting the first address #{addresses[0]} for use in your UUID state file."
- File.open file, "w" do |output|
- output.puts "mac_addr: \"#{addresses[0]}\""
- output.puts format("sequence: \"0x%04x\"", rand(0x10000))
- end
- puts "Created a new UUID state file: #{file}"
- end
- end
- file
+ 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
- private
+ @state_file
+ end
- def next_sequence(config = nil)
- # If called to advance the sequence number (config is nil), we have a state file that we're able to use.
- # If called from configuration, use the specified or default state file.
- state_file = (config && config[:state_file]) || @@state_file
+ ##
+ # Create a new UUID generator. You really only need to do this once.
- unless state_file
- if File.exist?(STATE_FILE)
- state_file = STATE_FILE
- else
- file = File.expand_path(File.join(Dir.pwd, STATE_FILE))
- state_file = File.exist?(file) ? file : setup
- end
- end
- begin
- File.open state_file, "r+" do |file|
- # Lock the file for exclusive access, just to make sure it's not being read while we're
- # updating its contents.
- file.flock(File::LOCK_EX)
- state = YAML::load file
- # Get the sequence number. Must be a valid 16-bit hexadecimal value.
- sequence = state['sequence']
- if sequence
- raise RuntimeError, format(ERROR_INVALID_SEQUENCE, sequence) unless sequence.is_a?(String) && sequence =~ /[0-9a-fA-F]{4}/
- sequence = sequence.hex & 0xFFFF
- else
- sequence = rand(0x10000)
- end
- # Get the MAC address. Must be 6 pairs of hexadecimal octets. Convert MAC address into
- # a 48-bit value with the higher bit being zero.
- mac_addr = state['mac_addr']
- raise RuntimeError, format(ERROR_INVALID_MAC_ADDR, mac_addr) unless
- mac_addr.is_a?(String) && mac_addr =~ /([0-9a-fA-F]{2}[:\-]){5}[0-9a-fA-F]{2}/
- mac_hex = mac_addr.scan(/[0-9a-fA-F]{2}/).join.hex & 0x7FFFFFFFFFFF
-
- # If everything is OK, proceed to the next step. Grab the sequence number and store
- # the new state. Start at beginning of file, and truncate file when done.
- @@mac_addr, @@mac_hex, @@sequence, @@state_file = mac_addr, mac_hex, sequence, state_file
- file.pos = 0
- dump file, true
- file.truncate file.pos
- end
- # Initialized.
- if @@logger
- @@logger.info format(INFO_INITIALIZED, @@sequence, @@mac_addr)
- else
- warn "UUID: " + format(INFO_INITIALIZED, @@sequence, @@mac_addr)
- end
- @@last_clock, @@drift = (Time.new.to_f * CLOCK_MULTIPLIER).to_i, 0
- rescue Errno::ENOENT=>error
- if !config
- # Generate random values.
- @@mac_hex, @@sequence, @@state_file = rand(0x800000000000) | 0xF00000000000, rand(0x10000), nil
- # Initialized.
- if @@logger
- @@logger.error ERROR_INITIALIZED_RANDOM_1
- @@logger.error ERROR_INITIALIZED_RANDOM_2
- else
- warn "UUID: " + ERROR_INITIALIZED_RANDOM_1
- warn "UUID: " + ERROR_INITIALIZED_RANDOM_2
- end
- @@last_clock, @@drift = (Time.new.to_f * CLOCK_MULTIPLIER).to_i, 0
- else
- # No state file. If we were called for configuration with valid sequence number and MAC address,
- # attempt to create state file. See code above for how we interpret these values.
- sequence = config[:sequence]
- raise RuntimeError, format(ERROR_NOT_A_SEQUENCE, sequence) unless sequence.is_a?(Integer)
- sequence &= 0xFFFF
- mac_addr = config[:mac_addr]
- raise RuntimeError, format(ERROR_INVALID_MAC_ADDR, mac_addr) unless
- mac_addr.is_a?(String) && mac_addr =~ /([0-9a-fA-F]{2}[:\-]){5}[0-9a-fA-F]{2}/
- mac_hex = mac_addr.scan(/[0-9a-fA-F]{2}/).join.hex & 0x7FFFFFFFFFFF
- File.open state_file, "w" do |file|
- file.flock(File::LOCK_EX)
- @@mac_addr, @@mac_hex, @@sequence, @@state_file = mac_addr, mac_hex, sequence, state_file
- file.pos = 0
- dump file, true
- file.truncate file.pos
- end
- # Initialized.
- if @@logger
- @@logger.info format(INFO_INITIALIZED, @@sequence, @@mac_addr)
- else
- warn "UUID: " + format(INFO_INITIALIZED, @@sequence, @@mac_addr)
- end
- @@last_clock, @@drift = (Time.new.to_f * CLOCK_MULTIPLIER).to_i, 0
- end
- rescue Exception=>error
- @@last_clock = nil
- raise error
+ 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
+ File.open self.class.state_file, 'r', @mode do |io|
+ @mac, @sequence, = read_state io
end
- end
+ else
+ config = ''
- def dump(file, plus_one)
- # Gets around YAML weirdness, like ont storing the MAC address as a string.
- if @@sequence && @@mac_addr
- file.puts "mac_addr: \"#{@@mac_addr}\""
- file.puts "sequence: \"0x%04x\"" % ((plus_one ? @@sequence + 1 : @@sequence) & 0xFFFF)
- file.puts "last_clock: \"0x%x\"" % (@@last_clock || (Time.new.to_f * CLOCK_MULTIPLIER).to_i)
+ Dir.chdir Dir.tmpdir do
+ config << `ifconfig 2> /dev/null`
+ config << `ipconfig /all 2>NUL` unless $?.success?
end
+
+ addresses =
+ config.scan(/[^:\-](?:[\da-z][\da-z][:\-]){5}[\da-z][\da-z][^:\-]/i)
+
+ raise Error, 'MAC address not found via ifconfig or ipconfig' if
+ addresses.empty?
+
+ @mac = addresses.first.scan(/[0-9a-fA-F]{2}/).join.hex & 0x7FFFFFFFFFFF
+ @sequence = rand 0x10000
+
+ open_lock 'w' do |io| write_state io end
end
end
- @@mutex = Mutex.new
- @@last_clock = nil
- @@logger = nil
- @@state_file = nil
+ ##
+ # Generates a new UUID string using +format+. See FORMATS for a list of
+ # supported formats.
- # Generates and returns a new UUID string.
- #
- # The argument +format+ specifies which formatting template to use:
- # * :default -- Produces 36 characters, including hyphens separating the UUID value parts
- # * :compact -- Produces a 32 digits (hexadecimal) value with no hyphens
- # * :urn -- Aadds the prefix urn:uuid: to the :default format
- #
- # For example:
- # print UUID.new :default
- # or just
- # print UUID.new
- #
- # :call-seq:
- # UUID.new([format]) -> string
- #
- def new(format = nil)
- # Determine which format we're using for the UUID string.
- template = FORMATS[format || :default] or raise RuntimeError, "I don't know the format '#{format}'"
-
- # 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
- # Initialize UUID generator if not already initialized. Uninitizlied UUID generator has no
- # last known clock.
- next_sequence unless @@last_clock
+ def generate(format = :default)
+ template = FORMATS[format]
+
+ raise ArgumentError, "invalid UUID format #{format.inspect}" if
+ template.nil?
+
+ # 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
- @@drift = 0
- @@last_clock = clock
- elsif clock = @@last_clock
- drift = @@drift += 1
- if drift < 10000
- @@last_clock += 1
+
+ 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
+ @last_clock = clock
end
- end while not clock
- sprintf template, clock & 0xFFFFFFFF, (clock >> 32)& 0xFFFF, ((clock >> 48) & 0xFFFF | VERSION_CLOCK),
- @@sequence & 0xFFFF, @@mac_hex & 0xFFFFFFFFFFFF
+ end until clock
+
+ template % [
+ clock & 0xFFFFFFFF,
+ (clock >> 32) & 0xFFFF,
+ ((clock >> 48) & 0xFFFF | VERSION_CLOCK),
+ @sequence & 0xFFFF,
+ @mac & 0xFFFFFFFFFFFF
+ ]
end
- alias uuid new
- module_function :uuid, :new
+ ##
+ # Updates the state file with a new sequence number.
-end
+ def next_sequence(config = nil)
+ open_lock 'r+' do |io|
+ @mac, @sequence, @last_clock = read_state io
+ io.rewind
+ io.truncate 0
-class ActiveRecord::Base
- class << self
- def uuid_primary_key()
- before_create { |record| record.id = UUID.new unless record.id }
+ @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
-end if defined?(ActiveRecord)
+ ##
+ # 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
-if __FILE__ == $0
- UUID.setup
end
+
diff --git a/test/test-uuid.rb b/test/test-uuid.rb
index 4fb3786..2363248 100644
--- a/test/test-uuid.rb
+++ b/test/test-uuid.rb
@@ -1,50 +1,37 @@
-#
-# = test-uuid.rb - UUID generator test cases
-#
# Author:: Assaf Arkin assaf@labnotes.org
-# Documentation:: http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/UuidGenerator
# Copyright:: Copyright (c) 2005,2007 Assaf Arkin
# License:: MIT and/or Creative Commons Attribution-ShareAlike
-#
-#--
-#++
-
require 'test/unit'
require 'uuid'
class TestUUID < Test::Unit::TestCase
- def setup
- end
+ def test_generate
+ uuid = UUID.new
+ assert_match(/\A[\da-f]{32}\z/i, uuid.generate(:compact))
- def teardown
- end
+ assert_match(/\A[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\z/i,
+ uuid.generate(:default))
- def test_format
- 10.times do
- uuid = UUID.new :compact
- assert uuid =~ /^[0-9a-fA-F]{32}$/, "UUID does not conform to :compact format"
- assert uuid =~ UUID::REGEXP_COMPACT, "UUID does not conform to :compact format"
- uuid = UUID.new :default
- assert uuid =~ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/, "UUID does not conform to :default format"
- assert uuid =~ UUID::REGEXP, "UUID does not conform to :compact format"
- assert uuid =~ UUID::REGEXP_FULL, "UUID does not conform to :compact format"
- uuid = UUID.new :urn
- assert uuid =~ /^urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i, "UUID does not conform to :urn format"
- assert uuid =~ UUID::REGEXP, "UUID does not conform to :compact format"
+ assert_match(/^urn:uuid:[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\z/i,
+ uuid.generate(:urn))
+
+ e = assert_raise ArgumentError do
+ uuid.generate :unknown
end
+
+ assert_equal 'invalid UUID format :unknown', e.message
end
def test_monotonic
- count = 100000
seen = {}
- count.times do |i|
- uuid = UUID.new
+ uuid_gen = UUID.new
+
+ 20_000.times do
+ uuid = uuid_gen.generate
assert !seen.has_key?(uuid), "UUID repeated"
seen[uuid] = true
- print '.' if (i % 10000) == 0
- STDOUT.flush
end
end