gem FriendlyIdの紹介と注意点


gem FriendlyIdについて

FriendlyId Guideがあるので一部抜粋したものを翻訳します。

FriendlyIdはRubyのActive Recordのアドオンで、URLのidを文字列に置き換えることができます。

FriendlyIdを使用しない場合
http://example.com/states/4323454
with FriendlyId
http://example.com/states/washington
人間が読めるキーワードを使ってページを識別するURLの部分を作成します。
これにより、ユーザーにとっても検索エンジンにとっても、アプリケーションをよりフレンドリーなものにすることができます。

gemの名前のFriendlyIdの由来はここから付けられたものだと言うことがわかりますね。

実装の例

弊社のサービスCarelyでも実際にfriendly_idを使用しているので紹介したいと思います。

class Customer < ApplicationRecord
  include FriendlyId ---- 

  friendly_id :uuid, use: :finders ---- 

①modelで FriendlyId を使用するには、まず FriendlyId moduleをincludeする必要があります。

②これはuuidでactiverecordのfind methodを使えるようにする記述です。

customer: {
  id: 10,
  uuid: "28d0d2b8-3f2c-49d0-bf11-3b01ec164311"
  firstName: "hogehoge",
  lastName: "fugafuga", 
  mailAddress: "[email protected]",
  age": "30"
  ・
  ・
  ・
}

上記のようなcsutomerのデータがあったとします。

本来findは主キー(id)しか対応していないため
customer = Customer.find(10)
のような記述になりますが、
friendly_id :uuid, use: :finders
を記述することで
customer = Customer.find("28d0d2b8-3f2c-49d0-bf11-3b01ec164311")
とuuidでもfind methodを使用することができます。
また、URLについても該当のcustomerの情報を表示する場合に
http://example.com/customer/10
となっていましたがFriendlyIdを使用することで
http://example.com/customer/28d0d2b8-3f2c-49d0-bf11-3b01ec164311
となります。
FriendlyIdを使うメリットではFriendlyId Guideに記載されているように
FriendlyIdを使用しない場合
http://example.com/states/4323454
with FriendlyId
http://example.com/states/washington
と名前をURLに含ませることで識別しやすくなる点もあるのですが弊社のようにuuidを使うことで何番目のデータだと推測できなくなり、テーブルのサイズを推測することもできなくなるというメリットもあります。

またsluggedという機能を使うとURLをフォーマットすることもできます。
FriendlyId Guideに記載されていた例では以下のようにtitleへスペース入りの文字列を入れた場合、本来はURLは/posts/this%20is%20the%20first%20post のようになっているはずですがSluggedを使用することで /posts/this-is-the-first-postと見やすくなります。

# a migration
class CreatePosts < ActiveRecord::Migration
  def self.up
    create_table :posts do |t|
      t.string :title, :null => false
      t.string :slug, :null => false
      t.text :body
    end

    add_index :posts, :slug, :unique => true
  end

  def self.down
    drop_table :posts
  end
end

# model
class Post < ActiveRecord::Base
  extend FriendlyId
  friendly_id :title, :use => :slugged
end
# Sluggedを使うことで整形され見やすくなる
@post = Post.create(:title => "This is the first post!)
@post.friendly_id # "this-is-the-first-post "を返します。
redirect_to @post # URLは/posts/this-is-the-first-postになります。

注意点

model内でFriendlyIdを使っているかどうかはちゃんと記述を見て判断しましょう。
大体のmodelでFriendlyIdを使ってるからこのmodelでも使ってるだろうという先入観で使うと???となる(なった)というお話です。

例えばcompanyというmodelではuuidというカラムがあるがFriendlyIdとして使っていなかった場合
company = Company.find("16d28548-2e57-4e67-a041-1f05acfdabba")
みたいに記述すると
company = Company.find(16)
の結果が表示されます。
これはfind methodが引数に文字列を渡した場合に.to_iで整数に変換しているからだと思われます。
エラーになればFriendlyIdを使っていないんだなと分かるのですが
データが返ってきたので正常にuuid = "16d28548-2e57-4e67-a041-1f05acfdabba"のデータが取れたのだと一瞬間違ってしまいますね。