RailsのActive Modelで使える基本的なバリデーションまとめ

# 

## はじめに
Railsで定義したモデルクラスには、Active Modelが提供する検証クラスによって様々なバリデーションを宣言することができます。
ユーザーからの入力をシンプルなコードで検証できる大変便利な機能なのですが、使用する度に忘れてしまうので、基本的なものだけでも覚えるために記事に起こしておきます。

注意：以下、Rails 5系をベースにした内容になります

## 検証対象の作成
何はともあれ検証対象となるモデルが必要になるので、`Rails new`からの`scaffold`でUserモデルをサクッと作成します。

```bash
$ rails new validation_test
$ rails g scaffold User name:string age:integer group:string email:string
```

## バリデーションの宣言
バリデーションは、モデルクラスに`validates`メソッドを記述することで定義できます。
まずは、先ほど作成したUserモデルのnameカラムに存在性のバリデーションを定義してみます。

```ruby
class User < ApplicationRecord
  validates :name, presence: true
end
```

validatesメソッドは、第一引数に検証対象のフィールド名、第二引数に検証ルール名とそのパラメーターのハッシュをとります。(presenceルールにはパラメーターがないため、単にtrueとのみ記載する)

この状態でUserモデルの新規作成・更新などを行おうとした時、いずれかのフィールドが空だとエラーが発生します。

<img width="534" alt="スクリーンショット 2019-09-06 0.22.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408323/3c03c5ef-2de6-a9ff-9c6f-3a2460e7bb91.png">

デフォルトでは、このようにエラーメッセージの表示ととフォームの強調表示をしてくれます。

検証するフィールドは、以下のように一度に複数個指定することも可能です。

```ruby
class User < ApplicationRecord
  validates :name, :email, :age, :group,  presence: true
end
```

また、後述するlength検証などと合わせて、一度に複数の検証を宣言する場合は以下のように記述します。

```ruby
class User < ApplicationRecord
  validates :name, presence: true, length: { is: 5 }, uniqueness: true
end
```

## バリデーションが機能するタイミング
バリデーションはレコードの新規作成・更新の際に機能すると書きましたが、具体的には以下のメソッドを実行しようとした時点で検証が行われます。

- create, create!
- save, save!
- update, update!

これら以外の、例えば`increment!`や`toggle!`、`update_attribute`などのメソッドを使用した際には、定義したバリデーションが機能せずにそのままDBに書き込まれてしまいます。
そのため、検証が不要なフィールドや事前に何らかの検証が行われているといった場合以外では、可能な限り上記のメソッドを使った方が安全です。

また、モデルオブジェクトに対して`valid?`メソッドを用いると、DBへの保存とは関係なくオブジェクトがvalidな状態かどうかを真偽値で返してくれます。

## 様々な検証機能

先に挙げた`presence`検証以外にも、Railsには多くの検証機能が存在します。

### 非存在性の検証
`absence`検証を用いると、値が空であるかどうかの認証ができます。特定の条件下では入力されたくないフィールドがある場合に使えますね。

```ruby
# groupフィールドへの入力を禁止する
validates :group, absence: true
```

### 文字列長の検証(length)
length検証を使用すると、入力された文字列の長さのチェックを行ってくれます。
指定できるパラメーターには、最小の長さを定める`minimum`、逆に最大の長さを定める`maximum`などがあります。

```ruby
# 最長文字数を10文字に設定する
validates :name, length: { maximum: 10 }

# 最小文字数を10文字に設定する
validates :name, length: { minimum: 3 }

# 最小・最大文字数を同時に検証する
validates :name, length: { maximum: 10, minimum: 3 }

# ぴったり5文字の文字列のみ許可する
validates :name, length: { is: 5 }
```

### 数値の検証(numericalcality)
`numericalcality`検証を宣言すると、数値入力が期待されるフィールドに対して数値チェックや大小チェックなどが行えます。

```ruby
# 入力値が数値かどうかを検証する
validates :age, numericality: true

# 入力値が整数かどうかを検証する
validates :age, numericality: { only_integer: true }

# 指定した値よりも大きいかどうか
validates :age, numericality: { greater_than: 5 }

# 指定した値以上かどうか
validates :age, numericality: { greater_than_or_equal_to: 5 }

# 指定した値よりも小さいかどうか
validates :age, numericality: { less_than: 100 }

# 指定した値以下かどうか
validates :age, numericality: { less_than_or_equal_to: 100 }


# 指定した値と同値かどうか
validates :age, numericality: { equal_to: 20 }

# 奇数かどうか
validates :age, numericality: { odd: true }

# 偶数かどうか
validates :age, numericality: { even: true }
```

### 範囲内 or 範囲外の検証(inclusion/exclusion)

`inclusion`検証を使用すると、値が指定のリストの中に含まれるか、もしくは指定の範囲に含まれるかどうかを検証できます。
対して`exclusion`検証を使用すると、値が指定のリストの中に含まれて**いない**か、もしくは指定の範囲に含まれて**いない**かどうかを検証できます。
ホワイトリスト、ブラックリストの定義に使えますね。

```ruby
# 入力値がA, B, Cのいずれかの文字列であるかどうか
validates :group, inclusion: { in: %w[A B C] }

# 同じ内容を範囲オブジェクトで指定することもできる
validates :group, inclusion: { in: ('A'..'C') }

# exclusionはinclusionの逆
validates :group, exclusion: { in: %w[A B C] }
validates :group, exclusion: { in: ('A'..'C') }
```

### 正規表現による検証(format)
`format`検証を用いると、入力された文字列が指定した正規表現にマッチするかどうかの検証を行えます。正規表現はwithパラメーターの値として指定します。
emailなど、一定のパターンに当てはまる値を期待する場合に指定することになるかと思います。

```ruby
# 入力値がA, B, Cのいずれかの文字列であるかどうか(inclusionの例と同じ)
validates :group, format: { with: /\A[ABC]\Z/ }

# emailの形式かどうかチェックする
email_regex = /\A[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*\Z/
validates :email, format: { with: email_regex }
```

### ユニークネス検証(uniqueness)
`uniqueness`検証を用いると、入力値がそのフィールドにおいて一意なものかどうかの検証を行ってくれます。重複を許さないデータ、例えばメールアドレスなどの検証に使えるかと思います。
オプションとして、パラメーターの`case_sensitive`を使用すると、大文字・小文字の区別を行うかどうかを設定できます。（デフォルトでは有効(true)。）

また、2つ以上のフィールドの組み合わせにおいて一意になるようにしたい！という場合は、パラメーターに`scope`を使用することで実現できます。

```ruby
# メールアドレスの一意性を検証する
validates :email, uniqueness: true

# 大文字・小文字の区別を行わない
validates :email, uniqueness: { case_sensitive: false }

# グループごとにメールアドレスの一意性を検証する
validates :email, uniqueness: { scope: :group }
```



## 参考
- [HTML リビングスタンダード](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address)(emailの正規表現)
- [Active Record バリデーション - Rails ガイド](https://railsguides.jp/active_record_validations.html)