【豆知識】rails console から errors メソッドや full_message メソッドの返り値を確認する手順について

# はじめに
アプリ制作課題に取り組むと同時に過去課題を参考にしているわーちゃんです。今回はユーザー登録でバリデーションエラーが発生した場合、そのエラーメッセージを画面に表示するようなファイル設定、コードの記載を行ったあと、ローカルで確認することもできますが、ターミナルで確認できるようになってこそ！！大切だとおもったので、改めて学習しなおしてみました。
前回に比べて、「じゃあこの場合はどうなるのか？」という考えもできるようになってきたのでアウトプットの為にもまとめてみます。


## 環境
- Windows, WSL
- Docker
- Ruby 3.2.3
- Rails 7.1.3


## さっそく手順
1. **`rails console` を起動** 
     `docker compose exec web rails c` で Rails コンソールを開く
    
2. **モデルのインスタンスを作成**  
    バリデーションエラーを確認するためには、モデルのインスタンスを作成し、そのインスタンスが無効な状態である必要があります。たとえば、`User` モデルがあると仮定します。
    
    ```ruby
    user = User.new
    ```
    ここでは、`User` モデルの新しいインスタンスを作成します
    
3. **エラーメッセージの確認**
    - **`errors` メソッド**
        `errors` メソッドを使用して、モデルインスタンスのエラーを確認できます。エラーオブジェクトは `ActiveModel::Errors` インスタンスです。
        
        ```ruby
         user.errors
        ```
        これで、バリデーションエラーの詳細が表示されます。通常、エラーは属性ごとにグループ化されています。
        
    - **`full_messages` メソッド**
        `full_messages` メソッドを使用して、エラーメッセージをユーザーフレンドリーな形式で取得できます。
        ```ruby
        user.errors.full_messages
        ```
        これで、全てのエラーメッセージが配列として表示されます。メッセージは属性名とエラーメッセージが組み合わさった形になります。
        
4. **具体的なコード例**
    例えば、`User` モデルが `email` 属性に対して `presence` バリデーションを持っている場合、`email` 属性が空のときにエラーメッセージを確認する方法は次の通りです。
    
    ```ruby
    # Userモデルのインスタンスを作成（属性を設定せず）
    user = User.new
    
    # バリデーションを実行
    user.valid?
    
    # エラーオブジェクトを確認
    user.errors
    # => #<ActiveModel::Errors:0x00007f9b7c39e5e0 ...>
    
    # エラーメッセージを全て確認
    user.errors.full_messages
    # => ["Email can't be blank"]
    ```

## エラーメッセージはどこに記述されているか
エラーメッセージは、Rails アプリケーションのモデル内で設定されているバリデーションに基づいています。具体的には、エラーメッセージはモデルファイル内に記述されています。以下のファイルや場所で設定されている可能性があります。

- 1: **モデルファイル内のバリデーション**
  モデルファイル (app/models/user.rb など) 内で、バリデーションとそのエラーメッセージが定義されています。以下は例になります。

  ```ruby
  class User < ApplicationRecord
    # バリデーション設定
    validates :email, presence: true
    validates :name, presence: true
    validates :password, presence: true, length: { minimum: 6 }
    validates :password_confirmation, presence: true
  end
  ```

- 2:**エラーメッセージのカスタマイズ**
  デフォルトのエラーメッセージは、バリデーションのオプションで指定できます。例えば、password に対する length バリデーションでカスタムメッセージを指定することができます。
  ```ruby
  validates :password, length: { minimum: 6, message: "is too short (minimum is %{count} characters)" }
  ```
  
- 3: **国際化 (I18n) ファイル**
エラーメッセージは、国際化 (I18n) ファイルでカスタマイズされることもあります。config/locales/ ディレクトリ内にある YAML ファイル（例えば、config/locales/en.yml）でエラーメッセージを設定できます。
 ```yaml
 en:
  errors:
    messages:
      blank: "can't be blank"
      too_short: "is too short (minimum is %{count} characters)"
  ```
  
この設定によって、エラーメッセージがカスタマイズされると、デフォルトのメッセージが上書きされます。

<br>

## 実際の例1（あらためて解説付き）

