tokenize_attr
Declarative secure token generation for ActiveRecord model attributes.
tokenize_attr adds a tokenize class macro to ActiveRecord models. When no
prefix is needed it transparently delegates to Rails' built-in
has_secure_token (Rails 5+). When a prefix is required — or when
has_secure_token is unavailable — it installs a before_create callback
backed by SecureRandom.base58 with configurable size, prefix, and retry
logic.
Installation
Add to your Gemfile:
gem "tokenize_attr"
Then run the install task to wire the gem into your Rails app:
rails tokenize_attr:install
This creates config/initializers/tokenize_attr.rb, which calls
ActiveSupport.on_load(:active_record) so that every model gains the
tokenize macro automatically.
To remove the initializer:
rails tokenize_attr:uninstall
Usage
Basic — delegates to has_secure_token
When no prefix is provided and the model supports has_secure_token (every
Rails 5+ ApplicationRecord), the built-in Rails implementation is used
automatically.
class User < ApplicationRecord
tokenize :api_token
end
user = User.create!
user.api_token # => "aBcD1234..." (64 base58 chars by default)
With prefix
When a prefix: is given the gem installs its own before_create callback.
class AccessToken < ApplicationRecord
tokenize :token, prefix: "tok", size: 32
end
AccessToken.create!.token # => "tok-aBcD1234..." ("tok-" + 32 random chars)
Custom size (no prefix)
class Session < ApplicationRecord
tokenize :session_id, size: 128
end
Custom retry budget
class InviteCode < ApplicationRecord
# Retry up to 5 times before raising TokenizeAttr::RetryExceededError
tokenize :code, prefix: "inv", retries: 5
end
Custom generator
Pass any callable as the second argument (or as a block) to replace
SecureRandom.base58 with your own algorithm. The callable receives size
and must return a String.
# Proc as second positional argument
class Order < ApplicationRecord
tokenize :reference, proc { |size| SecureRandom.alphanumeric(size) },
prefix: "ord", size: 12
end
Order.create!.reference # => "ord-aB3cD4eF5gH6"
# Method reference via &
class Order < ApplicationRecord
def self.reference_generator(size) = SecureRandom.alphanumeric(size)
tokenize :reference, &method(:reference_generator)
end
# Inline block (parentheses required)
class Order < ApplicationRecord
tokenize(:reference) { |size| SecureRandom.alphanumeric(size) }
end
Note: Providing a generator always uses the callback path — even when no
prefix:is given. The generator takes precedence overhas_secure_token.
Multiple tokenized attributes
class ApiCredential < ApplicationRecord
tokenize :public_key, prefix: "pk", size: 32
tokenize :private_key, prefix: "sk", size: 64
end
Options
| Option | Default | Description |
|---|---|---|
generator |
nil |
Proc/block called as generator.call(size) → String. |
Overrides the default SecureRandom.base58 algorithm. |
||
size |
64 |
Length passed to the generator or to SecureRandom.base58. |
prefix |
nil |
String prepended as "prefix-<token>". |
retries |
3 |
Max uniqueness-check attempts (custom callback path only). |
Note:
retriesis ignored when delegating tohas_secure_tokenbecause Rails does not perform uniqueness checks there. Use a DB unique index to enforce uniqueness and let the DB raise on collision.
Error handling
When the custom callback exhausts all retry attempts it raises
TokenizeAttr::RetryExceededError (a subclass of TokenizeAttr::Error < StandardError).
begin
InviteCode.create!
rescue TokenizeAttr::RetryExceededError => e
Rails.logger.error(e.)
# => "Could not generate a unique token for :code after 5 retries"
end
Rails integration
Run rails tokenize_attr:install once after adding the gem. The generated
initializer (config/initializers/tokenize_attr.rb) hooks into
ActiveSupport.on_load(:active_record) so every model gains the tokenize
macro without an explicit include.
For non-Rails classes that provide before_create and exists? include the
concern manually:
class MyModel
include TokenizeAttr::Concern
end
Recommended migration
Add a unique index on tokenized columns to enforce uniqueness at the DB level:
add_column :access_tokens, :token, :string
add_index :access_tokens, :token, unique: true
Development
bin/setup # install dependencies
bundle exec rake test # run the test suite
bin/console # interactive prompt
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/pniemczyk/tokenize_attr.
License
The gem is available as open source under the terms of the MIT License.