homeASCIIcasts

210: Deviseのカスタマイズ 

(view original Railscast)

Other translations: En Es It Cn

Other formats:

Written by Naomi Fujimoto

前回のエピソード[動画を見る, 読む]で、Railsアプリケーションでユーザ認証を行うためにdeviseを設定する方法を紹介しました。今回は、前回から引き続いてdeviseをカスタマイズする方法を見ていきます。

前回と同じアプリケーションを使うので、登録、ログイン、ログアウトのページがあり、すでにある程度の認証のしくみができています。

プロジェクト管理用アプリケーション

アクセスを制限する

次のステップでは、ログインユーザのみに特定のアクションへのアクセスを制限します。ログインしたユーザだけがプロジェクトを作成、編集、削除できるようにします。このためにProjectsControllerを修正しbefore_filterを追加して、deviseが提供するauthenticate_user!というメソッドを呼び出すようにします。このメソッドはユーザがログインしているかどうかを確認し、ログインしていない場合はユーザをログインページにリダイレクトします。indexshowの各アクションはすべてのユーザから見えるべきなので、:exceptパラメータを追加してフィルターからこれらのアクションを除外します。

/app/controllers/projects_controller.rb

class ProjectsController < ApplicationController
  
  before_filter :authenticate_user!, :except => [:show, :index]

  def index
    #rest of class

「新規プロジェクト(New Project)」リンクをクリックすると、ログインしていない場合はログインページにリダイレクトされます。

新規プロジェクトを作成しようとするとログインページにリダイレクトされる

認証が単純でユーザがログインしているかどうかを確認するだけでよければ、この手法でうまくいくでしょう。より複雑な認証のニーズに対しては、エピソード192 [動画を見る, 読む]で紹介したCanCanのような追加の認証ツールをdeviseと一緒に使用することができます。

Deviseのビューをカスタマイズする

次にdeviseのビューの見え方をカスタマイズする方法を紹介します。すでにサイトの見た目をかなり作り込んでいる場合、deviseが提供するフォームも他のページに合うデザインにしたいでしょう。deviseはRailsエンジンなのでそれ自身がビューを持っていますが、アプリケーションにビューを追加することで簡単にオーバーライドできます。deviseはまたそれらのビューを簡単にコピーするためのジェネレータも提供します。アプリケーションのディレクトリからrails generate devise_viewsを実行すると、ビューファイルが自動的に作成されます。

$ rails generate devise_views
      create  app/views/devise
      create  app/views/devise/confirmations/new.html.erb
      create  app/views/devise/mailer/confirmation_instructions.html.erb
      create  app/views/devise/mailer/reset_password_instructions.html.erb
      create  app/views/devise/mailer/unlock_instructions.html.erb
      create  app/views/devise/passwords/edit.html.erb
      create  app/views/devise/passwords/new.html.erb
      create  app/views/devise/registrations/edit.html.erb
      create  app/views/devise/registrations/new.html.erb
      create  app/views/devise/sessions/new.html.erb
      create  app/views/devise/shared/_links.erb
      create  app/views/devise/unlocks/new.html.erb

このコマンドですべてのビューがRailsエンジンであるdeviseからコピーされるので、それらを目的に合うように編集することができます。例えば下のコードは前に見たログインページ用のビューです。

/app/views/devise/sessions/new.html.erb
<h2>Sign in</h2>

<%= form_for(resource_name, resource, :url => session_path(resource_name)) do |f| %>
  <p><%= f.label :email %></p>
  <p><%= f.text_field :email %></p>

  <p><%= f.label :password %></p>
  <p><%= f.password_field :password %></p>

  <% if devise_mapping.rememberable?-%>
    <p><%= f.check_box :remember_me %> <%= f.label :remember_me %></p>
  <% end -%>

  <p><%= f.submit "Sign in" %></p>
<% end %>

<%= render :partial => "devise/shared/links" %>

このページを次のように修正します。

/app/views/devise/sessions/new.html.erb

<% title "Sign In" %>

<%= form_for(resource_name, resource, :url => session_path(resource_name)) do |f| %>
  <ol class="formList">
    <li><%= f.label :email %> <%= f.text_field :email %></li>
    <li><%= f.label :password %> <%= f.password_field :password %></li>
    <% if devise_mapping.rememberable?-%>
    <li><%= f.check_box :remember_me %> <%= f.label :remember_me %></li>
    <% end %>
    <li><%= f.submit "Sign in" %></li>
  </ol>
<% end %>
<%= render :partial => "devise/shared/links" %>

上のコードで、Ryan Batesのnifty generatorsが提供するtitleメソッドの呼び出しでヘッダを置き換えました。つまりヘッダのテキストがページのタイトルにも現れます。(この方法はエピソード30 [動画を見る, 読む]で紹介しました。) またページのレイアウトも修正して、フォーム要素がリスト項目として表示されるようにしました。CSSでスタイルを定義することで、これを使って各テキストフィールドのラベルを移動してフィールドの横に表示させることができます。

カスタマイズされたログインフォーム

他のすべてのビューも同じ方法でカスタマイズできるので、アプリケーションの他のページとデザインを合わせることができます。

エラーメッセージをカスタマイズする

Deviseは、何か間違った操作をした場合に表示するエラーメッセージをいくつか持っています。例えば、正しくないメールアドレスやパスワードを入力すると「メールアドレスかパスワードが不正です」というメッセージが表示されます。これらのメッセージはすべてlocaleファイルに保存されているので、簡単に修正したり他の言語に翻訳することが可能です。下のlocaleファイルではdevise.failure.invalidメッセージを修正しました。

/config/locales/devise.en.yml