```ruby
$ docker compose exec web rails c
Loading development environment (Rails 7.1.3.4)
irb(main):001> user = User.new
=> 
#<User:0x00007fc3732abb18
...
irb(main):002> user.valid?
  User Exists? (0.8ms)  SELECT 1 AS one FROM "users" WHERE "users"."email" IS NULL LIMIT $1  [["LIMIT", 1]]
=> false
irb(main):003> user.errors.full_messages
=>
["Password is too short (minimum is 6 characters)",
 "Password confirmation can't be blank",
 "Name can't be blank",
 "Email can't be blank"]
irb(main):004* user.errors.each do |error|
irb(main):005*   puts error.full_message
irb(main):006> end
Password is too short (minimum is 6 characters)
Password confirmation can't be blank
Name can't be blank
Email can't be blank
=>
[#<ActiveModel::Error attribute=password, type=too_short, options={:if=>#<Proc:0x00007fc37525f950 /myapp/app/models/user.rb:10 (lambda)>, :count=>6}>,
 #<ActiveModel::Error attribute=password_confirmation, type=blank, options={:if=>#<Proc:0x00007fc37525a770 /myapp/app/models/user.rb:12 (lambda)>}>,
 #<ActiveModel::Error attribute=name, type=blank, options={}>,
 #<ActiveModel::Error attribute=email, type=blank, options={}>]
irb(main):007> user.errors
=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=password, type=too_short, options={:if=>#<Proc:0x00007fc37525f950 /myapp/app/models/user.rb:10 (lambda)>, :count=>6}>, #<ActiveModel::Error attribute=password_confirmation, type=blank, options={:if=>#<Proc:0x00007fc37525a770 /myapp/app/models/user.rb:12 (lambda)>}>, #<ActiveModel::Error attribute=name, type=blank, options={}>, #<ActiveModel::Error attribute=email, type=blank, options={}>]>
```

### 1. `user = User.new`

このコードは**仮のユーザーオブジェクト**（`User` モデルのインスタンス）を作成しています。

- **何のファイルに基づいているか**: この仮のユーザーオブジェクトは、`app/models/user.rb` に定義された `User` モデルに基づいて作成されています。`User.new` は `User` クラスの新しいインスタンスを作成し、まだ保存されていないユーザーオブジェクトを作ります。

### 2. `user.valid?`

このコードは、`User` モデルに定義されている**バリデーションが適用されるかどうか**を確認するためのものです。

- **動作の流れ**:
    - `app/models/user.rb` で定義されたバリデーション（例: 必須フィールドや文字数制限など）が、この仮のユーザーオブジェクトに適用されます。
    - バリデーションに失敗すると `false` を返し、成功すると `true` を返します。

### 3. `user.errors.full_messages`

このコードは、**バリデーションに失敗した場合に発生するエラーメッセージを表示**するためのものです。

- **動作の流れ**:
    - `user.valid?` を実行した後、`user.errors.full_messages` を使って、発生したすべてのエラーメッセージを配列形式で返します。
    - これにより、どのフィールドでエラーが発生し、どのような問題があるかがわかります。

### ※`user.errors.each do |error| ... end` と `user.errors` の違い

- **`user.errors.each do |error| ... end`**:
    - エラーメッセージを1つずつ取り出して、何らかの処理（例えば `puts error.full_message` でメッセージを表示）を行うためのループです。このコードを実行すると、各エラーメッセージが順番にコンソールに出力されます。
- **`user.errors`**:
    - これは、エラーの詳細を含む `ActiveModel::Errors` オブジェクトを返します。これには、どのフィールドにどのようなエラーが発生しているかが含まれています。
    - 例として、`user.errors` の返り値は以下のようになります:
        
        ```ruby
        #<ActiveModel::Errors [#<ActiveModel::Error attribute=password, type=too_short, options={:count=>6}>, #<ActiveModel::Error attribute=email, type=blank, options={}>]>
        ```
        
    - このオブジェクトには、エラーメッセージそのものだけでなく、どの属性にどのようなタイプのエラーが発生しているかという詳細も含まれています。

### 実際の例1のまとめ

- `user = User.new`: `app/models/user.rb` に基づいて仮のユーザーオブジェクトを作成。
- `user.valid?`: `user.rb` で定義されたバリデーションを確認。
- `user.errors.full_messages`: バリデーションエラーの内容を確認。
- `user.errors.each do |error| ... end`: エラーメッセージを順番に処理。
- `user.errors`: エラーの詳細情報を含むオブジェクトを確認。


