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
