Module: ConcernsOnRails::Models::Tokenizable
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/concerns_on_rails/models/tokenizable.rb
Overview
Generates and manages security tokens (API keys, invite codes, share links).
class User < ApplicationRecord
include ConcernsOnRails::Tokenizable
tokenizable_by :api_token # 32-char URL-safe
tokenizable_by :reset_password_token, length: 24
tokenizable_by :invite_code, type: :alphanumeric, length: 8
end
user = User.create! # tokens auto-generated on create
user.regenerate_api_token! # new value, persisted
user.revoke_api_token! # sets the column to nil
user.api_token? # true if present
User.find_by_api_token(token) # Rails default
User.authenticate_by_api_token(token) # constant-time compare; returns record or nil
Unlike Hashable, one model can declare multiple token fields, generation is
URL-safe by default, and assign_tokenizable_value retries on uniqueness
collisions before insert (best-effort; pair with a unique DB index).
For stateless / self-expiring tokens (password resets, email confirmations)
on Rails 7.1+, consider the framework-native generates_token_for instead.
Constant Summary collapse
- VALID_TYPES =
%i[urlsafe hex alphanumeric numeric].freeze
- ALPHANUMERIC_ALPHABET =
(("A".."Z").to_a + ("a".."z").to_a + ("0".."9").to_a).freeze
- NUMERIC_ALPHABET =
("0".."9").to_a.freeze
- MAX_GENERATION_ATTEMPTS =
10
Instance Method Summary collapse
-
#assign_tokenizable_value(field) ⇒ Object
Assigns the generated value only when blank, so callers can pass an explicit one.
-
#tokenizable_unique_value(field) ⇒ Object
Generate → in-Ruby exists? precheck → retry, up to MAX_GENERATION_ATTEMPTS times — useful for short codes; a unique DB index is still the real guarantee.
Instance Method Details
#assign_tokenizable_value(field) ⇒ Object
Assigns the generated value only when blank, so callers can pass an explicit one.
138 139 140 141 142 |
# File 'lib/concerns_on_rails/models/tokenizable.rb', line 138 def assign_tokenizable_value(field) return if self[field].present? self[field] = tokenizable_unique_value(field) end |
#tokenizable_unique_value(field) ⇒ Object
Generate → in-Ruby exists? precheck → retry, up to MAX_GENERATION_ATTEMPTS
times — useful for short codes; a unique DB index is still the real
guarantee. Shared by create-time assignment and regenerate_
147 148 149 150 151 152 153 154 155 |
# File 'lib/concerns_on_rails/models/tokenizable.rb', line 147 def tokenizable_unique_value(field) MAX_GENERATION_ATTEMPTS.times do candidate = self.class.generate_tokenizable_value(field) return candidate unless self.class.unscoped.exists?(field => candidate) end raise "ConcernsOnRails::Models::Tokenizable: could not generate a unique value for '#{field}' " \ "after #{MAX_GENERATION_ATTEMPTS} attempts — consider a longer length or a larger alphabet" end |