【Rails】 binding.pryの活用方法

この記事では、binding.pryの使い方を解説しています。
　
binding.pryを活用することで、
・一次ソースに触れる機会が増え、学習効率が上がる
・binding.pryを複数設置して、paramsの流れが理解しやすくなる

など、たくさんメリットがあります。
　
自分みたいな、初学者方の参考になればと思い、記事にしてみました。
前提
---
チャットアプリを題材にbinding,pryの使い方を学びます。
(注意:この記事ではチャットアプリは完成しません！チャットアプリ作成の記事ではありません)



開発環境
・ruby 2.6.5
・Rails 6.0.3.3

完成イメージ
---
![chat_demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516687/b8e22694-826c-f4fc-9788-81f9c30d1180.gif)


ER図
---
![名称未設定ファイル (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516687/05ccd8e8-c5ce-0075-b2bb-a25420d31b5a.png)


必要なテーブル
・usersテーブル
・roomsテーブル
・entriesテーブル　(中間テーブルです！)



流れ
---
①user, room, entryモデル、テーブルを作成(下準備)

②アソシエーションを書く(下準備)

③Gemfileにpry-railsを追加してbundle installを実行(下準備)

④ビューにform_withを用意

⑤roomsコントローラーにcreateアクションを書く


①user, room, entryモデル、テーブルを作成(下準備)
---

__userモデルのマイグレーションを編集__
userモデルは、deviseを使って作成しているものとします！

```db/migrate/xxxx_devise_create_users.rb
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :nickname,  null: false
      t.string :email,     null: false, default: ""
      #省略
    end
    #省略
  end
end
```
usersテーブルには、nicknameとemailのカラムを用意しています。

__roomモデルのマイグレーションを編集__

```db/migrate/xxxx_create_rooms.rb
class CreateRooms < ActiveRecord::Migration[6.0]
  def change
    create_table :rooms do |t|
      t.string :name, null: false
      t.timestamps
    end
  end
end
```
roomsテーブルには、nameのカラムを用意しています。

__entryモデルのマイグレーションを編集(下準備)__

```db/migrate/xxxx_create_entries.rb
class CreateEntries < ActiveRecord::Migration[6.0]
  def change
    create_table :entries do |t|
      t.references :room, foreign_key: true
      t.references :user, foreign_key: true
      t.timestamps
    end
  end
end
```
entriesテーブルは、usersテーブルとroomsテーブルを繋ぐ中間テーブルなので、
user, roomそれぞれを外部キーとして、references型で保存するようにしています。

②アソシエーションを書く(下準備)
---

一人のuserは複数のroomに入れて、
一つのroomは複数人のuserが入るので、
usersテーブルとroomsテーブルは「多対多」の関係になります。
以下のように、アソシエーションを記述します。

__userモデル__

```app/models/user.rb
class User < ApplicationRecord
  #省略
  has_many :entries
  has_many :rooms, through: :entries
end
```

__roomモデル__

```app/models/room.rb
class Room < ApplicationRecord
  has_many :entries
  has_many :users, through: :entries
end
```

__entryモデル__

```app/models/entry.rb
class Entry < ApplicationRecord
  belongs_to :room
  belongs_to :user
end
```


③gem 'pry-rails' をインストール(下準備)
---
pry-railsをインストールできるようGemfileに記述して、bundle installを実行します。
参考 : [rweng/pry-rails: Rails >= 3 pry initializer - GitHub](https://github.com/rweng/pry-rails) 

```:Gemfile
gem 'pry-rails'
```

```:ターミナル
% bundle install
```

④ビューにform_withを用意
--

新しくルームを作成するために、roomsコントローラーにnewアクションを定義します。


```app/controllers/rooms_controller.rb
class RoomsController < ApplicationController
  def new
    @room = Room.new #newメソッドでインスタンスを作成
    @users = User.all #全ユーザーのレコードを取得
  end
end
```
　
ビューには、フォームを設置します。
チャットルーム名(name)を入力し、チャットしたい相手(user_ids)を選べるようにします。

```erb:app/views/rooms/new.html.erb
<%= form_with model: @room, local: true do |f| %>
  <%= f.label :チャットルーム名%>
  <%= f.text_field :name%>      　　　　  <%# 入力したチャットルーム名を取得 %>
  <label>チャットしたい相手</label>
  <select name="room[user_ids][]">　　      <%# 選択したユーザーを取得 %>
    <option value="">未選択</option>
    <% @users.each do |user| %>
      <option value=<%= user.id %>><%= user %></option>
    <% end %>
  </select>
  <%= f.submit %>
<% end %>
```

ここで、`user_ids`と複数形になっているのは、自分と相手の２人分保存するからです！
userモデルにて、`has_many`を定義したことで、`_ids`メソッドが使えるようになりました！
参考 : [Active Record の関連付け - Railsガイド](https://railsguides.jp/association_basics.html#has-many%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91%E3%81%AE%E8%A9%B3%E7%B4%B0) 

 
実際にフォームへ入力してみます。
すると下記のように、ユーザーが誰が誰だか分からないではありませんか！

<img width="300px" alt="ユーザー一覧表示_失敗" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516687/409a0e65-d37a-fdb7-b743-7187cfe53331.jpeg">

では、何が原因でこの出力が得られたのか推測します。

考えやすくするために、ユーザー選択の記述を、rubyの文法で書き直してみます。

```ruby:
@users.each do |user| 
 user 
end 
```

`@users`は、roomsコントローラーのnewアクションで定義しているインスタンス変数で、
`@users = User.all`と定義しています。
ビューの中では、each文による繰り返し処理によって、`@users`から一人ずつ取り出しています。

では、`binding.pry`を使って、出力される値を確認してみましょう。

```erb:app/views/rooms/new.html.erb
<%= form_with model: @room, local: true do |f| %>
  <%= f.label :チャットルーム名%>
  <%= f.text_field :name%>      　　　　  <%# 入力したチャットルーム名を取得 %>
  <label>チャットしたい相手</label>
  <select name="room[user_ids][]">　　    <%# 選択したユーザーを取得 %>
    <option value="">未選択</option>
    <% @users.each do |user| %>
      <% binding.pry %>                  <%# 👈each文の中にbinding.pryを設置！！ %>
      <option value=<%= user.id %>><%= user %></option>
    <% end %>
  </select>
  <%= f.submit %>
<% end %>
```
　
ブラウザをリロードすると、ターミナルに以下のような出力が表示されます。

```:ターミナル
     3:   <%= f.text_field :name%>
     4:   <p><label>チャットしたい相手</label></p>
     5:   <select name="room[user_ids][]">
     6:     <option value="">未選択</option>
     7:     <% @users.each do |user| %>
 =>  8:     <% binding.pry %>
     9:       <option value=<%=user.id%>><%= user %></option>
    10:     <% end %>
    11:   </select>
    13:   <p><%= f.submit%></p>

[1] pry(#<#<Class:xxxx>>)> 
```
`=>`で、ビューの８行目で処理を止めてるよ！とターミナルが教えてくれています。
　
７〜10行間は、each文で繰り返し処理していることから、
繰り返し処理の`1回目`で、一時的に処理を止めてくれています。
 
したがって、`user`には`一人目のデータ`が格納されていると考えられます。
では、実際に`user`の値を確認してみましょう。


```:ターミナル
[1] pry(#<#<Class:xxxx>>)> user
=> #<User id: 1, nickname: "user_1", email: "test@1">
[2] pry(#<#<Class:xxxx>>)> user.nickname
=> "user_1"
```

`[1]pry>` の後に、`式`を入力することで、
`=>`後に、`値`を出力してくれます。

`user`には、`idが1であるユーザーのレコード`が格納されていることが確認できました。
　
今回はユーザー名を一覧表示させたいので、`nickname`の値だけを取り出すことにします。
2回目のpryで、`user.nickname`と記述すると、ユーザー名を取り出せると確認できました。
したがって、ビューファイルを下記のように書き換えます。

```erb:app/views/rooms/new.html.erb
<%= form_with model: @room, local: true do |f| %>
  <%= f.label :チャットルーム名%>
  <%= f.text_field :name%>      　　　　  <%# 入力したチャットルーム名を取得 %>
  <label>チャットしたい相手</label>
  <select name="room[user_ids][]">　　    <%# 選択したユーザーを取得 %>
    <option value="">未選択</option>
    <% @users.each do |user| %>
      <option value=<%= user.id %>><%= user.nickname %></option> <%# 👈user.nicknameを表示するように変更！！ %>
    <% end %>
  </select>
  <%= f.submit %>
<% end %>
```
<img width="205px" alt="ユーザー一覧表示_成功" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516687/32c6c3c9-9275-5061-128c-6a7d51783bf8.jpeg">

無事にユーザー名を一覧表示することができました!
　
しかし、この一覧表示には、一つだけ問題があります。
それは、`自分自身も表示されている`ことです。
　
このままでは、自分しかいない孤独なチャットルームが作成されてしまいます...。
このような事態を防ぐために、`自分以外のユーザーを一覧表示`するようにします。
 
どんな式が必要か、`binding.pry`を活用して探していきます。

```:ターミナル
     3:   <%= f.text_field :name%>
     4:   <p><label>チャットしたい相手</label></p>
     5:   <select name="room[user_ids][]">
     6:     <option value="">未選択</option>
     7:     <% @users.each do |user| %>
 =>  8:     <% binding.pry %>
     9:       <option value=<%= user.id %>><%= user.nickname %></option>
    10:     <% end %>
    11:   </select>
    12:   <p><%= f.submit%></p>

[1] pry(#<#<Class:xxxx>>)>
```


まず初めに、`@users`の中身を確認しましょう。


```:ターミナル
[1] pry(#<#<Class:xxxx>>)> @users
=> [#<User id: 1, nickname: "user_1", email: "test@1">,
 #<User id: 2, nickname: "user_2", email: "test@2">,
 #<User id: 3, nickname: "user_3", email: "test@3">,
 #<User id: 4, nickname: "user_4", email: "test@4">,
 #<User id: 5, nickname: "user_5", email: "test@5">] # 👈現在のユーザー(current_user)
```
 
`@users`には、`全ユーザーのデータ`が、一人ずつ配列で格納されていることが確認できます。
ではそもそも、インスタンス変数`@users`とは何と定義していたかというと、
`User.all`と等しいよ！と定義していましたね。
　
では、`User.all`の内容を確認しましょう。

```:ターミナル
[2] pry(#<#<Class:xxxx>>)> User.all
=> [#<User id: 1, nickname: "user_1", email: "test@1">,
 #<User id: 2, nickname: "user_2", email: "test@2">,
 #<User id: 3, nickname: "user_3", email: "test@3">,
 #<User id: 4, nickname: "user_4", email: "test@4">,
 #<User id: 5, nickname: "user_5", email: "test@5">]
```
 
`@users`と全く同じデータが出力されることが確認できました。


続いて、`現在のユーザーのデータ`を取り出せないか試してみます。


```:ターミナル
[3] pry(#<#<Class:xxxx>>)> User.all.where(id: current_user)
=> [#<User id: 5, nickname: "user_5", email: "test@5">]
```

`.where`はActive Recordのメソッドの一つで、条件に該当するレコードを配列に格納して出力してくれます。
超便利なのでどんどん使っていきましょう。
参考 : [Active Record の基礎 - Railsガイド](https://railsguides.jp/active_record_basics.html)


さて、現在のユーザーのデータを取り出すことができました。
 
ということは、現在のユーザー以外のデータも取り出せるのでは？と思いつきます。
`.where.not`メソッドを使うと良さそうです。
`.where.not`は、条件に該当しないレコードを配列に格納して出力してくれる、`.where`と対をなすメソッドです。


```:ターミナル
[4] pry(#<#<Class:xxxx>>)> User.all.where.not(id: current_user)
=> [#<User id: 1, nickname: "user_1", email: "test@1">,
 #<User id: 2, nickname: "user_2", email: "test@2">,
 #<User id: 3, nickname: "user_3", email: "test@3">,
 #<User id: 4, nickname: "user_4", email: "test@4">]
```

現在のユーザーは`user_5`なので、`現在のユーザー以外のデータ`が出力されています。
　
これで、現在のユーザー以外を一覧表示させる式を見つけることができました！
したがって、`@users`の定義を変更しましょう。

```app/controllers/rooms_controller.rb
class RoomsController < ApplicationController
  def new
    @room = Room.new #newメソッドでインスタンスを作成
    @users = User.all.where.not(id: current_user) #現在のユーザー以外のレコードを取得
  end
end
```


<img width="210px" alt="ユーザー一覧表示_自分以外_成功" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516687/81f95d1c-3510-825a-d6bb-457d6e5ab2a5.jpeg">
これで自分だけのチャットルームを作らないよう設定できました。
　
めでたしめでたし...と言いたいところですが、
roomを保存できるか確認してみましょう。


⑤roomsコントローラーにcreateアクションを書く
---

roomsコントローラーにcreateアクションを定義します。

```app/controllers/rooms_controller.rb
class RoomsController < ApplicationController
  def new
    @room = Room.new
    @users = User.all.where.not(id: current_user)
  end

  def create      #createアクションを定義
    binding.pry
  end
end
```

フォームで入力した情報(リクエスト)を確認したいので、
この段階では、createアクションには何も処理は定義せず、
binding.pryを設置しておきます。
　
こうすることで、
「フォームで入力された情報が届いたよー！」
と、ルーティングを介して、roomsコントローラーのcreateアクションを実行する瞬間に、
処理を止めることができます。
　
では、フォームにルーム名`room1`、チャットしたい相手`user_2`と選択して送信します。
すると下記のようにターミナル上で、createアクション内で定義した`binding.pry`で処理を止めてるよと教えてくれます。

```:ターミナル
    7: def create
 => 8:   binding.pry
    9: end

[1] pry(#<RoomsController>)> 
```
　
では、リクエストのパラメータを確認してみましょう。

```:ターミナル
[1] pry(#<RoomsController>)> params
=> <ActionController::Parameters {"authenticity_token"=>"xxxxxxx==", "room"=>{"name"=>"room1", "user_ids"=>["2"]}, "commit"=>"Create Room", "controller"=>"rooms", "action"=>"create"} permitted: false>
```

`params`(パラムス)はパラメーターズの略です。
`authenticity_token`は、セキュリティのために生成されるトークンなので、今回は無視します。
　
`room`の中に、form_withで入力したパラメータが、配列としてハッシュで管理されています。
この`room`とは、form_withで用意した、`model: @room`と対応しています。

では、paramsの中の、`room`の情報だけ見てみます。


```:ターミナル
[2] pry(#<RoomsController>)> params[:room]
=> <ActionController::Parameters {"name"=>"room1", "user_ids"=>["2"]} permitted: false>
```

`params[:xxxx]`とすることで、見たいパラメータ`xxxx`だけを確認できます。
上記の結果から、`チャットルームroom1`に`user_idが2`のユーザーが入ったことが確認できます。
ちゃんとルームに人を呼べていることが確認できましたね。
　
めでたしめでたし.....
　
って、自分自身がルームに入ってないじゃん！！！
　
ここから、自分もルームに入れるよう、ビューを書き換えきます！

```erb:app/views/rooms/new.html.erb
<%= form_with model: @room, local: true do |f| %>
  <%= f.label :チャットルーム名%>
  <%= f.text_field :name%>      　　　　  <%# 入力したチャットルーム名を取得 %>
  <label>チャットしたい相手</label>
<select name="room[user_ids][]">　　      <%# 選択したユーザーを取得 %>
    <option value="">未選択</option>
    <% @users.each do |user| %>
      <option value=<%= user.id %>><%= user.nickname %></option>
    <% end %>
  </select>
  <input name="room[user_ids][]" type="hidden" value=<%=current_user.id%>>  <%# 👈現在のユーザーもroomに追加するように変更！！ %>
  <%= f.submit %>
<% end %>
```

`input`は、formにおけるテキストフィールドの種類を指定します。
`hidden`属性を指定することで、ブラウザには表示せずにパラメータとしてデータを受け渡すことができます。
この記述では、`user_ids`に`current_user(現在のユーザー)`も含まれるよう記述しています！
　
ではもう一度、フォームにルーム名`room1`、チャットしたい相手`user_2`と選択して送信します。

```:ターミナル
[1] pry(#<RoomsController>)> params
=> <ActionController::Parameters {"authenticity_token"=>"xxxxxxx==", "room"=>{"name"=>"room1", "user_ids"=>["2", "5"]}, "commit"=>"Create Room", "controller"=>"rooms", "action"=>"create"} permitted: false>

[2] pry(#<RoomsController>)> params[:room]
=> <ActionController::Parameters {"name"=>"room1", "user_ids"=>["2", "5"]} permitted: false>
```

無事に`現在のユーザー(user_5)`が`user_ids`に含まれていることが確認できました！
　
続いて、createアクションを定義し直して、テーブルにデータが保存できるようにしましょう。

```app/controllers/rooms_controller.rb
class RoomsController < ApplicationController
  #省略
  def create
    @room = Room.new(room_strong_params)
    if @room.save
      redirect_to root_path
    else
      render :new
    end
  end

  private

  def room_strong_params
    params.require(:room).permit(:name, user_ids: [])
  end
end
```

ストロングパラメータは、`roomモデル`の、`name`と`user_ids`のパラメータだけ許可するとしています。
　
createアクションの内部で、どのようにパラメータの受け渡しがされているか、`binding.pry`を使って確認しましょう。


```app/controllers/rooms_controller.rb
  #省略
  def create
    @room = Room.new(room_strong_params)
    binding.pry      # 👈binding.pryを設置！！
    if @room.save
      binding.pry    # 👈binding.pryを設置！！
      redirect_to root_path
    else
      render :new
    end
  end
```

インスタンス変数`@room`が、保存される前、後でパラメータをそれぞれ確認してみます。
　
では、フォームにルーム名`room1`、チャットしたい相手`user_2`と選択して送信します。



```:ターミナル
     7: def create
     8:   @room = Room.new(room_strong_params)
 =>  9:   binding.pry
    10:   if @room.save
    11:     binding.pry
    12:     redirect_to root_path
    13:   else
    14:     render :new
    15:   end
    16: end

[1] pry(#<RoomsController>)>
```

`@room`保存前の各パラメータを確認します。


```:ターミナル
[1] pry(#<RoomsController>)> params[:room]
=> <ActionController::Parameters {"name"=>"room1", "user_ids"=>["2", "5"]} permitted: false>

[2] pry(#<RoomsController>)> @room
=> #<Room:xxxx id: nil, name: "room1", created_at: nil, updated_at: nil>

[3] pry(#<RoomsController>)> room_strong_params
=> <ActionController::Parameters {"name"=>"room1", "user_ids"=>["2", "5"]} permitted: true>
```

2回目のpryに注目してください。
`@room`は、`id: nil`であることから、この時点では、レコードは作成されていないと分かります。
　
3回目のpryでは、ストロングパラメータを確認していますが、`name`と`user_ids`に値が正しく格納されていることが確認できます。
　
リクエストしたパラメータが、コントローラーのcreateアクションに正しく受け渡されているのに、まだレコードが作成されていない理由は、
`.new`メソッドでインスタンスを作成する場合、
`.save`メソッドを実行して初めてデータベースにレコードとしてコミットされるからです。
 
では、`@room.save`後を確認してみます。

```:ターミナル
     7: def create
     8:   @room = Room.new(room_strong_params)
     9:   binding.pry
    10:   if @room.save
 => 11:     binding.pry
    12:     redirect_to root_path
    13:   else
    14:     render :new
    15:   end
    16: end

[1] pry(#<RoomsController>)> params[:room]
=> <ActionController::Parameters {"name"=>"room1", "user_ids"=>["2", "5"]} permitted: false>

[2] pry(#<RoomsController>)> @room
=> #<Room:xxxx id: 1, name: "room1">

[3] pry(#<RoomsController>)> room_strong_params
=> <ActionController::Parameters {"name"=>"room1", "user_ids"=>["2", "5"]} permitted: true>
```
 
2回目のpryに注目してください。
`@room`は、`id: 1`であることから、レコードは正常に保存されました！


今回は無事に保存できたのですが、
レコードを保存できなかった時に使える便利なメソッドも、合わせて紹介します！

```:ターミナル
[4] pry(#<RoomsController>)> @room.valid?
=> true

[5] pry(#<RoomsController>)> @room.errors
=> #<ActiveModel::Errors:xxxx @base=#<Room:xxxx id: 1, name: "room1">, @details={}, @messages={}>
```

[4] pryの`@room.valid?`では、「`@room`のバリデーションはOK？」みたいな感じで、
バリデーションを実行してエラーがあるかを判別します。
エラーが無ければ`true`を,
エラーが有れば`false`を返します。

[5] pryの`@room.errors`では、@room.valid?でfalseが返された時に、エラーメッセージを出力してくれます。
今回はエラーはないので、エラーメッセージは出力されていません。
エラーがある時は`messages{}`の中にエラーメッセージが格納されます。


最後に、保存されたデータをコンソールで確認してみましょう。


```:ターミナル
% rails c
[1] pry(main)> Room.all
=> [#<Room:xxxx id: 1, name: "room1">]

[2] pry(main)> Entry.all
=> [#<Entry:xxxx id: 1, room_id: 1, user_id: 2>,
 #<Entry:xxxx id: 2, room_id: 1, user_id: 5>]
```

コンソールでも、Active Recordのメソッドを使うことができます。
　
`Room.all`で全てのルームを表示させると、
room1が保存されていることが確認できます。

`Entry.all`で全てのレコードを表示させると、
２つのレコードが保存されていることが確認できます。
「ルーム１にユーザー２と5がいるよーっ！」と教えてくれています。

コンソールで確認した内容は、以下の表と同じ内容です！

__roomsテーブル__

| id  | name    |
| --- | ------- |
| 1   | room1   |


   
__entriesテーブル__

| id  | room_id | user_id |
| --- | ------- | ------- |
| 1   | 1       | 2       |
| 2   | 1       | 5       |


 
最後までお付き合いいただきありがとうございました！:smile:

参考資料
---
・[【Rails】find・find_by・whereについてまとめてみた](https://qiita.com/nakayuu07/items/3d5e2f8784b6f18186f2)