diff --git a/stdlib/random-formatter/0/random-formatter.rbs b/stdlib/random-formatter/0/random-formatter.rbs index 7abccf7db0..232d033975 100644 --- a/stdlib/random-formatter/0/random-formatter.rbs +++ b/stdlib/random-formatter/0/random-formatter.rbs @@ -104,6 +104,7 @@ module RBS # # See RFC 3548 for the definition of URL-safe base64. # + %a{annotate:rdoc:copy:Random::Formatter#urlsafe_base64} def urlsafe_base64: (?Integer? n, ?boolish padding) -> String # + # + %a{annotate:rdoc:copy:Random::Formatter#uuid_v4} + alias uuid_v4 uuid + + # + # Generate a random v7 UUID (Universally Unique IDentifier). + # + # require 'random/formatter' + # + # Random.uuid_v7 # => "0188d4c3-1311-7f96-85c7-242a7aa58f1e" + # Random.uuid_v7 # => "0188d4c3-16fe-744f-86af-38fa04c62bb5" + # Random.uuid_v7 # => "0188d4c3-1af8-764f-b049-c204ce0afa23" + # Random.uuid_v7 # => "0188d4c3-1e74-7085-b14f-ef6415dc6f31" + # # |<--sorted-->| |<----- random ---->| + # + # # or + # prng = Random.new + # prng.uuid_v7 # => "0188ca51-5e72-7950-a11d-def7ff977c98" + # + # The version 7 UUID starts with the least significant 48 bits of a 64 bit Unix + # timestamp (milliseconds since the epoch) and fills the remaining bits with + # random data, excluding the version and variant bits. + # + # This allows version 7 UUIDs to be sorted by creation time. Time ordered UUIDs + # can be used for better database index locality of newly inserted records, + # which may have a significant performance benefit compared to random data + # inserts. + # + # The result contains 74 random bits (9.25 random bytes). + # + # Note that this method cannot be made reproducible because its output includes + # not only random bits but also timestamp. + # + # See [RFC9562](https://www.rfc-editor.org/rfc/rfc9562) for details of UUIDv7. + # + # #### Monotonicity + # + # UUIDv7 has millisecond precision by default, so multiple UUIDs created within + # the same millisecond are not issued in monotonically increasing order. To + # create UUIDs that are time-ordered with sub-millisecond precision, up to 12 + # bits of additional timestamp may added with `extra_timestamp_bits`. The extra + # timestamp precision comes at the expense of random bits. Setting + # `extra_timestamp_bits: 12` provides ~244ns of precision, but only 62 random + # bits (7.75 random bytes). + # + # prng = Random.new + # Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 12) } + # # => + # ["0188d4c7-13da-74f9-8b53-22a786ffdd5a", + # "0188d4c7-13da-753b-83a5-7fb9b2afaeea", + # "0188d4c7-13da-754a-88ea-ac0baeedd8db", + # "0188d4c7-13da-7557-83e1-7cad9cda0d8d"] + # # |<--- sorted --->| |<-- random --->| + # + # Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 8) } + # # => + # ["0188d4c7-3333-7a95-850a-de6edb858f7e", + # "0188d4c7-3333-7ae8-842e-bc3a8b7d0cf9", # <- out of order + # "0188d4c7-3333-7ae2-995a-9f135dc44ead", # <- out of order + # "0188d4c7-3333-7af9-87c3-8f612edac82e"] + # # |<--- sorted -->||<---- random --->| + # + # Any rollbacks of the system clock will break monotonicity. UUIDv7 is based on + # UTC, which excludes leap seconds and can rollback the clock. To avoid this, + # the system clock can synchronize with an NTP server configured to use a "leap + # smear" approach. NTP or PTP will also be needed to synchronize across + # distributed nodes. + # + # Counters and other mechanisms for stronger guarantees of monotonicity are not + # implemented. Applications with stricter requirements should follow [Section + # 6.2](https://www.rfc-editor.org/rfc/rfc9562.html#name-monotonicity-and-counter + # s) of the specification. + # + %a{annotate:rdoc:copy:Random::Formatter#uuid_v7} + def uuid_v7: (?extra_timestamp_bits: Integer) -> String + # + # Internal interface to Random; Generate random data *n* bytes. + # + %a{annotate:rdoc:copy:Random::Formatter#gen_random} + private def gen_random: (Integer n) -> String + + # + # Generate a string that randomly draws from a source array of characters. + # + # The argument *source* specifies the array of characters from which to generate + # the string. The argument *n* specifies the length, in characters, of the + # string to be generated. + # + # The result may contain whatever characters are in the source array. + # + # require 'random/formatter' + # + # prng.choose([*'l'..'r'], 16) #=> "lmrqpoonmmlqlron" + # prng.choose([*'0'..'9'], 5) #=> "27309" + # + %a{annotate:rdoc:copy:Random::Formatter#choose} + private def choose: (Array[String] source, Integer n) -> String end end end diff --git a/test/stdlib/Random_Formatter_test.rb b/test/stdlib/Random_Formatter_test.rb index 41bc39df9e..edc9d09239 100644 --- a/test/stdlib/Random_Formatter_test.rb +++ b/test/stdlib/Random_Formatter_test.rb @@ -42,6 +42,13 @@ def test_uuid Random, :uuid end + def test_uuid_v7 + assert_send_type "() -> ::String", + Random, :uuid_v7 + assert_send_type "(extra_timestamp_bits: ::Integer) -> ::String", + Random, :uuid_v7, extra_timestamp_bits: 12 + end + def test_alphanumeric assert_send_type "() -> ::String", Random, :alphanumeric @@ -93,6 +100,13 @@ def test_uuid Random.new, :uuid end + def test_uuid_v7 + assert_send_type "() -> ::String", + Random.new, :uuid_v7 + assert_send_type "(extra_timestamp_bits: ::Integer) -> ::String", + Random.new, :uuid_v7, extra_timestamp_bits: 12 + end + def test_alphanumeric assert_send_type "() -> ::String", Random.new, :alphanumeric @@ -101,4 +115,14 @@ def test_alphanumeric assert_send_type "(::Integer, chars: Array[::String]) -> ::String", Random.new, :alphanumeric, 10, chars: ["a", "b", "c"] end + + def test_gen_random + assert_send_type "(::Integer) -> ::String", + Random.new, :gen_random, 10 + end + + def test_choose + assert_send_type "(Array[::String], ::Integer) -> ::String", + Random.new, :choose, ["a", "b", "c"], 3 + end end