class UUID

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.

Constants

CLOCK_GAPS

Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time ticks.

CLOCK_MULTIPLIER

Clock multiplier. Converts Time (resolution: seconds) to UUID clock (resolution: 10ns)

FORMATS

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

SOCKET_NAME

You don’t have to use this, it’s just a good default.

STATE_FILE_FORMAT

MAC address (48 bits), sequence number and last clock

VERSION
VERSION_CLOCK

Version number stamped into the UUID to identify it as time-based.

Public Instance Methods

generate(format = :default) click to toggle source

Generates a new UUID string using format. See FORMATS for a list of supported formats.

# File lib/uuid.rb, line 282
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
iee_mac_address() click to toggle source

Uses system calls to get a mac address

# File lib/uuid.rb, line 237
def iee_mac_address
  begin
    Mac.addr.gsub(/:|-/, '').hex & 0x7FFFFFFFFFFF
  rescue
    0
  end
end
inspect() click to toggle source
# File lib/uuid.rb, line 353
def inspect
  mac = ("%012x" % @mac).scan(/[0-9a-f]{2}/).join(':')
  "MAC: #{mac}  Sequence: #{@sequence}"
end
mac_address() click to toggle source

return #iee_mac_address if available, #pseudo_mac_address otherwise

# File lib/uuid.rb, line 248
def mac_address
  return iee_mac_address unless iee_mac_address == 0
  return pseudo_mac_address
end
next_sequence() click to toggle source

Updates the state file with a new sequence number.

# File lib/uuid.rb, line 329
def next_sequence
  if self.class.state_file
    open_lock 'rb+' do |io|
      @mac, @sequence, @last_clock = read_state(io)

      io.rewind
      io.truncate 0

      @sequence += 1

      write_state io
    end
  else
    @sequence += 1
  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
pseudo_mac_address() click to toggle source

Generate a pseudo MAC address because we have no pure-ruby way to know the MAC address of the NIC this system uses. Note that cheating with pseudo arresses here is completely legal: see Section 4.5 of RFC4122 for details.

This implementation is shamelessly stolen from

https://github.com/spectra/ruby-uuid/blob/master/uuid.rb

Thanks spectra.

# File lib/uuid.rb, line 214
def pseudo_mac_address
  sha1 = ::Digest::SHA1.new
  256.times do
    r = [rand(0x100000000)].pack "N"
    sha1.update r
  end
  str = sha1.digest
  r = rand 14 # 20-6
  node = str[r, 6] || str
  if RUBY_VERSION >= "1.9.0"
    nnode = node.bytes.to_a
    nnode[0] |= 0x01
    node = ''
    nnode.each { |s| node << s.chr }
  else
    node[0] |= 0x01 # multicast bit
  end
  node.bytes.collect{|b|b.to_s(16)}.join.hex & 0x7FFFFFFFFFFF
end

Protected Instance Methods

open_lock(mode) { |io| ... } click to toggle source

Open the state file with an exclusive lock and access mode mode.

# File lib/uuid.rb, line 362
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_state(io) click to toggle source

Read the state from io

# File lib/uuid.rb, line 375
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_state(io) click to toggle source

Write that state to io

# File lib/uuid.rb, line 385
def write_state(io)
  mac2 =  @mac        & 0xffffffff
  mac1 = (@mac >> 32) & 0xffff

  io.write [mac1, mac2, @sequence, @last_clock].pack(STATE_FILE_FORMAT)
end

Public Class Methods

generate(format = :default) click to toggle source

Generates a new UUID string using format. See FORMATS for a list of supported formats.

# File lib/uuid.rb, line 125
def self.generate(format = :default)
  @uuid ||= new
  @uuid.generate format
end
generator() click to toggle source

Returns the UUID generator used by generate. Useful if you need to mess with it, e.g. force next sequence when forking (e.g. Unicorn, Resque):

after_fork do

UUID.generator.next_sequence

end

# File lib/uuid.rb, line 137
def self.generator
  @uuid ||= new
end
mode() click to toggle source

The access mode of the state file. Set it with state_file.

# File lib/uuid.rb, line 113
def self.mode
  @mode
end
mode=(mode) click to toggle source
# File lib/uuid.rb, line 117
def self.mode=(mode)
  @mode = mode
end
new() click to toggle source

Create a new UUID generator. You really only need to do this once.

# File lib/uuid.rb, line 255
def initialize
  @drift = 0
  @last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i
  @mutex = Mutex.new

  state_file = self.class.state_file
  if state_file && File.size?(state_file) then
    next_sequence
  else
    @mac = mac_address
    fail "Cannot determine MAC address from any available interface, tried with #{mac_address}" if @mac == 0
    @sequence = rand 0x10000

    # Ensure the mode is respected, even with a restrictive umask
    File.open(state_file, 'w') { |f| f.chmod(self.class.mode) } if state_file && !File.exist?(state_file)

    if state_file
      open_lock 'wb' do |io|
        write_state io
      end
    end
  end
end
server=(address) click to toggle source

Call this to use a UUID Server. Expects address to bind to (SOCKET_NAME is a good default)

# File lib/uuid.rb, line 144
def self.server=(address)
  @uuid = Client.new(address) unless Client === @uuid
end
state_file(mode = 0644) click to toggle source

Creates an empty state file in #Dir.tmpdir/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.

# File lib/uuid.rb, line 157
def self.state_file(mode = 0644)
  return @state_file unless @state_file.nil?

  @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 = Dir.tmpdir
  end

  @state_file = File.join(state_dir, 'ruby-uuid')

  if !File.writable?(state_dir) || (File.exist?(@state_file) && !File.writable?(@state_file)) then
    @state_file = File.expand_path('.ruby-uuid', '~')
  end

  @state_file
end
state_file=(path) click to toggle source

Specify the path of the state file. Use this if you need a different location for your state file.

Set to false if your system cannot use a state file (e.g. many shared hosts).

# File lib/uuid.rb, line 190
def self.state_file=(path)
  @state_file = path
  @mode ||= 0644
end
validate(uuid) click to toggle source

Returns true if uuid is in compact, default or urn formats. Does not validate the layout (RFC 4122 section 4) of the UUID.

# File lib/uuid.rb, line 198
def self.validate(uuid)
  return true if uuid =~ /\A[\da-f]{32}\z/
  return true if
    uuid =~ /\A(urn:uuid:)?[\da-f]{8}-([\da-f]{4}-){3}[\da-f]{12}\z/
end