How Rubycoders use FriendlyId gem

Cristiano - 2017-03-20

By default Ruby On Rails shows jobs advertisement published at Rubycoders with a progessive number, instead we want a better url representation using a slug.

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

Installation

Add FriendlyId to your Gemfile

gem 'friendly_id' 

and then install with bundler

$ bundle install

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

# app/models/job.rb

private

def should_generate_new_friendly_id?
  slug.nil? || title_changed?
end

We're telling FriendlyId to generate new slugs when the slug is nil or the title has changed.

Let's add a migration for adding the slug attribute

$ rails generate migration add_slug_to_jobs

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

class AddSlugToJobs < ActiveRecord::Migration
  def up
    add_column :jobs, :slug, :string
    add_index :jobs, :slug, unique: true

    say_with_time 'generating job slugs' do
      Job.find_each(&:save)
    end
  end

  def down
    remove_index :jobs, :slug
    remove_column :jobs, :slug
  end
end

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

$ rake db:migrate

Usage

In the Job model we add:

  # app/models/job.rb

  extend FriendlyId
  friendly_id :title, use: :slugged

On the controller we use the friendly method

# app/controllers/companies_controller.rb

class JobsController < ApplicationController
  def show
    @job = Job.friendly.find(params[:id])
  end
end

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

Bonus

Pay attention with metaprogramming! After using FriendlyId I changed the controller for the favorite jobs. Favorite controller manage a polymorphic relation, so the friendly method is not always present.

  # app/controllers/favorites_controller.rb

  private
  def set_favorable
    @favorable = favorite_class.respond_to?(:friendly) ? favorite_class.friendly.find(params[:id]) : favorite_class.find(params[:id])
  end