Generate CUIDs in Ruby

Resources  |  Generate CUIDs in Ruby

Generating a CUID (Collision-resistant Unique Identifier) in Ruby involves using techniques such as the current timestamp, a counter, a machine fingerprint, and randomness. Below is an example implementation of generating a CUID in Ruby.

Steps to Generate a CUID in Ruby

  1. Timestamp: Get the current timestamp in milliseconds.
  2. Counter: Use a counter to handle multiple CUIDs generated in a short period.
  3. Fingerprint: Generate a machine-specific fingerprint.
  4. Randomness: Add random characters to further reduce the risk of collisions.
  5. Base36 Encoding: Encode the components using Base36 for readability.

Example Code

Here's a Ruby implementation to generate a CUID:

require 'digest/md5'

class CUIDGenerator
  BASE36_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz'.chars.freeze
  @@counter = 0
  @@last_timestamp = 0

  def self.generate_cuid
    timestamp = (Time.now.to_f * 1000).to_i

    if timestamp == @@last_timestamp
      @@counter += 1
    else
      @@last_timestamp = timestamp
      @@counter = 0
    end

    timestamp_base36 = encode_base36(timestamp)
    counter_base36 = encode_base36(@@counter)
    fingerprint = get_machine_fingerprint
    random_string = get_random_string(4)

    "c#{timestamp_base36}#{counter_base36}#{fingerprint}#{random_string}"
  end

  private

  def self.encode_base36(value)
    return '0' if value == 0

    result = ''
    while value > 0
      result = BASE36_CHARS[value % 36] + result
      value /= 36
    end
    result
  end

  def self.get_machine_fingerprint
    begin
      hostname = `hostname`.strip
      hash_value = Digest::MD5.hexdigest(hostname)
      hash_value[0..3]
    rescue
      '0000'
    end
  end

  def self.get_random_string(length)
    Array.new(length) { BASE36_CHARS.sample }.join
  end
end

# Example usage
puts CUIDGenerator.generate_cuid

Explanation

  1. Base36 Encoding:

    • The encode_base36 method converts a number to a Base36 encoded string.
  2. Timestamp:

    • timestamp = (Time.now.to_f * 1000).to_i gets the current timestamp in milliseconds.
  3. Counter:

    • A class-level counter (@@counter) is used to handle multiple CUIDs generated within the same millisecond. It is incremented and reset as necessary.
  4. Machine Fingerprint:

    • get_machine_fingerprint fetches the machine's hostname, computes an MD5 hash, and returns the first 4 characters.
  5. Random String:

    • get_random_string generates a random string of specified length using Base36 characters.
  6. Combine Components:

    • The generate_cuid method combines the timestamp, counter, fingerprint, and random parts to form the final CUID.

Summary

This Ruby implementation of CUID generation uses standard libraries (Digest::MD5 for hashing, Time for timestamps, and basic string operations) to handle timestamps, hashing, randomness, and counters. This approach ensures that the generated CUIDs are unique, readable, and collision-resistant, suitable for various applications. Adjustments can be made based on specific requirements, such as error handling when fetching the machine's hostname or modifying the length of the random string portion.