Rails4: deviseを使ってみる

# はじめに

deviceは認証系で有名という話を聞き、これはキャッチアップしなければ！ということで実際に動かしてみようと思います。
https://github.com/plataformatec/devise
ここでは、まず動かすということで、設定はほぼデフォルトの状態です。

# Railsアプリのひな形まで作成

* 各モジュールのバージョンは下記のとおりです。インストールされていないのであれば適宜入れます。

```
$ ruby -v
ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-linux]
 
$ rails -v
Rails 4.2.5
 
$ bundle -v
Bundler version 1.11.2
```

* アプリの枠を作ります。

```
$ rails new myapp
```

# deviseのインストール

* Gemfileにdeviseを追加します。

```ruby:Gemfile
gem 'devise'
```

* bundle installを実行します。

```
$ bundle install

$ bundle show | grep devise
  * devise (3.5.3)
```

# deviseの設定

### 0. deviseのインストール

```
$ rails g devise:install
Running via Spring preloader in process 8433
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

Some setup you must do manually if you haven't yet:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

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

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

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

  4. If you are deploying on Heroku with Rails 3.2 only, you may want to set:

       config.assets.initialize_on_precompile = false

     On config/application.rb forcing your application to not access the DB
     or load models when precompiling your assets.

  5. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================

```
上記のメッセージを順に対応していきます。

### 1. デフォルトURLの設定

```ruby:config/environments/development.rb
Rails.application.configure do
  ...

  # devise の設定
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
end

```


### 2. root_urlの設定

```ruby:config/routes.rb
Rails.application.routes.draw do
  root to: "home#index"
end
```

- このタイミングでhome#indexファイルも作成します。

```
$ rails g controller Home index
Running via Spring preloader in process 31047
      create  app/controllers/home_controller.rb
       route  get 'home/index'
      invoke  erb
      create    app/views/home
      create    app/views/home/index.html.erb
      invoke  test_unit
      create    test/controllers/home_controller_test.rb
      invoke  helper
      create    app/helpers/home_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/home.coffee
      invoke    scss
      create      app/assets/stylesheets/home.scss

```

### 3. エラーや情報メッセージの表示場所設定

```erb:app/views/layouts/application.html.erb
<body>

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

<%= yield %>

</body>
```

### 4. HerokuでRails3.2を利用する場合の設定

* Rails4なのでスキップします。

### 5. deviseのViewをカスタマイズするための設定

* 今回はシンプルで行くのでスキップします。


# deviseのユーザモデル

### Userモデルの作成
- モデルを生成して、ファイルの中身を見てみます。

```
$ rails g devise User
Running via Spring preloader in process 29880
      invoke  active_record
      create    db/migrate/20160105095646_devise_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      insert    app/models/user.rb
       route  devise_for :users
```

- ユーザモデルでコメントアウトされている:confirmable や :lockable は、migrateファイルのコメントアウトと一致します。つまりコメントを外すとmigrateファイルのコメント部分もはずさないとなりません。

```ruby:app/models/user.rb
  1 class User < ActiveRecord::Base
  2   # Include default devise modules. Others available are:
  3   # :confirmable, :lockable, :timeoutable and :omniauthable
  4   devise :database_authenticatable, :registerable,
  5          :recoverable, :rememberable, :trackable, :validatable
  6 end
```

- DBのカラム情報です。

```ruby:db/migrate/20160105095646_devise_create_users.rb
  1 class DeviseCreateUsers < ActiveRecord::Migration
  2   def change
  3     create_table(:users) do |t|
  4       ## Database authenticatable
  5       t.string :email,              null: false, default: ""
  6       t.string :encrypted_password, null: false, default: ""
  7 
  8       ## Recoverable
  9       t.string   :reset_password_token
 10       t.datetime :reset_password_sent_at
 11 
 12       ## Rememberable
 13       t.datetime :remember_created_at
 14 
 15       ## Trackable
 16       t.integer  :sign_in_count, default: 0, null: false
 17       t.datetime :current_sign_in_at
 18       t.datetime :last_sign_in_at
 19       t.string   :current_sign_in_ip
 20       t.string   :last_sign_in_ip
 21 
 22       ## Confirmable
 23       # t.string   :confirmation_token
 24       # t.datetime :confirmed_at
 25       # t.datetime :confirmation_sent_at
 26       # t.string   :unconfirmed_email # Only if using reconfirmable
 27 
 28       ## Lockable
 29       # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
 30       # t.string   :unlock_token # Only if unlock strategy is :email or :both
 31       # t.datetime :locked_at
 32 
 33 
 34       t.timestamps null: false
 35     end
 36 
 37     add_index :users, :email,                unique: true
 38     add_index :users, :reset_password_token, unique: true
 39     # add_index :users, :confirmation_token,   unique: true
 40     # add_index :users, :unlock_token,         unique: true
 41   end
 42 end

```

### migrateする

- DBを作っていなければ作成し、config/database.ymlを変更の上、migrateコマンドを実行します。

```
$ rake db:migrate
== 20160105095646 DeviseCreateUsers: migrating ================================
-- create_table(:users)
   -> 0.0059s
-- add_index(:users, :email, {:unique=>true})
   -> 0.0161s
-- add_index(:users, :reset_password_token, {:unique=>true})
   -> 0.0144s
== 20160105095646 DeviseCreateUsers: migrated (0.0366s) =======================
```

