Skip to content

Instantly share code, notes, and snippets.

@joncode
Last active January 16, 2026 00:47
Show Gist options
  • Select an option

  • Save joncode/a39791fdcdd7c19a695e3e6e7af16d7e to your computer and use it in GitHub Desktop.

Select an option

Save joncode/a39791fdcdd7c19a695e3e6e7af16d7e to your computer and use it in GitHub Desktop.
HexIdMethods: secure, prefix-based IDs for ActiveRecord models for WEB2
# WEB2 Secure Random ID generator
# app/models/concerns/hex_id_methods.rb
# include HexIdMethods in any model that uses :hex_id
module HexIdMethods
extend ActiveSupport::Concern
# Create a safe character set: a-k, m-z, 0, 2-9 (no l, 1) - 34 characters
SAFE_CHARS = (('a'..'k').to_a + ('m'..'z').to_a + ['0'] + ('2'..'9').to_a).freeze
MAX_RETRIES = 3
included do
before_validation :generate_hex_id, on: :create
validates :hex_id, presence: true, uniqueness: true
around_create :retry_on_hex_id_collision
end
private
def generate_hex_id
return if hex_id.present?
self.hex_id = build_hex_id
end
def build_hex_id
"#{hex_id_prefix}_#{secure_random_safe_string}"
end
def hex_id_prefix
self.class.const_get(:HEX_ID_PREFIX)
rescue NameError
raise "Missing HEX_ID_PREFIX in #{self.class.name}"
end
def retry_on_hex_id_collision
retries = 0
begin
yield
rescue ActiveRecord::RecordNotUnique
raise if (retries += 1) > MAX_RETRIES
self.hex_id = nil
generate_hex_id
retry
end
end
def secure_random_safe_string(length=10)
chars = []
n = SAFE_CHARS.length
max = 256 - (256 % n)
while chars.length < length
SecureRandom.random_bytes(1).each_byte do |b|
next if b >= max
chars << SAFE_CHARS[b % n]
break if chars.length == length
end
end
chars.join
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment