バリデーションは大事だって話なんだよねぇ！

# はじめに
バリデーションは常にアプリ開発で必要不可欠だと思いますが、「こんなバリデーションを実装したいな」と考えても、意外とどれを使えばいいかな？とか、記憶が曖昧だから調べようとなることが多いと思うので、この記事でほとんど解決出来るくらいの情報量を記したいと思います！！
バリデーションは奥が深いので実にたくさんのことができます。
正しく理解することで、強固なセキュリティかつ隙のないアプリケーションの実装が可能です！

## バリデーションヘルパー
### 空でないか

```ruby

validates :title, presence: true

validates_presence_of :title

has_many :line_items, inverse_of: :order # 関連付けられたレコードの存在が必須であること
```

### 空であるか

```ruby

validates :title, absence: true

validates_acceptance_of :title

has_many :line_items, inverse_of: :order # 関連付けられたレコードの存在が空であること
```

### 文字数制限

```ruby

validates :name, length: { maximum: 50 }

validates :name, length: { minimum: 2 }　#文字数の下限を設定

validates :name, length: { in: 2..50 }　#長さの範囲を設定

validates :name, length: { is: 5 } # 長さを指定

validates_length_of :name, maximum: 30
```

### チェックボックスがオンになっているか

```ruby

validates :title, acceptance: true

validates_acceptance_of :title
```

### 両方のモデルに対してバリデーションを実行する

```ruby

class Library < ApplicationRecord
  has_many :books
  validates_associated :books
end
```

### 2つのテキストフィールドで受け取る内容が完全に一致するか
password_confirmationがnilでない場合のみ行われので、確認を必須にするには、確認用の属性について存在チェックも追加する。

```ruby

validates :password, confirmation: true
validates :password, confirmation: { case_sensitive: false } # 大文字小文字の違いを確認しない
validates :password_confirmation, presence: true

validates_confirmation_of :password
```

```index.html.erb
・・・
<%= text_field :person, :password %>
<%= text_field :person, :password_confirmation %>
・・・
```

### 与えられた集合に属性の値が含まれていないか

```ruby

validates :domain, exclusion: { in: %w(www us ca jp) }

validates_exclusion_of :domain, in: %w(www us ca jp)
```

### 与えられた集合に属性の値が含まれているか

```ruby

validates :domain, inclusion: { in: %w(www us ca jp) }

validates_inclusion_of :domain, in: %w(www us ca jp)
```

### 与えられた正規表現と属性の値がマッチするか

```ruby

EMAIL_FORMAT = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/
validates :email, format: { with: EMAIL_FORMAT }

validates_format_of :email, with: EMAIL_FORMAT
```

### 属性に数値のみが使われているか

```ruby

# 指定された値よりも大きくなければならないこと
validates :age, numericality: { only_integer: true,
                                greater_than: 10 }

# 指定された値と等しいか、それよりも大きくなければならないこと
validates :age, numericality: { only_integer: true,
                                greater_than_or_equal_to: 10 }

# 指定された値と等しくなければならないこと
validates :age, numericality: { only_integer: true,
                                equal_to: 10 }

# 指定された値よりも小さくなければならないこと
validates :age, numericality: { only_integer: true,
                                less_than: 10 }

# 指定された値と等しいか、それよりも小さくなければならないこと
validates :age, numericality: { only_integer: true,
                                less_than_or_equal_to: 10 }

# 渡した値以外の値でなければならないこと
validates :age, numericality: { only_integer: true,
                                other_than: 10 }

# 奇数であること
validates :age, numericality: { only_integer: true,
                                odd: true }

# 偶数であること
validates :age, numericality: { only_integer: true,
                                even: true }

validates_numericality_of :age
```

### 属性の値が一意であり重複していないか

```ruby

validates :email, uniqueness: true

validates :name, uniqueness: { case_sensitive: false } # 一意性制約で大文字小文字を区別しない

validates_uniqueness_of :name
```

## 共通のバリデーションオプション

### 対象の値がnilの場合にバリデーションをスキップ

```ruby

validates :size, inclusion: { in: %w(small medium large) }, allow_nil: true
```

### 属性の値がblank?に該当する場合（nilや空文字など）にバリデーションをスキップ

```ruby

validates :title, length: { is: 5 }, allow_blank: true
```

### バリデーション失敗時にerrorsコレクションに追加されるカスタムエラーメッセージを指定

```ruby

validates :name, presence: { message: "must be given please" }

# 動的な属性値を含むメッセージ
validates :age, numericality: { message: "%{value} seems wrong" }
```

### バリデーション実行のタイミングを指定
※デフォルトでは、保存時にバリデーションが実行される

```ruby

validates :email, uniqueness: true, on: :create

# カスタムテキストを渡す
validates :email, uniqueness: true, on: :account_setup
```

```test_controller.rb

・・・
person.valid?(:account_setup)
・・・
```

## 条件付きバリデーション
### 特定の条件を満たす場合にのみバリデーションを実行

```ruby

validates :card_number, presence: true, if: :paid_with_card?

# ワンライナーで短く
validates :password, confirmation: true, unless: -> { password.blank? }
```

### 条件付きバリデーションをグループ化

```ruby

with_options if: :is_admin? do |admin|
  admin.validates :password, length: { minimum: 10 }
  admin.validates :email, presence: true
end
```

## カスタムバリデーション
### カスタムバリデータ
ActiveModel::Validatorを継承するクラス。
これらのクラスでは、validateメソッドを実装する必要があります。
このメソッドはレコードを1つ引数に取り、それに対してバリデーションを実行します。カスタムバリデータはvalidates_withメソッドを使って呼び出します。

```ruby

# 個別の属性を検証する
class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
      record.errors[attribute] << (options[:message] || "はメールアドレスではありません")
    end
  end
end

class Person < ApplicationRecord
  validates :email, presence: true, email: true
end
```

### モデルの状態を確認し、無効な場合にerrorsコレクションにメッセージを追加する

```ruby

validate :expiration_date_cannot_be_in_the_past

def expiration_date_cannot_be_in_the_past
  if expiration_date.present? && expiration_date < Date.today
    errors.add(:expiration_date, ": 過去の日付は使えません")
  end
end
```

# 終わりに
こう見ると、たくさんありますね笑
この記事をストックして、バリデーション王になりましょう！

## 参考
[Active Record バリデーション]
(https://railsguides.jp/active_record_validations.html)

[Railsドキュメント]
(https://railsdoc.com/validation)