Best way to generate slugs (human-readable IDs) in Rails
You know, like myblog.com/posts/donald-e-knuth.
Should I do this with the built in parameterize
method?
What about a plugin? I could imagine a plugin being nice for handling duplicate slugs, etc. Here are some popular Github plugins -- does anyone have any experience with them?
- http://github.com/rsl/stringex/tree/master
- http://github.com/norman/friendly_id/tree/master
Basically it seems like slugs are a totally solved problem, and I don't to reinvent the wheel.
Solution 1:
In Rails you can use #parameterize
For example:
> "Foo bar`s".parameterize
=> "foo-bar-s"
Solution 2:
The best way to generate slugs is to use the Unidecode gem. It has by far the largest transliteration database available. It has even transliterations for Chinese characters. Not to mention covering all European languages (including local dialects). It guarantees a bulletproof slug creation.
For example, consider those:
"Iñtërnâtiônàlizætiøn".to_slug
=> "internationalizaetion"
>> "中文測試".to_slug
=> "zhong-wen-ce-shi"
I use it in my version of the String.to_slug method in my ruby_extensions plugin. See ruby_extensions.rb for the to_slug method.
Solution 3:
I use the following, which will
- translate & --> "and" and @ --> "at"
- doesn't insert an underscore in place of an apostrophe, so "foo's" --> "foos"
- doesn't include double-underscores
- doesn't create slug that begins or ends with an underscore
def to_slug
#strip the string
ret = self.strip
#blow away apostrophes
ret.gsub! /['`]/,""
# @ --> at, and & --> and
ret.gsub! /\s*@\s*/, " at "
ret.gsub! /\s*&\s*/, " and "
#replace all non alphanumeric, underscore or periods with underscore
ret.gsub! /\s*[^A-Za-z0-9\.\-]\s*/, '_'
#convert double underscores to single
ret.gsub! /_+/,"_"
#strip off leading/trailing underscore
ret.gsub! /\A[_\.]+|[_\.]+\z/,""
ret
end
so, for example:
>> s = "mom & dad @home!"
=> "mom & dad @home!"
>> s.to_slug
> "mom_and_dad_at_home"
Solution 4:
Here is what I use:
class User < ActiveRecord::Base
before_create :make_slug
private
def make_slug
self.slug = self.name.downcase.gsub(/[^a-z1-9]+/, '-').chomp('-')
end
end
Pretty self explanatory, although the only problem with this is if there is already the same one, it won't be name-01 or something like that.
Example:
".downcase.gsub(/[^a-z1-9]+/, '-').chomp('-')".downcase.gsub(/[^a-z1-9]+/, '-').chomp('-')
Outputs: -downcase-gsub-a-z1-9-chomp
Solution 5:
I modified it a bit to create dashes instead of underscores, if anyone is interested:
def to_slug(param=self.slug)
# strip the string
ret = param.strip
#blow away apostrophes
ret.gsub! /['`]/, ""
# @ --> at, and & --> and
ret.gsub! /\s*@\s*/, " at "
ret.gsub! /\s*&\s*/, " and "
# replace all non alphanumeric, periods with dash
ret.gsub! /\s*[^A-Za-z0-9\.]\s*/, '-'
# replace underscore with dash
ret.gsub! /[-_]{2,}/, '-'
# convert double dashes to single
ret.gsub! /-+/, "-"
# strip off leading/trailing dash
ret.gsub! /\A[-\.]+|[-\.]+\z/, ""
ret
end