Class: Friendly::UUID

Inherits:
Object
  • Object
show all
Defined in:
lib/friendly/uuid.rb

Overview

UUID format version 1, as specified in RFC 4122, with jitter in place of the mac address and sequence counter.

Defined Under Namespace

Classes: InvalidVersion, TypeError

Constant Summary collapse

GREGORIAN_EPOCH_OFFSET =

Oct 15, 1582

0x01B2_1DD2_1381_4000
VARIANT =
0b1000_0000_0000_0000

Instance Method Summary collapse

Constructor Details

#initialize(bytes = nil) ⇒ UUID

Returns a new instance of UUID.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/friendly/uuid.rb', line 19

def initialize(bytes = nil)
  case bytes
  when self.class # UUID
    @bytes = bytes.to_s
  when String
    case bytes.size
    when 16 # Raw byte array
      @bytes = bytes
    when 36 # Human-readable UUID representation; inverse of #to_guid
      elements = bytes.split("-")
      raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (malformed UUID representation)" if elements.size != 5
      @bytes = Array(elements.join).pack('H32')
    else
      unescaped = PGconn.unescape_bytea(bytes)
      if unescaped.size == 16
        @bytes = unescaped
      else
        raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (invalid bytecount)"
      end
    end

  when Integer
    raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (integer out of range)" if bytes < 0 or bytes > 2**128
    @bytes = [
      (bytes >> 96) & 0xFFFF_FFFF,
      (bytes >> 64) & 0xFFFF_FFFF,
      (bytes >> 32) & 0xFFFF_FFFF,
      bytes & 0xFFFF_FFFF
    ].pack("NNNN")

  when NilClass, Time
    time = (bytes || Time).stamp * 10 + GREGORIAN_EPOCH_OFFSET
    # See http://github.com/spectra/ruby-uuid/
    @bytes = [
      time & 0xFFFF_FFFF,
      time >> 32,
      ((time >> 48) & 0x0FFF) | 0x1000,
      # Top 3 bytes reserved
      rand(2**13) | VARIANT,
      rand(2**16),
      rand(2**32)
    ].pack("NnnnnN")

  else
    raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (unknown source class)"
  end
end

Instance Method Details

#<=>(other) ⇒ Object



104
105
106
# File 'lib/friendly/uuid.rb', line 104

def <=>(other)
  total_usecs <=> other.send(:total_usecs)
end

#==(other) ⇒ Object



130
131
132
# File 'lib/friendly/uuid.rb', line 130

def ==(other)
  self.to_s == other.to_s
end

#eql?(other) ⇒ Boolean

Returns:



126
127
128
# File 'lib/friendly/uuid.rb', line 126

def eql?(other)
  other.is_a?(Comparable) and @bytes == other.to_s
end

#hashObject



122
123
124
# File 'lib/friendly/uuid.rb', line 122

def hash
  @bytes.hash
end

#inspect(long = false) ⇒ Object



108
109
110
111
112
113
114
115
116
# File 'lib/friendly/uuid.rb', line 108

def inspect(long = false)
  "<Friendly::UUID##{object_id} time: #{
    Time.at(seconds).inspect
  }, usecs: #{
    usecs
  } jitter: #{
    @bytes.unpack('QQ')[1]
  }" + (long ? ", version: #{version}, variant: #{variant}, guid: #{to_guid}>" :  ">")
end

#secondsObject



96
97
98
# File 'lib/friendly/uuid.rb', line 96

def seconds
  total_usecs / 1_000_000
end

#sql_literal(dataset) ⇒ Object



138
139
140
# File 'lib/friendly/uuid.rb', line 138

def sql_literal(dataset)
  dataset.literal(to_s.to_sequel_blob)
end

#to_guidObject



85
86
87
88
89
90
# File 'lib/friendly/uuid.rb', line 85

def to_guid
  elements = @bytes.unpack("NnnCCa6")
  node = elements[-1].unpack('C*')
  elements[-1] = '%02x%02x%02x%02x%02x%02x' % node
  "%08x-%04x-%04x-%02x%02x-%s" % elements
end

#to_iObject



67
68
69
70
71
72
73
# File 'lib/friendly/uuid.rb', line 67

def to_i
  ints = @bytes.unpack("NNNN")
  (ints[0] << 96) +
  (ints[1] << 64) +
  (ints[2] << 32) +
  ints[3]
end

#to_json(*args) ⇒ Object



92
93
94
# File 'lib/friendly/uuid.rb', line 92

def to_json(*args)
  to_guid.to_json(*args)
end

#to_sObject



134
135
136
# File 'lib/friendly/uuid.rb', line 134

def to_s
  @bytes
end

#usecsObject



100
101
102
# File 'lib/friendly/uuid.rb', line 100

def usecs
  total_usecs % 1_000_000
end

#variantObject



81
82
83
# File 'lib/friendly/uuid.rb', line 81

def variant
  @bytes.unpack('QnnN')[1] >> 13
end

#versionObject



75
76
77
78
79
# File 'lib/friendly/uuid.rb', line 75

def version
  time_high = @bytes.unpack("NnnQ")[2]
  version = (time_high & 0xF000).to_s(16)[0].chr.to_i
  version > 0 and version < 6 ? version : -1
end