Rails 3.0用にrestful_authentication から deviseに移行してみる

Rails 2.3.8では、ユーザー認証にrestful_authenticationを使っていたのだけど、Rails 3.0ではdeviseが人気のようなので、こちらに移行してみる。

追記

"devise with all the bells and whistles" を作ってみるとして、リベンジ:Rails 2.3RC1で”Restful Authentication with all the bells and whistles”をやってみるで行ったことをdeviseで行ってみた。この二つのエントリーを読み比べてもらえるとわかりやすいと思う。

環境

目標

リベンジ:Rails 2.3RC1で”Restful Authentication with all the bells and whistles”をやってみるでできたことがdeviseを使ってもできるようにする。

Rails 2.3.8で作成したプロジェクトの3.0への移行

Web+DB press Vol. 58の特集、詳解Rails 3の「第6章 移行の手引き」にしたがって行った。概要だけ述べると以下のとおり。

  1. Rails 3.0 で新たにプロジェクトを作成する(仮に app_3 とする)
  2. Rails 2.3.8での既存プロジェクト(仮に app_2とする)の必要なファイルをコピーする。
    1. app_2/app, db, lib, public, script の必要なファイルをコピーする。
    2. app_2/config/environments.rb の主要な内容(Rails::Initializer.run do |config| から end の内側で定義されているもの)を app_3/config/application.rb へ移す。
  3. app_2内でrequireおよび使用しているgemライブラリを、すべて app_3/Gemfile で列挙する。
  4. Rails 3.0からconfig/routes.rbの書き方が変更になったので、app_2/config/routes.rb の内容を app_3/config/routes.rb へ新記法で書き直す。
  5. rails server で実行して、あとはエラーメッセージにしたがい動くまで直しつづける

deviseの導入

gemで導入する。

% gem1.8 install devise

app_3/Gemfileに以下を書き加える。

gem 'devise', '1.1.2

deviseを組み込む。app_3/ において以下のコマンドを実行する。

% rails generate devise:install

app_3/views/layouts/application.html.erbに以下を追加。

<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>

app_3/config/environments/development.rbに以下を追加。

config.action_mailer.default_url_options = { :host => 'localhost:3000' }

以上で準備終了。

restful_authentication 由来のものを確認

ほとんど、リベンジ:Rails 2.3RC1で”Restful Authentication with all the bells and whistles”をやってみるのとおりに作ってあるので、ここで指定したものを確認する。

  • モデル
    • permission.rb, role.rb, user.rb, user_mailer.rb, user_mailer.rb
  • コントローラー&ビュー
    • accounts_controller.rb, passwords_controller.rb, sessions_controller.rb, users_controller.rb
  • フィルター(lib/authenticated_system.rbに記載)
    • current_user, login_required, not_logged_in_required

私は、各コントローラーでbefore_filterとして、login_requiredとnot_logged_in_requiredを主に使っていた。また、各アクション内でcurrent_userを常用していた。

restful_authentication から deviseへ移行

わたしは、restful_authentication で Userというモデルを作っていた。あとから必要になるかもしれないので、既存のuserモデル関連のものを別名で保存しておく。

% cd app_3/app/models
% mv user.rb user.rb.pre
% cd ../../db/migrate
% mv *_create_users.rb create_users.rb.pre

app_2/config/environment.rbの中身をそのままapp_3/config/application.rbにコピーしている場合、以下の行が含まれていると、deviseでモデルが生成できないので、削除する。

config.active_record.observers = :user_observer

次にモデルを作成する。とりあえず、userというモデルを作成する。

% rails g devise user
     invoke  active_record
      create    app/models/user.rb
      invoke    test_unit
      create      test/unit/user_test.rb
      create      test/fixtures/users.yml
      create    db/migrate/20100905080023_devise_create_users.rb
      inject    app/models/user.rb
       route  devise_for :users