en:
  errors:
    messages:
      not_found: "not found"
      already_confirmed: "was already confirmed"
      not_locked: "was not locked"

  devise:
    failure:
      unauthenticated: 'You need to sign in or sign up before continuing.'
      unconfirmed: 'You have to confirm your account before continuing.'
      locked: 'Your account is locked.'
      invalid: 'OH NOES!ERROR IN TEH EMAIL!'
      invalid_token: 'Invalid authentication token.'
      timeout: 'Your session expired, please sign in again to continue.'
      inactive: 'Your account was not activated yet.'
    sessions:
      signed_in: 'Signed in successfully.'
      signed_out: 'Signed out successfully.'
#rest of file omitted.

不正なメールアドレスでログインしようとすると、書き換えたメッセージが表示されます。

カスタムのエラーメッセージが表示される

これでエラーメッセージは対応できましたが、deviseが表示するバリデーション(例えばユーザ登録時に誤った情報を入力した場合など)はどうすればいいでしょうか。

Deviseのデフォルトのバリデーションメッセージ

アプリケーションの/config/initializersディレクトリの中にdevise.rbというファイルがあり、このファイルにはdeviseを設定するために使用する多くのオプションが含まれています。これらのオプションについてはドキュメントに詳しい説明があるので、変更したい場合にはどの部分を修正すればいいかが簡単にわかるでしょう。例えば、パスワードの最小文字数を6から4に変えたければ、ファイル中の該当するパートの一番下の行のコメントをはずして、必要な変更を加えます。なんらか変更を加えた後は、アプリケーションにそれを反映させるためにサーバを再起動する必要があります。

/config/initalizers/devise.rb

  # ==> Configuration for :validatable
  # Range for password length
  # config.password_length = 6..20

これをさらに進めたければ、バリデーションのデフォルト設定を削除して、自分で書いたコードと置き換えます。Userモデルの中を見ると、アプリケーションが使用しているdeviseモジュールのリストがあり、その一つが:validatableです。

/app/models/user.rb

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :lockable, :timeoutable and :activatable
  # :confirmable,
  devise :database_authenticatable, :registerable, 
         :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation
end

:validatableモジュールは、登録時にメールアドレスとパスワードのバリデーション処理を行います。この動作を変更したい場合は、このモジュールを削除して自分でバリデーション処理を行うこともできます。しかしvalidatableモジュールはデフォルト設定のままでほとんどの用途に使えるので、今はそのままにしておきます。

ルーティング

次にルートのカスタマイズについて見ていきます。デフォルトでは登録ページは/users/sign_upにあるのですが、それを/registerに変更します。前回のエピソードではdeviseのジェネレータでdevise_for :usersというルートを作成しました。このルートにいくつかのパラメータを指定して目的に合うようにルートを修正します。

/config/routes.rb

ProjectManage::Application.routes.draw do |map|
  devise_for :users

  resources :projects
  root :to => 'projects#index'
end

追加できるパラメータのひとつは、:path_namesで、これを使って登録ページのルートを変更できます。

/config/routes.rb

ProjectManage::Application.routes.draw do |map|
  devise_for :users, :path_names => { :sign_up => "register" }

  resources :projects
  root :to => 'projects#index'
end

この修正によって、/users/sign_upにアクセスするとルーティングエラーが発生するようになり、代わりに/users/registerにアクセスさせます。devise_forルートに渡すことができるオプションは他にも多くあり、そのリストをドキュメントで参照できます。

ログイン要件をカスタマイズする

現在のアプリケーションではユーザのログインにメールアドレスとパスワードを用いていますが、メールアドレスの代わりにユーザ名を要求するようにしたければ、それも簡単に実現できます。

まず最初にUserテーブルにusername列を追加しますが、これをマイグレーションを作成して行います。

$ rails generate migration add_username_to_users username:string

次にマイグレーションを実行します。

$ rake db:migrate

データベースには1件のユーザしかないので、簡単にRailsコンソールにログインしてそのユーザのusername属性の値を設定します。

$ rails c
Loading development environment (Rails 3.0.0.beta2)
ruby-1.8.7-p249 > User.first.update_attribute(:username, "eifion")
 => true

データベースを修正したのでdeviseの設定ファイルも修正します。config.authentication_keysの行を非コメント化して、値を:emailから:usernameに変えます。

/config/initializers/devise.rb

config.authentication_keys = [ :username ]

この値が設定されると、deviseはusernameフィールドを認証キーとして使用するようになります。またログインフォームを修正して、メールアドレスの代わりにユーザ名のフィールドを配置します。

/app/views/devise/sessions/new.html.erb

<% title "Sign In" %>

<%= form_for(resource_name, resource, :url => session_path(resource_name)) do |f| %>
  <ol class="formList">
    <li><%= f.label :username %> <%= f.text_field :username %></li>
    <li><%= f.label :password %> <%= f.password_field :password %></li>
    <% if devise_mapping.rememberable?-%>
    <li><%= f.check_box :remember_me %> <%= f.label :remember_me %></li>
    <% end %>
    <li><%= f.submit "Sign in" %></li>
  </ol>
<% end %>

<%= render :partial => "devise/shared/links" %>

ユーザ登録フォームも同じように修正する必要があります。またUserモデルの属性に検証を追加しなくてはいけませんが、ここでは省略します。

サーバを再起動して設定の変更を反映させたら、ログインページにアクセスしてメールアドレスの代わりにユーザ名でログインできます。

ユーザ名でログインできるようになった

deviseのカスタマイズについての今回のエピソードはこれで終わりです。deviseはRailsアプリケーションで認証を行うための優れたツールで、デフォルト設定のままでも十分使用できますが、どのようにでもカスマイズできる柔軟性も持っています。