class AddUniqueSlugToArticles < ActiveRecord::Migration[6.1]
def change
add_column :articles, :slug, :string, null: false
add_index :articles, :slug, unique: true
end
end
class Articles::Create
def initialize(params)
@params = params
end
def call
Article.create!(@params.merge(slug: build_slug(@params.fetch(:title))))
rescue ActiveRecord::RecordNotUnique
Article.create!(@params.merge(slug: "#{build_slug(@params.fetch(:title))}-#{SecureRandom.hex(3)}"))
end
private
def build_slug(title)
title.to_s.parameterize.presence || SecureRandom.hex(4)
end
end
Slug generation is deceptively racy under concurrency. Use a unique index plus retry with a suffix. Keep it deterministic and fast; don’t query in a loop without bounds.