Module: TypedUUID

Defined in:
lib/typed_uuid.rb,
lib/typed_uuid/version.rb

Defined Under Namespace

Modules: ActiveRecord, PsqlColumnMethods, PsqlSchemaDumper Classes: Railtie

Constant Summary collapse

MIGRATIONS_PATH =
File.expand_path('../../db/migrate', __FILE__)
VERSION =
'4.2'

Class Method Summary collapse

Class Method Details

.enum(uuid) ⇒ Object



68
69
70
71
# File 'lib/typed_uuid.rb', line 68

def enum(uuid)
  uuid = uuid.gsub('-', '')
  ((uuid[8..11].to_i(16) ^ uuid[24..27].to_i(16)) ^ uuid[28..31].to_i(16)) >> 3
end

.namebased_uuid(enum, digester:, name:, namespace: "") ⇒ Object



62
63
64
65
66
# File 'lib/typed_uuid.rb', line 62

def namebased_uuid(enum, digester:, name:, namespace: "")
  uuid = digester.digest(name + namespace).unpack("nnnnnnnn")
  uuid[7] = (uuid[2] ^ uuid[6]) ^ ((enum << 3) | 5)
  "%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % uuid
end

.random_uuid(enum) ⇒ Object



29
30
31
32
33
34
# File 'lib/typed_uuid.rb', line 29

def random_uuid(enum)
  uuid = SecureRandom.random_bytes(16).unpack("nnnnnnnn")

  uuid[7] = (uuid[2] ^ uuid[6]) ^ ((enum << 3) | 4)
  "%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % uuid
end

.sequence(uuid) ⇒ Object



88
89
90
# File 'lib/typed_uuid.rb', line 88

def sequence(uuid)
  ("\x00" + sequence_b(uuid)).unpack1('Q>')
end

.sequence_b(uuid) ⇒ Object



83
84
85
86
# File 'lib/typed_uuid.rb', line 83

def sequence_b(uuid)
  uuid = uuid.gsub('-', '')
  uuid[14..30].scan(/.{4}/).map{|i| i.to_i(16) }.pack('n*').b[0..-2]
end

.timestamp(uuid) ⇒ Object



78
79
80
81
# File 'lib/typed_uuid.rb', line 78

def timestamp(uuid)
  uuid = uuid.gsub('-', '')
  Time.at(*uuid[0..13].to_i(16).divmod(1_000_000), :usec)
end

.timestamp_uuid(enum, timestamp: nil, sequence: nil) ⇒ Object



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
# File 'lib/typed_uuid.rb', line 36

def timestamp_uuid(enum, timestamp: nil, sequence: nil)
  timestamp  ||= Time.now

  uuid = [timestamp.to_i * 1_000_000 + timestamp.usec].pack('Q>')[1..-1]
  uuid << if sequence.nil?
    SecureRandom.random_bytes(8)
  elsif sequence.is_a?(Integer)
    sequence = [sequence].pack("Q>")
    if sequence.bytesize == 8 && sequence[0] == "\x00"
      sequence[1..]
    else
      raise ArgumentError, 'Sequence must be less than 8 bytes'
    end
  elsif sequence.is_a?(String)
    raise ArgumentError, 'Sequence must be less than 8 bytes' if sequence.bytesize > 7
    sequence.b
  else
    raise ArgumentError, 'Unable to convert sequence to binary'
  end
  uuid << "\x00\x00"

  uuid = uuid.unpack("nnnnnnnn")
  uuid[7] = ((uuid[2] ^ uuid[6]) ^ ((enum << 3) | 1))
  "%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % uuid
end

.uuid(enum, version = 4, **options) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/typed_uuid.rb', line 12

def uuid(enum, version = 4, **options)
  if enum < 0 || enum > 8_191
    raise ArgumentError, "UUID type must be between 0 and 8,191"
  end

  case version
  when 1
    timestamp_uuid(enum, **options)
  when 3
    namebased_uuid(enum, digester: Digest::MD5, **options)
  when 4
    random_uuid(enum, **options)
  when 5
    namebased_uuid(enum, digester: Digest::SHA1, **options)
  end
end

.version(uuid) ⇒ Object



73
74
75
76
# File 'lib/typed_uuid.rb', line 73

def version(uuid)
  uuid = uuid.gsub('-', '')
  ((uuid[8..11].to_i(16) ^ uuid[24..27].to_i(16)) ^ uuid[28..31].to_i(16)) & 0b0000000000000111
end