diff --git a/README.md b/README.md index 4926143..1d5fbf6 100644 --- a/README.md +++ b/README.md @@ -53,13 +53,20 @@ SecureRandom.alphanumeric(10) #=> "S8baxMJnPl" SecureRandom.alphanumeric(10) #=> "aOxAg8BAJe" ``` -Generate UUIDs: +Generate UUIDs v4 (random): ```ruby SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594" SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab" ``` +Generate UUIDs v7 (unix timestamp + random): + +```ruby +SecureRandom.uuid_v7 #=> "01843a55-736e-785c-9f2e-0f74f11d6145" +SecureRandom.uuid_v7 #=> "01843a55-7370-799e-8498-669a5b1fbc19" +``` + ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. diff --git a/lib/random/formatter.rb b/lib/random/formatter.rb index 744853a..736a49b 100644 --- a/lib/random/formatter.rb +++ b/lib/random/formatter.rb @@ -148,6 +148,28 @@ def uuid "%08x-%04x-%04x-%04x-%04x%08x" % ary end + # Random::Formatter#uuid generates a random v7 UUID (Universally Unique IDentifier). + # + # require 'random/formatter' + # + # prng.uuid_v7 #=> "01843a54-f268-7f51-934c-216e9ca4fe05" + # prng.uuid_v7 #=> "01843a54-f26b-7edf-862b-d986fb0f421b" + # prng.uuid_v7 #=> "01843a54-f26f-7664-b455-85fa8a5e6a4d" + # + # The version 7 UUID is random, but contains a time-based component for ordering. + # + # The result contains 74 random bits (9 random bytes). + # + # See RFC 4122 for details of UUID. + # + def uuid_v7 + ts = [Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)].pack('Q>').unpack('nNn').drop(1) + ary = random_bytes(10).unpack("nnnN") + ary[0] = (ary[0] & 0x0fff) | 0x7000 + ary[1] = (ary[1] & 0x3fff) | 0x8000 + "%08x-%04x-%04x-%04x-%04x%08x" % (ts + ary) + end + private def gen_random(n) self.bytes(n) end diff --git a/test/ruby/test_random_formatter.rb b/test/ruby/test_random_formatter.rb index a507209..fe07ba3 100644 --- a/test/ruby/test_random_formatter.rb +++ b/test/ruby/test_random_formatter.rb @@ -75,6 +75,17 @@ def test_uuid assert_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/, uuid) end + def test_uuid_v7 + uuid = @it.uuid_v7 + assert_equal(36, uuid.size) + + # Check time_hi_and_version and clock_seq_hi_res bits (RFC 4122 4.4) + assert_equal('7', uuid[14]) + assert_include(%w'8 9 a b', uuid[19]) + + assert_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/, uuid) + end + def test_alphanumeric 65.times do |n| an = @it.alphanumeric(n)