showやeditなど、member系のアクションに、数値のIDなどではなく、TwitterIDのような一意の文字列でアクセスする方法。
例えば、
http://your.app.com/users/400
でなく
http://your.app.com/users/bob-smith
でアクセスできるようにする。
単純にURLのかっこ良さが上がる気がするし、文字列でのアクセスのみ受け入れるようにしたら、サイトの持っている情報のスクレイピング対策にもなる。
例として、適用するモデル名をUser、urlに使用するカラム名をcanonical_nameとする。ここではidとcanonical_nameのどちらでもアクセスできるようにする例を書く。
モデル側のコード
class User < ActiveRecord::Base
validates :canonical_name,
uniqueness: { case_sensitive: false },
format: { with: /^[A-Za-z][\w-]*$/ },
length: { minimum: 3, maximum: 25 }
def to_param
canonical_name ? canonical_name : id.to_s
end
def self.find_by_canonical_name_or_id(arg)
find_by_canonical_name(arg) || find(arg)
end
end
to_paramというActiveRecordのメソッドが肝。これで通常の表示がidからcanonical_nameに切り替わる。
validationは大体こんな感じにすると良いと思う。ここでは大文字小文字が違うだけのcanonical_nameは許さないようにしているが、そもそも大文字を禁止するのもありだと思う。
なお、ここではcanonical_nameが設定されていないUserもいることを前提としている。
コントローラー側のコード
class UsersController < ApplicationController
before_filter :load_user, only: [:show, :edit, :update, :destroy]
## if you use cancan
# load_and_authorize_resource :user, find_by: 'find_by_canonical_name_or_id'
private
def load_user
@user = User.find_by_canonical_name_or_id(params[:id])
end
end
数字でなくてもidの情報はparams[:id]に入って渡される。
canonical_nameでしかアクセスできないようにしたい時はload_userでUser.find_by_canonical_nameを使えば良い。
別案
探すときに、find_by_canonical_name_or_idというのを使うとto_paramを変更しているクラスだけ、findでModelを探せなくなってしまう。これだと困る場合は、
def self.find(arg)
find_by_canonical_name(arg) || super
end
とfindを上書きしてしまえばいいかもしれない。
追記
https://gist.github.com/agnellvj/1209733 にもっといろんな方法が載っているのを発見。興味ある人は見てみるべし。