A Social Network in Rails: Elegant permalinks


In this post we're adding permalinks to a Rails app using the friendly_id gem.

Let's say you have a `User` and a `Photo` model in your app, and a user has_many :photos.
To add a permalink to a photo post that looks like this:
www.nice-pics.com/berners-lee/photos/my-first-site



Add friendly_id to your Gemfile:
gem "friendly_id"
Add the permalink schema to your routes.rb:
get ":user_id/photos/:photo_id", to: "photos#show", as: :photo_permalink
Now, add the friendly_id scheme to your User:

class User::HasFriendlyId < ActiveRecord::Base
  extend FriendlyId
  friendly_id :slug_candidates

  has_many :photos
  
  def slug_candidates
    # if there's a username clash, use the full email
    [:username, :email]
  end
  
  def username
    email.split("@").first
  end  
end
and to your Photo:
class Photo < ActiveRecord::Base
  include Rails.application.routes.url_helpers
  extend FriendlyId
  friendly_id :slug_candidates, use: :scoped, scope: :user

  belongs_to :user
  
  def slug_candidates
    # if there's a name clash, use the photo's name, width & height
    [:name, -> { "#{name}-#{width}-#{height}" }]
  end

  def permalink
    photo_permalink_url(
      user_id: user.friendly_id,
      photo_id: friendly_id,
      host: "https://www.example.com"
    )
  end
end
Add the slugs to your db via migrations:
rails generate migration add_slug_to_photo slug:string
rails generate migration add_slug_to_user slug:string
And finally, add the controller action:
class ClipsController < ApplicationController
  def show
    user = User.friendly.find(params[:user_id])
    @clip = user.photos.friendly.find(params[:clip_id])
  end
end
Done! Now, if you do in your Rails console:
> user = User.create(email: berners-lee@internet.org)
> user.photos.create(name: my-first-site, width: 200, height: 300)
The following link will work: www.nice-pics.com/berners-lee/photos/my-first-site
And, if we add another photo with the same name, its link will still look nice:
> user.photos.create(name: my-first-site, width: 400, height: 800)
www.nice-pics.com/berners-lee/photos/my-first-site-400-800

Human readable, clear, and permanent. Nice :D