Our task today is to add unique slugs to our Company model on our Ruby on Rails app. The Company model has name and location properties, and we want to use those to create the slug. E.g. the company named Lugo Labs and based in London should have a slug, lugo-labs-london. We can then use that when showing the company page.

We could create the solution ourselves, but today we'll use the awesome FriendlyId to help us with it.

Installation

Installing FriendlyId is easy, we add it to our Gemfile:

ruby
# Gemfile
gem 'friendly_id'

and run:

sh
bundle install

In order to generate slugs for our existing Company records, we need to tell our FriendlyId when to generate the slugs, by adding this private method:

ruby
# apps/models/company.rb

private

def should_generate_new_friendly_id?
  slug.nil? || name_changed? || location_changed?
end

We're telling FriendlyId to generate new slugs when the slug is nil or name, or location attributes have changed.

Migration

Let's add a migration for adding the slug attribute (using Rails 5):

sh
rails g migration add_slug_to_companies slug

We could also generate the slugs as part of the migration, if we had existing company records.

ruby
class AddSlugToCompanies < ActiveRecord::Migration[5.0]
  def up
    add_column :companies, :slug, :string
    add_index :companies, :slug, unique: true

    say_with_time 'generating company slugs' do
      Company.find_each(&:save)
    end
  end

  def down
    remove_column :companies, :slug, :string
  end
end

Calling save on the models triggers slug generation, so let's run the migration and see our slugs created:

sh
rails db:migrate

We have also added a unique index on slug for good measure. FriendlyId also makes the slugs unique by default, appending a unique token to the next duplicated slug it finds. E.g. if we ever created another company Lugo Labs with location, London, its slug would be something like lugo-labs-london-127a893725532ahsr277.

Usage

In the Company model we add:

ruby
# apps/models/company.rb

extend FriendlyId
friendly_id %i(name location), use: :slugged

The friendly_id macro has many options, here we're just telling it to use name and location to create slugs. You can see more info on this on the FriendlyId's README.

On the controller we can the enhancements provided by extending FriendlyId:

ruby
# app/controllers/companies_controller.rb

class CompaniesController < ApplicationController
  def show
    @company = Company.friendly.find(params[:id])
  end
end

Considering that params[:id] is the company slug, rather than company id, the friendly method makes the finder to look up companies by slug, rather than id.

Conclusion

Now when we create new Company records, they will have unique slugs generated automatically. The companies URLs are all pretty with words in them, rather than the ids.

Happy coding!