【Rails】Docker環境でのpryの使い方

## pryとは

pryはirbのようRubyの対話的なデバッグツールで、変数の中身を確認したり、コードの挙動を動的に変更することができる。

## pryのインストール

Gemfileに関連するgemを追記し、`bundle install`を実行する。

```ruby:Gemfile
group :development, :test do
  # gem 'pry'   # 'pry-rails'もしくは'pry-byebug'をインストールするなら'pry'は不要
  gem 'pry-rails'
  gem 'pry-byebug'
  gem 'pry-doc'
end
```

```bash
bundle install
```

一旦コンテナを再起動する。

## pry関連のgemの種類
### pry
Rubyの対話型デバッグツール。
ターミナルで `pry` を実行するとpryのプロンプトを開くことができ、Rubyファイルのコード内に`binding.pry`を挿入すれば、実行中のコードを特定のポイントで一時停止してデバッグすることができる。

※`pry-rails`と`pry-byebug`とは`pry`に依存しており、どちらかをインストールすると`pry`もインストールされる為、`pry-rails`と`pry-byebug`をインストールする場合は`pry`のインストールは不要。

### pry-rails
`pry-rails`を使用すると、`rails console`で立ち上がるRailsコンソールがirbではなくpryに置き換えられ、Railsアプリケーション内でpryの機能を利用することができる。

### pry-byebug
`binding.pry`をコードに挿入し、ステップ実行、ブレークポイントの設定、コードの実行を再開するなど、より高度なデバッグが可能になる。

### pry-doc
Rubyのコアドキュメント（標準ライブラリや組み込みクラス/モジュールのドキュメント）やC拡張（Rubyの機能を拡張するためにC言語で書かれたコードのこと）のソースコードを、pry内で表示できるようにする。


## pry-rails, pry-docの使い方

`rails console`を実行するとpryが起動する。

```pry
$ rails console
Loading development environment (Rails 7.0.4.3)
[1] pry(main)> 
```

- railsのコードを実行する。
通常のirbと同様に、Railsのコードを使うことができる。

```pry
[1] pry(main)> User.create(name: 'test', email: 'test@test.com')
  TRANSACTION (0.6ms)  BEGIN
  User Create (12.4ms)  INSERT INTO `users` (`name`, `email`, `created_at`, `updated_at`, `discarded_at`) VALUES ('test', 'test@test.com', '2023-04-17 11:45:56.803781', '2023-04-17 11:45:56.803781', NULL)
  TRANSACTION (15.9ms)  COMMIT
=> #<User:0x00007f3d3d917c20
 id: 1,
 name: "test",
 email: "test@test.com",
 created_at: Mon, 17 Apr 2023 20:45:56.803781000 JST +09:00,
 updated_at: Mon, 17 Apr 2023 20:45:56.803781000 JST +09:00,
 discarded_at: nil>

[2] pry(main)> User.all
=>   User Load (15.8ms)  SELECT `users`.* FROM `users` WHERE `users`.`discarded_at` IS NULL
[#<User:0x00007f3d3d880cd0
  id: 1,
  name: "test",
  email: "test@test.com",
  created_at: Mon, 17 Apr 2023 20:45:56.803781000 JST +09:00,
  updated_at: Mon, 17 Apr 2023 20:45:56.803781000 JST +09:00,
  discarded_at: nil>]
```

- `show-source`コマンドでソースコードを確認する。
カスタムクラスやメソッド、Rails内部のクラスやメソッド、Gemやライブラリのソースコードも確認することができる。

```pry
[3] pry(main)> show-source ApplicationRecord::create

From: /usr/local/bundle/gems/activerecord-7.0.4.3/lib/active_record/persistence.rb:11:
Owner: ActiveRecord::Persistence::ClassMethods
Visibility: public
Signature: create(attributes=?, &block)
Number of lines: 9

def create(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create(attr, &block) }
  else
    object = new(attributes, &block)
    object.save
    object
  end
end
```



- pry-docでドキュメントを表示する。
`show-source`コマンドに`-d`オプションをつける。

```pry
[4] pry(main)> show-source -d ApplicationRecord::create

From: /usr/local/bundle/gems/activerecord-7.0.4.3/lib/active_record/persistence.rb:11:
Owner: ActiveRecord::Persistence::ClassMethods
Visibility: public
Signature: create(attributes=?, &block)
Number of lines: 32

Creates an object (or multiple objects) and saves it to the database, if validations pass.
The resulting object is returned whether the object was saved successfully to the database or not.

The attributes parameter can be either a Hash or an Array of Hashes. These Hashes describe the
attributes on the objects that are to be created.

==== Examples
  # Create a single new object
  User.create(first_name: 'Jamie')

  # Create an Array of new objects
  User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])

  # Create a single object and pass it into a block to set other attributes.
  User.create(first_name: 'Jamie') do |u|
    u.is_admin = false
  end

  # Creating an Array of new objects using a block, where the block is executed for each object:
  User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
    u.is_admin = false
  end

def create(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create(attr, &block) }
  else
    object = new(attributes, &block)
    object.save
    object
  end
end
```


## pry-byebugの使い方
Dockerを使っている場合、下記コマンドを実行してRubyのコンテナにアタッチする。

```
$ docker-compose up -d && docker attach [コンテナ名]
```

```:実行例
$ docker compose up -d && docker attach web
[+] Running 4/4
 ⠿ Container chrome  Running                                                                                                                                                       0.0s
 ⠿ Container test    Running                                                                                                                                                       0.0s
 ⠿ Container db      Running                                                                                                                                                       0.0s
 ⠿ Container web     Started                                                                                                                                                       1.4s


=> Booting Puma
=> Rails 7.0.4.3 application starting in development 
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.6.5 (ruby 3.1.3-p185) ("Birdie's Version")
*  Min threads: 5
*  Max threads: 5
*  Environment: development
*          PID: 1
* Listening on http://0.0.0.0:3001
Use Ctrl-C to stop
```

- ソースコードにブレークポイントを設置する。

```ruby:UsersController
 # GET /users
  def index
    binding.pry    # ブレークポイントを設置
    @users = User.all
  end
```

- ブレークポイントを設置した上で、該当の処理を通るリクエストを行うと、`docker attach web`を実行したターミナルでデバッグを行うことができる。


```pry
From: /myapp/app/controllers/users_controller.rb:7 UsersController#index:

    5: def index
    6:   binding.pry
 => 7:   @foo = 1
    8:   @users = User.all
    9: end

[1] pry(#<UsersController>)> @foo  # この時点ではnil
=> nil
[2] pry(#<UsersController>)> @users  # この時点ではnil
=> nil
[3] pry(#<UsersController>)> next  # ステップ実行を進める

From: /myapp/app/controllers/users_controller.rb:8 UsersController#index:

    5: def index
    6:   binding.pry
    7:   @foo = 1
 => 8:   @users = User.all
    9: end

[3] pry(#<UsersController>)> @foo  # ステップ実行して@fooが定義された
=> 1
[4] pry(#<UsersController>)> @users  # @usersはまだnil
=> nil
[5] pry(#<User::ActiveRecord_Relation>)> exit
```

`next`と入力すると、ステップ実行を進めることができる。
`exit`と入力すると、ステップ実行を終了してプログラムの実行が再開される。