- 更新日: 2014年6月13日
- Rails
Railsで /users/123 の代わりに /userid でユーザーページにアクセス可能にする
Rails のデフォルトでは、ユーザーのプロフィールページのパスは、http://example.com/users/123 のように /users/ 以下にユーザー固有の数字idのパスとなります。ユーザー固有の値としてユーザーが決めた userid 的な値を利用して、かつドメイン直下で http://example.com/userid というパスでもアクセス可能にしたい。
— 環境 —
Rails 4.0.1
ユニークな userid フィールドを追加
まず、サインアップ時に userid を入力してもらうようにします。また userid フィールドは DB および validation で、unique 強制しておきます。サインアップフォームに独自の入力フィールドを追加するのは以下を参考。
Rails4 Devise でサインアップ用のユーザー登録フォームに、独自の入力フィールドを追加する | EasyRamble
canonical_user_path メソッドを ApplicationController に定義
ユーザーが正当な userid を持っている場合は、/userid のパスを返す canonical_user_path というメソッドを作ります。
app/controllers/application_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class ApplicationController < ActionController::Base # ... helper_method :canonical_user_path # return canonical user path def canonical_user_path(user) unless user.is_a? User user = User.find(user) end if user.blank? return nil end if user.provider.blank? && user.userid.present? # for a normal user who has a canonical userid. # return user's path like root/shinji "/#{user.userid}" else # for oauth users. they don't have a canonical userid. # return user's path like root/users/123 user_path(user) end end # ... end |
canonical_user_path メソッドは、ユーザーが正当な userid を持っている場合は、ユーザーのプロフィールページのURLとして、http://example.com/shinji のようなドメイン名直下にユーザー名というパスを返します。また、ApplicationController に定義して、helper_method で指定することにより、コントローラーとビューのどこからでも使えるようにしています。
1 |
if user.provider.blank? && user.userid.present? |
の判定は、通常フォームと OAuth の両方による認証を想定しているためです。詳しくは以下を。
Rails4 で Devise と OmniAuth で、Twitter/Facebook のOAuth認証と通常フォームでの認証を併用して実装 | EasyRamble
これで、コントローラーやビューから canonical_user_path(@user) としてやると、正当な userid を持つ user の場合は “/userid” がユーザーページのパスとして返されます。
http://example.com/userid 用のコントローラーとビュー
UsersController に show_by_userid というメソッドを定義します。
app/controllers/users_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class UsersController < ApplicationController # ... def show_by_userid @user = User.find_by!(userid: params[:userid]) # ... render :template => "users/show" end def show @user = User.find(params[:id]) # ... end # ... end |
show_by_userid メソッドの中身は、show メソッドとほぼ同じです。show_by_userid では、@user の検索に userid を用います。あと、最後にビュー用のテンプレートとして、show アクションと同じものを指定するために、render :template => “users/show” を追加しています。
その他の部分の、show_by_userid と show のロジックは同じですので、適宜 private メソッドに追いやって呼び出すようにすると DRY 違反を防げる。
ルーティング設定
ルーティングに以下を設定します。
config/routes.rb
1 |
get '/:userid', to: 'users#show_by_userid' |
http://example.com/userid を、users コントローラーの show_by_userid アクションにルーティングさせます。get メソッドを指定。
以上で、例えば id が 123, userid が “shinji” である正当な userid を持つユーザーの場合、/users/123 および /shinji の両方のパスで、ユーザーのプロフィールページにアクセスできます。
canonical_user_path メソッドをテスト
ApplicationController のメソッドをテストする場合、Anonymous Controller を使ってやると良いそうです。以下のテストを書きました。
spec/controllers/application_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
require 'spec_helper' describe ApplicationController do controller do def index @normal_user = User.create( name: "normal", provider: "", uid: "12345", userid: "normal_id", email: "normal@example.com", password: "hogefuga", password_confirmation: "hogefuga" ) @facebook_oauth_user = User.create( id: 123456789, name: "facebook oauth", provider: "facebook", uid: "12345", userid: "facebook_id", email: "facebook@example.com", password: "hogefuga", password_confirmation: "hogefuga" ) @normal_user_path = canonical_user_path(@normal_user) @normal_user_path_by_id = canonical_user_path(@normal_user.id) @fb_user_path = canonical_user_path(@facebook_oauth_user) @fb_user_path_by_id = canonical_user_path(@facebook_oauth_user.id) @text = [ @normal_user_path, @normal_user_path_by_id, @fb_user_path, @fb_user_path_by_id ].join(", ") render :text => @text end end it "should have correct canonical user path" do get :index expect(response.body).to eq "/normal_id, /normal_id, /users/123456789, /users/123456789" end # ... end |
canonical_user_path が正しいパスを返すかどうかだけをテストしたかったのですけど、書き方がトリッキーな気がします…。何か良い書き方がありそうなので、テストしたいことの意図だけ汲んで下さい。
通常のフォームからサインアップしたユーザーを @normal_user として、Facebook での OAuth により認証したユーザーを @facebook_oauth_user としています。@facebook_oauth_user はモック的なもので、provider に facebook を指定した User オブジェクト。
あとは、各々 canonical_user_path メソッドの実行結果をまとめて、@text に渡してレンダリング。response.body が canonical_user_path の正しい文字列となっているかをチェックしています。
user_path(id) メソッドが /users/userid を返すように変更する方法
以降補足です。
以下のように User モデルで to_param をオーバーライドすると、user_path が user の id ではなくて userid を使うように変更できます。
app/models/user.rb
1 2 3 4 5 6 7 8 |
class User < ActiveRecord::Base def to_param # overridden userid end end user = User.find_by(userid: 'shinji') user_path(user) # => "/users/shinji" |
この場合は、返されるパスは /users/shinji という形になります。私はドメイン直下にユーザー名というURLでアクセス可能にしたかったので、結局採用しませんでした。
- – 参考リンク –
- ActiveRecord::Integration
- How do I override rails route helper methods? – Stack Overflow
- anonymous controller – Controller specs – RSpec Rails – RSpec – Relish
- Anonymous Controller を使ってコントローラのテストを RSpec で記述する – happy lie, happy life
- Rails の関連記事
- RailsでMySQLパーティショニングのマイグレーション
- Rails ActiveRecordでdatetime型カラムのGROUP BY集計にタイムゾーンを考慮する
- RailsプラグインGemの作成方法、RSpecテストまで含めたrails pluginの作り方
- RailsでAMPに対応するgemをリリースしました
- Railsでrequest.urlとrequest.original_urlの違い
- Railsでwheneverによるcronバッチ処理
- Google AnalyticsのRails Turbolinks対応
- Railsアプリにソーシャル・シェアボタンを簡単設置
- Rails監視ツール用にErrbitをHerokuで運用
- Facebook APIバージョンのアップグレード手順(Rails OmniAuth)
- 2件のコメント
新しくルーティングを切って、
/about
とかでaboutページを表示したいときはどうすればいいのでしょうか?
user#show_by_useridに飛ばされてしまうので…
ARZ さん、こんにちは。私が使っている方法ではありますが…
1. ‘about’ など使われたくないユーザーIDでユーザー登録できないようにする Validator を作成
2. User モデルでその Validator を使ってバリデーションを行う
3. config/routes.rb で about の get ルーティングを users#show_by_userid のルーティングより上に(優先させて)設定
以下の記事が参考になります。
登録されるとつらいユーザー名を禁止するRails Validator
http://bitarts.jp/blog/archives/004363.html
登録されるとつらいユーザー名リスト
http://qiita.com/phimcall/items/4c559b70f70ea7f1953b
以上ですがよろしくお願いいたします!