deviseは、モジュールを選択することで様々な機能を追加することができる。GitHub:deviseLazyLoadLife:Railsの第4世代認証エンジンDeviseのREADMEを翻訳してみたによると。自アプリケーションでパスワード管理をするのに関連するモジュールは以下のとおり(Token Authenticatable、Oauthable省略)

  1. 自アプリケーションでパスワード管理をするならば必ず選ぶ:Database Authenticatable
  2. ユーザーが自分でアカウント作成&編集&削除できるようにするならば選ぶ:Registerable
  3. restful_authenticationで言うactivationが必要ならば選ぶ:Confirmable
  4. パスワード忘れ対策が必要ならば選ぶ:Recoverable
  5. 「次回以降自動的にログインする」の機能を実現するならば選ぶ:Rememberable
  6. ログイン履歴をとるならば選ぶ:Trackable
  7. 一定期間操作を行っていなければ自動ログアウトを実現するならば選ぶ:Timeoutable
  8. 入力されたメールアドレスやパスワードのチェックをするならば選ぶ:Validatable
  9. 規定回数以上ログインに失敗したらアカウントをロックするならば選ぶ:Lockable

私がrestful_authenticationで使っていたのは、1, 2, 3, 4, 5, 8(ただし自作)のモジュール。なので、restful_authenticationで行えたことはdeviseを使って全部実現できる。公開は上の9つのモジュールすべてを使用することにする。

apt_3/db/migrate/*_devise_create_users.rb を以下のようにする(使いたいモジュールに対応する部分をコメントインする)。

class DeviseCreateUsers < ActiveRecord::Migration
  def self.up
    create_table(:users) do |t|
      t.database_authenticatable :null => false
      t.recoverable
      t.rememberable
      t.trackable
      t.confirmable
      t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
      # t.token_authenticatable

      t.timestamps
    end

    add_index :users, :email,                :unique => true
    add_index :users, :reset_password_token, :unique => true
    add_index :users, :confirmation_token,   :unique => true
    add_index :users, :unlock_token,         :unique => true
  end

  def self.down
    drop_table :users
  end
end

deviseが生成するUsersテーブルの中身の具体的な内容は db/schema.rb を見れば確認できる。

また、app_3/app/models/user.rb を以下のようにする(使用したいモジュールを列挙する)。

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

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

app_3/config/routes.rb 内の restful_authentication に関わる設定をすべてコメントアウトするか消す。私の場合は以下のものが該当する。

  match 'signup' => 'users#new', :as => 'signup'
  match 'login' => 'sessions#new', :as => 'login'
  match 'logout' => 'sessions#destroy', :as => 'logout'
  match 'forgot_password' => 'passwords#new', :as => 'forgot_password'
  match 'change_password' => 'accounts#edit', :as => 'change_password'
  match 'activate/:id' => 'accounts#show'
  match 'reset_password/:id' => 'passwords#edit'

また、以下を追加してログイン後のリダイレクト先を指定する。私の場合はユーザーのトップ画面をusers#showにしていたので、それを踏襲する。

  # root
  root :to => 'users#show'

  # for devise
  devise_for :users
  get 'users', :to => 'users#show', :as => :user_root

devise関連のビューを編集できるようにするためにビューをローカルに生成する。

% rails generate devise:views

app_3/app/view/devise 以下に生成されるディレクトリと同名のコントローラーがあると不備が生じるのでとりあえず別名にしておくこと。具体的には以下のコントローラー。括弧内はたぶん対応すると思われるモジュール名。

  • confirmations_controller(対応:Confirmable)
  • passwords_controller(対応:Recoverable)
  • registrations_controller(対応:Registerable)
  • sessions_controller(対応:Database Authenticatable)
  • unlocks_controller(対応:Lockable)

app_3/app/controllers/users_controller.rbから、restful_authentication由来のフィルタを外し、deviseのフィルターを入れる。外すフィルターは以下のとおり。

  before_filter :not_logged_in_required, :only => [:new, :create]
  before_filter :login_required, :only => [:show, :edit, :update]

login_requiredを以下に置き換える。

before_filter :authenticate_user!, :only => [:show, :edit, :update]

データベースを更新して、サーバーを動かせばちゃんと動くはず。うごかない場合にはrestful_authentication由来の関数が存在するのが原因。

% rake db:migrate
% rails server

メソッドの対応は多分こんな感じ

restful_authentication devise
current_user current_user
before_filter :login_required before_filter :authenticate_user!
logged_in? user_signed_in?

とりあえず、ここまで。