# Slimak
Slimak generates slugs from multiple attributes (columns) and helps persist them to the database with uniqueness and fast lookup.
Features
- Configure which columns to include in a slug: `slug_columns :name, :assignee_name, :urgency`
- Returns readable, parameterized slugs (uses ActiveSupport if available)
- Optionally persists slug to a DB column (default :slug)
- Ensures uniqueness by appending a numeric suffix when needed (e.g., `paint-the-wall`, `paint-the-wall-2`)
- Fast lookup via `Model.find_by_slug("...")` and optional DB index/unique constraint
- Rails Railtie automatically includes into ActiveRecord models
Installation
Add to your Gemfile:
```ruby
gem "slimak"
Usage (ActiveRecord) 1) Add a slug column and index to your model's table:
class AddSlugToTasks < ActiveRecord::Migration[6.0]
def change
add_column :tasks, :slug, :string
add_index :tasks, :slug, unique: true
# If you scope uniqueness (e.g., per project):
# add_index :tasks, [:project_id, :slug], unique: true
end
end
2) Configure the model:
class Task < ApplicationRecord
include Slimak::Sluggable # is auto-included by Railtie, but explicit include is fine
slug_columns :name, :urgency, :assignee_name
# optional:
# slug_options column: :permalink
# slug_options scope: :project_id
end
By default, a slug will be generated and saved before validation if the slug column is blank. If a generated slug collides with an existing record, a numeric suffix will be appended (e.g., -2, -3) until uniqueness is achieved.
3) Finding by slug:
Task.find_by_slug("paint-the-wall-2")
# or
Task.find_by_slug!("paint-the-wall")
Usage (Plain Ruby object)
class FakeTask
include Slimak::Sluggable
attr_accessor :name, :urgency, :assignee_name
slug_columns :name, :urgency, :assignee_name
end
t = FakeTask.new
t.name = "Paint the wall"
t.urgency = "Critical"
t.assignee_name = "Mark"
t.slug # => "paint-the-wall-critical-mark"
Generators
rails generate slimak:add_slug ModelName
Options: --column=NAME Column to add/store slug (default: slug) --scope=COLUMN_NAME Optional scope column (creates composite unique index)
Notes & Next steps
- For robust concurrency-safe uniqueness you should also enforce a UNIQUE index in the database and handle possible race conditions (e.g., retry on unique constraint violation). Slimak already appends numeric suffixes to avoid collisions, but an index prevents races.
- You can scope uniqueness via
slug_options scope: :project_idand add a composite unique index [project_id, slug]. - If you'd like, I can add a Rails generator to create migrations, or implement a DB-retry strategy to handle high concurrency collisions.
</code>