Miguel Parramón Teixidó

A Social Network in Rails: Elegant permalinks

· Miguel Parramón Teixidó · Blogger

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: [email protected])
> 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

𒁾 𒊕 𒅆𒁺 →