class MagicLink < ApplicationRecord CODE_LENGTH = 6 EXPIRATION_TIME = 15.minutes belongs_to :identity scope :active, -> { where(expires_at: Time.current...) } scope :stale, -> { where(expires_at: ..Time.current) } before_validation :generate_code, on: :create before_validation :set_expiration, on: :create validates :code, uniqueness: true, presence: true class << self def consume(code) active.find_by(code: Code.sanitize(code))&.consume end def cleanup stale.delete_all end end def consume destroy identity end private def generate_code self.code ||= loop do candidate = Code.generate(CODE_LENGTH) break candidate unless self.class.exists?(code: candidate) end end def set_expiration self.expires_at ||= EXPIRATION_TIME.from_now end end