## 実際の例2（例1との比較）
```ruby
irb(main):003> user = User.new(name: 'Hoge')
=> 
#<User:0x00007f0889854100
...
irb(main):004> user.save
  TRANSACTION (0.4ms)  BEGIN
  User Exists? (1.6ms)  SELECT 1 AS one FROM "users" WHERE "users"."email" IS NULL LIMIT $1  [["LIMIT", 1]]
  TRANSACTION (0.5ms)  ROLLBACK
=> false
irb(main):005> user.errors
=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=password, type=too_short, options={:if=>#<Proc:0x00007f0889a25a10 /myapp/app/models/user.rb:10 (lambda)>, :count=>6}>, #<ActiveModel::Error attribute=password_confirmation, type=blank, options={:if=>#<Proc:0x00007f0889a24458 /myapp/app/models/user.rb:12 (lambda)>}>, #<ActiveModel::Error attribute=email, type=blank, options={}>]>
irb(main):006> user.errors.full_messages
=>
["Password is too short (minimum is 6 characters)",
 "Password confirmation can't be blank",
 "Email can't be blank"]
irb(main):007* user.errors.each do |error|
irb(main):008*   puts error.full_message
irb(main):009> end
Password is too short (minimum is 6 characters)
Password confirmation can't be blank
Email can't be blank
=>
[#<ActiveModel::Error attribute=password, type=too_short, options={:if=>#<Proc:0x00007f0889a25a10 /myapp/app/models/user.rb:10 (lambda)>, :count=>6}>,
 #<ActiveModel::Error attribute=password_confirmation, type=blank, options={:if=>#<Proc:0x00007f0889a24458 /myapp/app/models/user.rb:12 (lambda)>}>,
 #<ActiveModel::Error attribute=email, type=blank, options={}>]
 ```

 ### コードの違いと説明

### 1. `user = User.new(name: 'Hoge')`

- **意味**: これは `User` クラスの新しいインスタンスを作成し、その際に `name` という属性に `'Hoge'` という値を与えています。このオブジェクトはまだデータベースには保存されていません。
- **違い**: 先ほどのコードは何も属性を指定せずにユーザーオブジェクトを作成しましたが、このコードでは `name` が設定されています。

### 2. `user.save`

- **意味**: このメソッドは、`user` オブジェクトをデータベースに保存しようとします。保存が成功すれば `true` を返し、失敗すれば `false` を返します。
- **違い**: 先ほどのコードでは `save` を使用せず、`user.valid?` でオブジェクトが有効かどうかをチェックしていました。`save` はデータベースに保存するため、バリデーションが重要になります。

### 3. `user.errors`

- **意味**: これは `user` オブジェクトに関連するバリデーションエラーを保持しているオブジェクトです。`save` が失敗したとき、どの属性でエラーが発生したかを表示します。

### 4. `user.errors.full_messages`

- **意味**: バリデーションに失敗した場合のエラーメッセージを配列形式で取得します。例えば、`["Name can't be blank", "Email can't be blank"]` のような形で表示されます。

### 5. `user.errors.each do |error|`

- **意味**: `user.errors` を使って、個々のエラーメッセージをループで処理します。このコードでは、各エラーメッセージを `puts` を使って出力しています。

### コードの違い

先ほどのコードでは `user.valid?` を使ってバリデーションを手動でチェックし、その後エラーメッセージを確認しました。一方で、こちらのコードでは `save` を使ってデータベースへの保存を試み、それに失敗した場合にエラーメッセージを確認する流れになっています。

- **`save` の意味**: `save` は、オブジェクトが有効であればデータベースに保存し、無効であれば保存せず、エラー情報を保持します。
- **順番の違い**: `save` を使うことで、データベースへの保存が試みられるため、`valid?` ではなく `save` の結果に応じてエラー情報が格納されます。

この違いにより、`save` が成功するか失敗するかによって、エラーメッセージが異なる場合があります。


# さいごに
rails consoleから返り値を確認することが無事できました。saveを使用する時と使用しないときとでは返ってくるこたえもかわり、凄いなぁとあらためて実感。
今回の記事が何か参考になれば幸いです。