You're so vain, you probably think this URL is about you.
August 08, 2014
In Ruby on Rails, when you want to make a particular route that has a parameter with spaces in it, you would typically get something like /regions/new%20york for a region named new york. This however looks terrible and your users would be very upset about typing this out. Fortunately, there is a way to remedy this by creating a URL slug.
First add a column called slug to the table of the resource you want to fix.
rails generate migration AddSlugToRegion slug
This creates the migration that you MUST edit before running rake db:migrate
.
The preferred way is to copy and parameterize the name column of each entry after the column is created.
class AddSlugToRegion < ActiveRecord::Migration
class Region < ActiveRecord::Base
end
def change
add_column :regions, :slug, :string, index: true
Region.all.each do |region|
region.slug = region.name.parameterize
region.save
end
end
end
You don’t have to open up the Region model as you are just adding to a new column and you shouldn’t have any validations affecting it, but it can be a good habit to use. Then, you are adding a column called slug which is a string and is indexed. Lastly, you are iterating through each row and changing the column to the parameterized name.
You must also edit the corresponding model.
class Region < ActiveRecord::Base
validates :name, uniqueness: true, presence: true
after_create :generate_slug
before_update :assign_slug
def to_param
slug
end
private
def assign_slug
self.slug = to_slug
end
def generate_slug
update_attributes(slug: to_slug)
end
def to_slug
name.parameterize
end
end
This seems like a lot to add, but what is happening, during record creation, is it is updating the slug after the record is created. For updating, it must be before the record is saved because it would already have a url assigned to it.
The controller must also be edited.
class RegionsController < ApplicationController
...
def show
@region = Region.find_by(slug: params[:id])
end
...
end
Here it is finding the region by its slug value. So the slug value new-york
would be equal to the record that has new york
in the name field. Now you know how to combine this post to use the to_param
method with the current post to really generate nice routes with any number of named records. I should note, make sure you validate that the name would not equal a path you would use, like new
or edit
. Look for more dynamic routes in a future post.