- mysqlを利用したので下記が生成されることが確認できます。

```
mysql> desc users;
+------------------------+--------------+------+-----+---------+----------------+
| Field                  | Type         | Null | Key | Default | Extra          |
+------------------------+--------------+------+-----+---------+----------------+
| id                     | int(11)      | NO   | PRI | NULL    | auto_increment |
| email                  | varchar(255) | NO   | UNI |         |                |
| encrypted_password     | varchar(255) | NO   |     |         |                |
| reset_password_token   | varchar(255) | YES  | UNI | NULL    |                |
| reset_password_sent_at | datetime     | YES  |     | NULL    |                |
| remember_created_at    | datetime     | YES  |     | NULL    |                |
| sign_in_count          | int(11)      | NO   |     | 0       |                |
| current_sign_in_at     | datetime     | YES  |     | NULL    |                |
| last_sign_in_at        | datetime     | YES  |     | NULL    |                |
| current_sign_in_ip     | varchar(255) | YES  |     | NULL    |                |
| last_sign_in_ip        | varchar(255) | YES  |     | NULL    |                |
| created_at             | datetime     | NO   |     | NULL    |                |
| updated_at             | datetime     | NO   |     | NULL    |                |
+------------------------+--------------+------+-----+---------+----------------+
13 rows in set (0.00 sec)

```

### routeの確認

- Userモデル作成時にrouteも自動で生成されているので確認します。

```
$ rake routes
                  Prefix Verb   URI Pattern                    Controller#Action
              home_index GET    /home/index(.:format)          home#index
        new_user_session GET    /users/sign_in(.:format)       devise/sessions#new
            user_session POST   /users/sign_in(.:format)       devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)      devise/sessions#destroy
           user_password POST   /users/password(.:format)      devise/passwords#create
       new_user_password GET    /users/password/new(.:format)  devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format) devise/passwords#edit
                         PATCH  /users/password(.:format)      devise/passwords#update
                         PUT    /users/password(.:format)      devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)        devise/registrations#cancel
       user_registration POST   /users(.:format)               devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)       devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)          devise/registrations#edit
                         PATCH  /users(.:format)               devise/registrations#update
                         PUT    /users(.:format)               devise/registrations#update
                         DELETE /users(.:format)               devise/registrations#destroy
                    root GET    /                              home#index
```

# Viewの変更

ヘルパーメソッドを使ってViewにリンクを追加します。

### deviseのヘルパーメソッド

- `user_signed_in?`: ログイン中か確認
- `current_user`: 　ログイン中のユーザ（オブジェクト）
- `user_session`: 現在のスコープのユーザセッション

もし、UserではなくMemberでモデルを作った場合は、user部がmemberに置き換えられるそうです。

### Viewにリンクの追加

```erb:app/views/layouts/application.html.erb
<body>
  <% if user_signed_in? %>
    ログイン中: <strong><%= current_user.email %></strong>
    <%= link_to 'プロフィール変更', edit_user_registration_path %> |
    <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
  <% else %>
    <%= link_to "サインアップ", new_user_registration_path %> |
    <%= link_to "ログイン", new_user_session_path %>
  <% end %>

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

<%= yield %>

</body>
```

# 動かしてみる

### 起動
- `rails s`
- http://localhost:3000/ にアクセス

### 画面遷移

- 初期画面
![016.png](https://qiita-image-store.s3.amazonaws.com/0/92328/f0581017-d37b-d8c3-ccf6-93bb492696cd.png)


- サインアップ画面 （登録に成功すると、DBにレコードが挿入される）
![013.png](https://qiita-image-store.s3.amazonaws.com/0/92328/362ee78a-5603-abfc-b0f8-c3d6ed8db768.png)
- プロフィール変更
![014.png](https://qiita-image-store.s3.amazonaws.com/0/92328/6a86048b-8958-900d-d385-a77205a26a46.png)
- ログアウト
![015.png](https://qiita-image-store.s3.amazonaws.com/0/92328/cb9ac357-9740-b927-f9e4-3be4bc31a698.png)
- エラーメッセージの表示
![012.png](https://qiita-image-store.s3.amazonaws.com/0/92328/b601ff4f-f243-ce30-93ee-9a6f7fba67d1.png)



# ハマったところ

* ローカルにrailsを入れず、bundleのほうを使ったら、エラーが出ました。
　bundleインストール済みの状態でもありネットで検索しても原因不明…。

```
$ bundle exec rails g devise:install
.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- bundler/setup (LoadError)
```

* bundle installでpath指定したらフリーズした。原因不明。このためpath未指定で対応。
  途中で切断すると下記のようなメッセージが表示されました。

```
bundle/ruby/2.2.0/gems/spring-1.6.1/lib/spring/client/run.rb:99:in `gets': Interrupt
from bundle/ruby/2.2.0/gems/spring-1.6.1/lib/spring/client/run.rb:99:in `verify_server_version'
```

* rvmを使っていたせいなのかは不明…。

# 参考サイト
http://ruby-rails.hatenadiary.com/entry/20140801/1406907000