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 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
126 def self.generate(format = :default)
127   @uuid ||= new
128   @uuid.generate format
129 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
138 def self.generator
139   @uuid ||= new
140 end
mode() click to toggle source

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

    # File lib/uuid.rb
114 def self.mode
115   @mode
116 end
mode=(mode) click to toggle source
    # File lib/uuid.rb
118 def self.mode=(mode)
119   @mode = mode
120 end
new() click to toggle source

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

    # File lib/uuid.rb
256 def initialize
257   @drift = 0
258   @last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i
259   @mutex = Mutex.new
260 
261   state_file = self.class.state_file
262   if state_file && File.size?(state_file) then
263     next_sequence
264   else
265     @mac = mac_address
266     fail "Cannot determine MAC address from any available interface, tried with #{mac_address}" if @mac == 0
267     @sequence = rand 0x10000
268 
269     # Ensure the mode is respected, even with a restrictive umask
270     File.open(state_file, 'w') { |f| f.chmod(self.class.mode) } if state_file && !File.exist?(state_file)
271 
272     if state_file
273       open_lock 'wb' do |io|
274         write_state io
275       end
276     end
277   end
278 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
145 def self.server=(address)
146   @uuid = Client.new(address) unless Client === @uuid
147 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
158 def self.state_file(mode = 0644)
159   return @state_file unless @state_file.nil?
160 
161   @mode = mode
162 
163   begin
164     require 'Win32API'
165 
166     csidl_common_appdata = 0x0023
167     path = 0.chr * 260
168     get_folder_path = Win32API.new('shell32', 'SHGetFolderPath', 'LLLLP', 'L')
169     get_folder_path.call 0, csidl_common_appdata, 0, 1, path
170 
171     state_dir = File.join(path.strip)
172   rescue LoadError
173     state_dir = Dir.tmpdir
174   end
175 
176   @state_file = File.join(state_dir, 'ruby-uuid')
177 
178   if !File.writable?(state_dir) || (File.exist?(@state_file) && !File.writable?(@state_file)) then
179     @state_file = File.expand_path('.ruby-uuid', '~')
180   end
181 
182   @state_file
183 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
191 def self.state_file=(path)
192   @state_file = path
193   @mode ||= 0644
194 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
199 def self.validate(uuid)
200   return true if uuid =~ /\A[\da-f]{32}\z/i
201   return true if
202     uuid =~ /\A(urn:uuid:)?[\da-f]{8}-([\da-f]{4}-){3}[\da-f]{12}\z/i
203 end

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
283 def generate(format = :default)
284   template = FORMATS[format]
285 
286   raise ArgumentError, "invalid UUID format #{format.inspect}" unless template
287 
288   # The clock must be monotonically increasing. The clock resolution is at
289   # best 100 ns (UUID spec), but practically may be lower (on my setup,
290   # around 1ms). If this method is called too fast, we don't have a
291   # monotonically increasing clock, so the solution is to just wait.
292   #
293   # It is possible for the clock to be adjusted backwards, in which case we
294   # would end up blocking for a long time. When backward clock is detected,
295   # we prevent duplicates by asking for a new sequence number and continue
296   # with the new clock.
297 
298   clock = @mutex.synchronize do
299     clock = (Time.new.to_f * CLOCK_MULTIPLIER).to_i & 0xFFFFFFFFFFFFFFF0
300 
301     if clock > @last_clock then
302       @drift = 0
303       @last_clock = clock
304     elsif clock == @last_clock then
305       drift = @drift += 1
306 
307       if drift < 10000 then
308         @last_clock += 1
309       else
310         Thread.pass
311         nil
312       end
313     else
314       next_sequence
315       @last_clock = clock
316     end
317   end until clock
318 
319   template % [
320       clock        & 0xFFFFFFFF,
321      (clock >> 32) & 0xFFFF,
322     ((clock >> 48) & 0xFFFF | VERSION_CLOCK),
323     @sequence      & 0xFFFF,
324     @mac           & 0xFFFFFFFFFFFF
325   ]
326 end
iee_mac_address() click to toggle source

Uses system calls to get a mac address

    # File lib/uuid.rb
238 def iee_mac_address
239   begin
240     Mac.addr.gsub(/:|-/, '').hex & 0x7FFFFFFFFFFF
241   rescue
242     0
243   end
244 end
inspect() click to toggle source
    # File lib/uuid.rb
354 def inspect
355   mac = ("%012x" % @mac).scan(/[0-9a-f]{2}/).join(':')
356   "MAC: #{mac}  Sequence: #{@sequence}"
357 end
mac_address() click to toggle source

return iee_mac_address if available, pseudo_mac_address otherwise

    # File lib/uuid.rb
249 def mac_address
250   return iee_mac_address unless iee_mac_address == 0
251   return pseudo_mac_address
252 end
next_sequence() click to toggle source

Updates the state file with a new sequence number.

    # File lib/uuid.rb
330 def next_sequence
331   if self.class.state_file
332     open_lock 'rb+' do |io|
333       @mac, @sequence, @last_clock = read_state(io)
334 
335       io.rewind
336       io.truncate 0
337 
338       @sequence += 1
339 
340       write_state io
341     end
342   else
343     @sequence += 1
344   end
345 rescue Errno::ENOENT
346   open_lock 'w' do |io|
347     write_state io
348   end
349 ensure
350   @last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i
351   @drift = 0
352 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
215 def pseudo_mac_address
216   sha1 = ::Digest::SHA1.new
217   256.times do
218     r = [rand(0x100000000)].pack "N"
219     sha1.update r
220   end
221   str = sha1.digest
222   r = rand 14 # 20-6
223   node = str[r, 6] || str
224   if RUBY_VERSION >= "1.9.0"
225     nnode = node.bytes.to_a
226     nnode[0] |= 0x01
227     node = ''
228     nnode.each { |s| node << s.chr }
229   else
230     node[0] |= 0x01 # multicast bit
231   end
232   node.bytes.collect{|b|b.to_s(16)}.join.hex & 0x7FFFFFFFFFFF
233 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
363 def open_lock(mode)
364   File.open self.class.state_file, mode, self.class.mode do |io|
365     begin
366       io.flock File::LOCK_EX
367       yield io
368     ensure
369       io.flock File::LOCK_UN
370     end
371   end
372 end
read_state(io) click to toggle source

Read the state from io

    # File lib/uuid.rb
376 def read_state(io)
377   mac1, mac2, seq, last_clock = io.read(32).unpack(STATE_FILE_FORMAT)
378   mac = (mac1 << 32) + mac2
379 
380   return mac, seq, last_clock
381 end
write_state(io) click to toggle source

Write that state to io

    # File lib/uuid.rb
386 def write_state(io)
387   mac2 =  @mac        & 0xffffffff
388   mac1 = (@mac >> 32) & 0xffff
389 
390   io.write [mac1, mac2, @sequence, @last_clock].pack(STATE_FILE_FORMAT)
391 end