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
117 def self.generate(format = :default)
118   @uuid ||= new
119   @uuid.generate format
120 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
129 def self.generator
130   @uuid ||= new
131 end
mode() click to toggle source

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

    # File lib/uuid.rb
105 def self.mode
106   @mode
107 end
mode=(mode) click to toggle source
    # File lib/uuid.rb
109 def self.mode=(mode)
110   @mode = mode
111 end
new() click to toggle source

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

    # File lib/uuid.rb
247 def initialize
248   @drift = 0
249   @last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i
250   @mutex = Mutex.new
251 
252   state_file = self.class.state_file
253   if state_file && File.size?(state_file) then
254     next_sequence
255   else
256     @mac = mac_address
257     fail "Cannot determine MAC address from any available interface, tried with #{mac_address}" if @mac == 0
258     @sequence = rand 0x10000
259 
260     # Ensure the mode is respected, even with a restrictive umask
261     File.open(state_file, 'w') { |f| f.chmod(self.class.mode) } if state_file && !File.exist?(state_file)
262 
263     if state_file
264       open_lock 'wb' do |io|
265         write_state io
266       end
267     end
268   end
269 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
136 def self.server=(address)
137   @uuid = Client.new(address) unless Client === @uuid
138 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
149 def self.state_file(mode = 0644)
150   return @state_file unless @state_file.nil?
151 
152   @mode = mode
153 
154   begin
155     require 'Win32API'
156 
157     csidl_common_appdata = 0x0023
158     path = 0.chr * 260
159     get_folder_path = Win32API.new('shell32', 'SHGetFolderPath', 'LLLLP', 'L')
160     get_folder_path.call 0, csidl_common_appdata, 0, 1, path
161 
162     state_dir = File.join(path.strip)
163   rescue LoadError
164     state_dir = Dir.tmpdir
165   end
166 
167   @state_file = File.join(state_dir, 'ruby-uuid')
168 
169   if !File.writable?(state_dir) || (File.exist?(@state_file) && !File.writable?(@state_file)) then
170     @state_file = File.expand_path('.ruby-uuid', '~')
171   end
172 
173   @state_file
174 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
182 def self.state_file=(path)
183   @state_file = path
184   @mode ||= 0644
185 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
190 def self.validate(uuid)
191   return true if uuid =~ /\A[\da-f]{32}\z/i
192   return true if
193     uuid =~ /\A(urn:uuid:)?[\da-f]{8}-([\da-f]{4}-){3}[\da-f]{12}\z/i
194 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
274 def generate(format = :default)
275   template = FORMATS[format]
276 
277   raise ArgumentError, "invalid UUID format #{format.inspect}" unless template
278 
279   # The clock must be monotonically increasing. The clock resolution is at
280   # best 100 ns (UUID spec), but practically may be lower (on my setup,
281   # around 1ms). If this method is called too fast, we don't have a
282   # monotonically increasing clock, so the solution is to just wait.
283   #
284   # It is possible for the clock to be adjusted backwards, in which case we
285   # would end up blocking for a long time. When backward clock is detected,
286   # we prevent duplicates by asking for a new sequence number and continue
287   # with the new clock.
288 
289   clock = @mutex.synchronize do
290     clock = (Time.new.to_f * CLOCK_MULTIPLIER).to_i & 0xFFFFFFFFFFFFFFF0
291 
292     if clock > @last_clock then
293       @drift = 0
294       @last_clock = clock
295     elsif clock == @last_clock then
296       drift = @drift += 1
297 
298       if drift < 10000 then
299         @last_clock += 1
300       else
301         Thread.pass
302         nil
303       end
304     else
305       next_sequence
306       @last_clock = clock
307     end
308   end until clock
309 
310   template % [
311       clock        & 0xFFFFFFFF,
312      (clock >> 32) & 0xFFFF,
313     ((clock >> 48) & 0xFFFF | VERSION_CLOCK),
314     @sequence      & 0xFFFF,
315     @mac           & 0xFFFFFFFFFFFF
316   ]
317 end
iee_mac_address() click to toggle source

Uses system calls to get a mac address

    # File lib/uuid.rb
229 def iee_mac_address
230   begin
231     Mac.addr.gsub(/:|-/, '').hex & 0x7FFFFFFFFFFF
232   rescue
233     0
234   end
235 end
inspect() click to toggle source
    # File lib/uuid.rb
345 def inspect
346   mac = ("%012x" % @mac).scan(/[0-9a-f]{2}/).join(':')
347   "MAC: #{mac}  Sequence: #{@sequence}"
348 end
mac_address() click to toggle source

return iee_mac_address if available, pseudo_mac_address otherwise

    # File lib/uuid.rb
240 def mac_address
241   return iee_mac_address unless iee_mac_address == 0
242   return pseudo_mac_address
243 end
next_sequence() click to toggle source

Updates the state file with a new sequence number.

    # File lib/uuid.rb
321 def next_sequence
322   if self.class.state_file
323     open_lock 'rb+' do |io|
324       @mac, @sequence, @last_clock = read_state(io)
325 
326       io.rewind
327       io.truncate 0
328 
329       @sequence += 1
330 
331       write_state io
332     end
333   else
334     @sequence += 1
335   end
336 rescue Errno::ENOENT
337   open_lock 'w' do |io|
338     write_state io
339   end
340 ensure
341   @last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i
342   @drift = 0
343 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
206 def pseudo_mac_address
207   sha1 = ::Digest::SHA1.new
208   256.times do
209     r = [rand(0x100000000)].pack "N"
210     sha1.update r
211   end
212   str = sha1.digest
213   r = rand 14 # 20-6
214   node = str[r, 6] || str
215   if RUBY_VERSION >= "1.9.0"
216     nnode = node.bytes.to_a
217     nnode[0] |= 0x01
218     node = ''
219     nnode.each { |s| node << s.chr }
220   else
221     node[0] |= 0x01 # multicast bit
222   end
223   node.bytes.collect{|b|b.to_s(16)}.join.hex & 0x7FFFFFFFFFFF
224 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
354 def open_lock(mode)
355   File.open self.class.state_file, mode, self.class.mode do |io|
356     begin
357       io.flock File::LOCK_EX
358       yield io
359     ensure
360       io.flock File::LOCK_UN
361     end
362   end
363 end
read_state(io) click to toggle source

Read the state from io

    # File lib/uuid.rb
367 def read_state(io)
368   mac1, mac2, seq, last_clock = io.read(32).unpack(STATE_FILE_FORMAT)
369   mac = (mac1 << 32) + mac2
370 
371   return mac, seq, last_clock
372 end
write_state(io) click to toggle source

Write that state to io

    # File lib/uuid.rb
377 def write_state(io)
378   mac2 =  @mac        & 0xffffffff
379   mac1 = (@mac >> 32) & 0xffff
380 
381   io.write [mac1, mac2, @sequence, @last_clock].pack(STATE_FILE_FORMAT)
382 end