Before Rails Tutorial４章　ログイン機能


本投稿は講義資料であり、Rubyの基礎は理解しているが、rails tutorialで躓く読者を対象としています。
[１章：環境構築](https://qiita.com/earth660/items/a51874eba618e2aa5ce6)
[２章：Scaffold を用いた高速なアプリケーション構築 及び MVCの理解](https://qiita.com/earth660/items/c791f1e46875e8a9994e)
[３章：Scaffold を用いない開発方法　及び　応用](https://qiita.com/earth660/items/161fa9090860cbd174bf)
[４章：ログイン機能](https://qiita.com/earth660/items/6501eeb77c0e5ba9da6b)

3章で作成した、蔵書管理アプリ　bukukore　にログイン機能を付与していきます。
ログイン機能は、 Devise という GEM を用いて構築していきます。

[Deviseの使い方はこちら](https://github.com/heartcombo/devise#getting-started)

# Devise
bukukore ディレクトリ内の、Gemfile を立ち上げて下記のコードを追記してください。


```Gemfile:Gemfile(追記)
gem 'devise'
```
Gemfile に追記したら、 bundle install していきましょう。

```terminal:terminal
$ bundle install --path vendor/bundle
```

devise のインストールが完了したら、deviseを適応させていきます。


```terminal:terminal
$ rails g devise:install
(省略)
===============================================================================

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. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================
```

deviseをインストールすると、上記のようなメッセージが表示されます。
１つ目のメッセージに従い、デフォルトのURL を設定します。今回は指示通りで良いので、
config/environments/development.rb の一番下に下記のコードを追加します。

```ruby:config/environments/development.rb(追加)
(省略)
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
```

２つ目のメッセージは、３つ目のメッセージは、のちほど追加します。
４つ目のメッセージでViewを作成するのですが、そのまえにModelをつくっておきます。
devise で使いたいのは User登録機能なので 下記のコマンドを実行します。

```ruby:config/initializers/devise.rb(247行目あたりの該当箇所を書換。　なければ検索してください)
config.scoped_views = true
```


config.scoped_views を false から true に書き換えてください。
(devise をどのフォルダにインストールするかの設定です。)
(行頭の #は削除してください)
[使い方はこちら](https://github.com/heartcombo/devise#configuring-views)


```terminal:terminal
$ rails g devise User
  create   db/migrate/20200216064430_devise_create_users.rb
  create   app/models/user.rb
   route   devise_for :users
```
これで user.rb の他に、devise に関するマイグレーションファイルと、ルーティングが追加されています。
ルーティングは、 rails routes を実行することで確認できます。
マイグレーションファイルは、 db/migrate の中にあります。
devise では機能毎に ON / OFF 設定可能です。

| モジュール名　 |　　各機能の説明   |
|:--|:--|
| database_authenticatable | データベースに保存されたパスワードが正しいかどうかの検証とを行ってくれます。またパスワードの暗号化も同時に行ってくれます。 |
| registerable | ユーザー自身がアカウント登録、編集、削除することを許可します。 |
| recoverable | パスワードをリセットできるようにし、メールで通知します。 |
| rememberable | ３０日間ログインしたままにするというような永続ログインを可能にします。ログイン画面の下のチェックボックスにチェックすることで永続ログインを有効化できます。 |
| trackable | ユーザーのサインイン回数や、サインイン時間、IPアドレスなどを記録できるようにします。 |
| validatable | Emailやパスワードのバリデーションを可能にします。独自に定義したバリデーションを追加することもできます。 |
| confirmable | メールに記載されているURLをクリックして本登録を完了する、といったよくある登録方式を可能にします。また、サインイン中にアカウントが認証済みかどうかを検証します。 |
| lockable | 一定回数ログインを失敗するとアカウントをロックします。ロック解除にはメールによる解除か、一定時間経つと解除するといった方法があります。 |
| timeoutable | 一定時間活動していないアカウントのログインを破棄します。 |
| omniauthable | TwitterやFacebookなどのSNS認証を追加したい場合に使用します。 |



```ruby:db/migrate/2020(省略)_devise_create_users.rb
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      t.string   :confirmation_token
      t.datetime :confirmed_at
      t.datetime :confirmation_sent_at
      t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      t.string   :unlock_token # Only if unlock strategy is :email or :both
      t.datetime :locked_at


      t.timestamps null: false
    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
end

```

上記のようにすべての機能を解放しておきます。
（講義終了後に、興味ある学生が拡張して遊べるための配慮）
今回はメール認証を行いたいので Confirmable を使用します。
user.rb もすべての機能が使用できるように変更しておきます。


```ruby:app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :confirmable, :lockable, :timeoutable
end
```

マイグレーションファイル を変更したので適応させましょう。
マイグレーションファイルの実行は下記のコマンドでしたね。


```terminal:terminal
$ rails db:migrate
== 20200220202020 DeviseCreateUsers: migrating ================================
-- create_table(:users)
   -> 0.0021s
-- add_index(:users, :email, {:unique=>true})
   -> 0.0008s
-- add_index(:users, :reset_password_token, {:unique=>true})
   -> 0.0010s
== 20200220202020 DeviseCreateUsers: migrated (0.0041s) =======================
```

**rails db:migrate した後は、 rails sever を再起動しておいてください。**

マイグレーションを実行することでデータベースの中に、usersテーブルが作成されます。
４つ目のメッセージに書いてあるように、Devise が使用するView（アカウント作成関連） も作っていきます。

```terminal:terminal
$ rails g devise:views user
  app/views/devise/shared/_links.html.erb (リンク用パーシャル)
  app/views/devise/confirmations/new.html.erb (認証メールの再送信画面)
  app/views/devise/passwords/edit.html.erb (パスワード変更画面)
  app/views/devise/passwords/new.html.erb (パスワードを忘れた際、メールを送る画面)
  app/views/devise/registrations/edit.html.erb (ユーザー情報変更画面)
  app/views/devise/registrations/new.html.erb (ユーザー登録画面)
  app/views/devise/sessions/new.html.erb (ログイン画面)
  app/views/devise/unlocks/new.html.erb (ロック解除メール再送信画面)
  app/views/devise/mailer/confirmation_instructions.html.erb (メール用アカウント認証文)
  app/views/devise/mailer/password_change.html.erb （メール用パスワード変更完了文）
  app/views/devise/mailer/reset_password_instructions.html.erb (メール用パスワードリセット文)
  app/views/devise/mailer/unlock_instructions.html.erb (メール用ロック解除文)
```

上記コマンドを実行することで、 Devise の機能により、ユーザー作成関連の View が自動生成されます。
この時点でアカウント作成画面などにアクセスは可能ですが、メール認証機能は実装していないのでアカウントは作れません。
http://localhost:3000/users/sign_up

いまのうちに、新規登録、ログインへのリンクを用意しておきましょう。

```erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Bukukore</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <% if user_signed_in? %>  
      <%= 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 %>  
    <%= yield %>
  </body>
</html>

```

#メール認証
[Google 二段階認証アプリパスワードの発行方法](https://support.google.com/accounts/answer/185833?hl=ja)
まず、上記のURLにアクセスし、Googleの２段階認証アプリパスワードの発行を行いましょう。
学校のメールアドレスでは発行できないので、自身で作成したメールアドレスを使用しましょう。

config/initializers/devise.rb の21行目あたりにある
config.mailer_sender を自分の gmail アドレスに変更します。

```ruby:config/initializers/devise.rb
config.mailer_sender = '[自分のアドレスに置き換えます]@gmail.com'  

```

最後に開発環境の設定ファイルの最後に以下を追加します。

```ruby:config/environments/development.rb（追記）
(省略)
ActionMailer::Base.smtp_settings = {  
  address: 'smtp.gmail.com',  
  port: 587,  
  user_name: '[自分のアドレスに置き換えます]@gmail.com',  
  password: '先程取得した二段階認証のアプリパスワード',
  authentication: 'plain',  
  enable_starttls_auto: true  
}  

```

以上でメール認証処理は完了です。

> 「こんなの自力でわかるわけない！自力開発なんてできない」
> と思うかもしれませんが、GEMの使い方はリファレンスにすべて書いてあるのでご安心ください。
> 今後、リファレンスを読む癖、調べる癖をつけていきましょう。
> https://github.com/heartcombo/devise#getting-started
> https://qiita.com/gakkie/items/6ef70c0788c3cbff81ee

実際にユーザー登録を行い、メールが届くことを確認できます。
http://localhost:3000/users/sign_up
届いたメールに記載されている URL をクリックすることで、認証完了です。
rails server のログにも URL が書いてあるので、そちらをコピペしても認証可能です。
![スクリーンショット 2021-04-14 16.26.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33092/65f0bfc5-5496-6b3a-f0db-2c9fdc5000c5.png)


どういう仕組みで、メールの認証やログイン・ログアウトが行われているのか詳しくは
https://railstutorial.jp/ 
で学べます。


#ユーザーの一覧と詳細表示ページ
Userの一覧をみるページ、詳細情報をみるページを作成していきましょう。
Model はすでに作っているので、 Controller と View を作成します。

```terminal:terminal
$ rails g controller Users index show
```


２つ目のメッセージにあったルート設定もここでしておきましょう。
ユーザーの一覧ページ(users#index)をTOPページにしたいので、下記コードを追加します。

```ruby:config/routes.rb(追加)
（省略）
  root to: 'users#index'

```

これで、root（最初のページ）にアクセスがあった時に、usersControllerのindexアクションを呼び出します。
http://localhost:3000/


####課題 MVCの復習
１， User Model を使い、users テーブルから全データを取り出し
２， user index ページに表示してみましょう。
３, user show ページにユーザー情報を表示してください　http://localhost:3000/users/show/1

ヒント： Model は Controller に記述します。
一度何も見ずにやってみてみると理解度チェックになります。
曖昧な場合は、前章の内容を確認してください。

<details><summary>１の解答例はここをクリックすると表示されます</summary><div>


```ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def show
  end
end
```
</div></details>



<details><summary>２の解答例はここをクリックすると表示されます</summary><div>

```erb:app/views/users/index.html.erb
<h1>ユーザーの一覧</h1>

<% @users.each do |user| %>
  <p><%= user[:id] %></p>
  <p><%= user[:email] %></p>
<% end %>
```

</div></details>


<details><summary>３の解答例はここをクリックすると表示されます</summary><div>


```ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end
end
```


```erb:config/routes.rb(書き換え)
#   get 'users/show'(コメントアウト)
  get 'users/show/:id', to: 'users#show', as: 'user'
```
as: 'user' とすることで、user_path と記述すれば、アクセスできるようになります。

```erb:app/views/users/show.html.erb
<h1>ユーザーの情報</h1>

<p><%= @user.id %></p>
<p><%= @user.email %></p>
```

</div></details>


#新しいカラムの追加方法
現在 id,email と２つのカラムがあります。これだけでは誰か分かりづらいので
名前を登録するために、 name カラムを追加してみましょう。

```terminal:terminal
$ rails g migration AddNameToUsers name:string
```
[migration コマンドのまとめはこちら](https://qiita.com/zaru/items/cde2c46b6126867a1a64)

上記のコマンドで、usersテーブルに name カラムを追加するためのマイグレーションファイルが自動生成されます。

```ruby:db/migrate/2020(省略)_add_name_to_users.rb(自動生成)
class AddNameToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :name, :string
  end
end
```

マイグレーションファイルを実行します。

```terminal:terminal
$ rails db:migrate
== 20200222020202 AddNameToUsers: migrating ===================================
-- add_column(:users, :name, :string)
   -> 0.0017s
== 20200222020202 AddNameToUsers: migrated (0.0018s) ==========================
```
users テーブルに、nameカラムを追加することができました。

####ストロングパラメータに name を許可する
しかし、追加しただけでは保存できないようになっています。
これは rails が自動的行っているセキュリティ対策の為です
[ストロングパラメータについて](https://qiita.com/ozackiee/items/f100fd51f4839b3fdca8)

保存できるようにするためには、ストロングパラメータへの追加が必要です。



```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :permitted_parameters, if: :devise_controller?

  private

  def permitted_parameters
    devise_parameter_sanitizer.permit :sign_up, keys: [:name]
    devise_parameter_sanitizer.permit :account_update, keys: [:name]
  end

end 

```

permitted_parameters アクションの中を見てください。
devise_parameter_sanitizer.permit 【許可する場所】, keys:　【許可するカラム名】
というようにして、許可を与えます。

before_action は、このファイルが呼び出されたときに最初に実行されるものです。

これで新たに、名前を保存できるようになりました。

####課題
１， 登録画面に、名前を登録するためのフォームを追加してください。
　 　http://localhost:3000/users/sign_up
２， 実際に名前を登録し、 show ページで名前を表示してください。
　　 http://localhost:3000/users/show/1 (必要があればidは登録した番号に変更してください)


#BookテーブルとUserテーブルの関連付け
続いて、2章で学習したテーブルの関連付けの復習をやっていきます。
詳細ページに、そのユーザーが登録した、本の情報を表示していきましょう。
> http://localhost:3000/users/show/1 ←ユーザーの詳細ページ

> http://localhost:3000/books/new ←本の情報登録ページ
> http://localhost:3000/books/show/1 ←登録内容を確認

####課題
１．ユーザーの詳細ページに、登録した本の情報が表示されるようにしてください。

<details><summary>解答例はここをクリックすると表示されます</summary><div>

```ruby:app/models/user.rb(1行追加)
class User < ApplicationRecord
   has_many :books
  
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :confirmable, :lockable, :timeoutable
end
```

```ruby:app/models/book.rb
class Book < ApplicationRecord
  belongs_to :user
end
```


```ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
    @books = @user.books
  end
end
```



```erb:app/views/users/show.html.erb

<p><%= @user.id %></p>
<p><%= @user.email %></p>
<p><%= @user.name %></p>

<% @books.each do |book| %>
  <p>本のタイトル： <%= book.title %></p>
  <p>著者： <%= book.author %></p>
<% end %>
```

</div></details>


２． ログイン中のユーザーは、新規蔵書登録の際に、自動的に自身の user_id で投稿できるようにしてください。

<details><summary>ヒントはここをクリックすると表示されます</summary><div>


user_id の登録は、books_controller.rb の create アクションでおこなわれています。
ログイン中のユーザーID は current_user.id とすれば取得できます。
また、ログインしていない状態で蔵書登録を行うと、user_idがnilのエラーが出ます。
なので、コントローラーの先頭に`before_action :authenticate_user!` を記載する必要があります。

参考URL
[Rails deviseで使えるようになるヘルパーメソッド一覧](https://qiita.com/tobita0000/items/866de191635e6d74e392)

</div></details>

　
<details><summary>解答例はここをクリックすると表示されます</summary><div>

```ruby/app/controllers/books_controller.rb(一部変更)

  def create
    (省略)
    @book.user_id = current_user.id
    (省略)
  end

```
もともとあった
    @book.user_id = params[:book][:user_id]
を
    @book.user_id = current_user.id
に置き換えることで、ログイン中のユーザーの蔵書として登録できます。


```erb:app/views/books/new.html.erb(削除)

-  <%= f.label :user_id %>(削除)
-  <%= f.number_field :user_id %>(削除)

```
上記の２行は不要になるので削除しておきます。


</div></details>


https://bbbbruno.qrunch.io/entries/wcNZqbMwA5SRBqQj