【Rails】オプションを使ってスマートにバリデーションを記述する！

## はじめに
私は普段Ruby on Railsで開発をしているのですが、その中でRailsのActive Recordの持つバリデーション機能をよく使います。
先日改めて公式ドキュメントを読んだので、以前の自分に対する自戒を込めて、オプションについて紹介したいと思います。


## 知らないともったいないオプション
以前の私は、安易にvalidateメソッドを使用している事がありました。とてもシンプルなバリデーション（存在チェックするだけ、文字数チェックするだけ、数値チェックするだけなど）以外は全部validateで書けばいいじゃん！と思っていました。しかし、validatesメソッドを用いることで、無駄に独自バリデーションメソッドを書かずに済むし、読む方も分かりやすいし、いいこと尽くしです。そして何より、Railsを使ってるのに用意されたオプションを使いこなしてないのは、とてもダサい...。ということで、以前の自分ならvalidateメソッドで書いてたかもしれないバリデーションをちゃんとvalidatesで書く場合に使えるオプションを紹介します。


- `:if`オプション
このオプションでは、ブーリアンを返却する条件を指定することで、特定の条件時にバリデーションを発動させることが出来ます。
バリデーションの実行直前に呼び出されるメソッド名を、:ifや:unlessオプションにシンボルで指定したり、Procやlambdaを用いてその場で条件を書いたり、引数が必要なメソッドを呼び出す事ができます。
例えば、支払いフォームにおいて、カード払いを選択されている場合のみカード番号のバリデーションを実行するという場合、オプションを知らなければこんな感じで書いてしまうかもしれません。validateの行を読んだだけでは具体的にどんなバリデーションなのかわかりませんし、メソッド内も条件分岐が複数出てきて、シンプルな仕様に対してコード数がかかっている気がします。。

```ruby:オプションを知らない場合
class Order < ApplicationRecord
  validate :validate_card_number

  def validate_card_number
    return unless payment_type == "card"

    errors.add(:card_number, '入力してください') if card_number.blank?
  end
end
```
しかし、オプションを知っていれば、以下のように書くことが出来ます。validatesの一行を見るだけで、何に対するどんなバリデーションがどんな条件のときに実行されるのか一瞬で理解できます。


```ruby:オプションを知っている場合
class Order < ApplicationRecord
  validates :card_number, presence: true, if: :paid_with_card?

  def paid_with_card?
    payment_type == "card"
  end
end
```
[引用] [Railsガイド](https://railsguides.jp/active_record_validations.html#if%E3%82%84-unless%E3%81%A7%E3%82%B7%E3%83%B3%E3%83%9C%E3%83%AB%E3%82%92%E4%BD%BF%E3%81%86)

　さらに、`with_options`を用いることで以下のように、
　一つの条件で複数のバリデーションをコントロールすることが出来ます。
```ruby
class User < ApplicationRecord
  with_options if: :is_admin? do |admin|
    admin.validates :password, length: { minimum: 10 }
    admin.validates :email, presence: true
  end
end
```
[引用] [Railsガイド](https://railsguides.jp/active_record_validations.html#%E6%9D%A1%E4%BB%B6%E4%BB%98%E3%81%8D%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97%E5%8C%96%E3%81%99%E3%82%8B)

- `:on`オプション
このオプションではバリデーション実行のタイミングを指定します。以下のように、指定したコンテキストでのみバリデーションを実行します。
```ruby
class User < ApplicationRecord
  validates :name, presence: true, on: :foo
end

user.valid?(:foo)
```
　また、`unless: -> { validation_context == :foo }`のように
　指定したコンテキスト以外の時にバリデーションを実行することも出来ます。


- `:allow_nil`オプション
読んで字の通り、中身がnilの時にバリデーションを通過させるオプションです。nilの時にはバリデーション書けなくていいんだよな、という時って結構あると思います。ちゃんと使っていきたいです。
使わないとこんなことになります。
```ruby:オプションを使わない場合
class Coffee < ApplicationRecord
  validate :validate_size

  def validate_size
    return if size.nil?

    errors.add(:size, '有効な値ではありません') unless %w(small medium large).include?(size)
  end
end
```

```ruby:オプションを使う場合
class Coffee < ApplicationRecord
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value}は有効な値ではありません" }, allow_nil: true
end
```

- `:allow_blank`
先程の`:allow_nil`と同様、読んで字の通り、中身がblankの時にバリデーションを通過させるオプションです。

## 最後に
今回紹介したオプション以外にも、`:strict`オプションや`:message`オプションもありますし、lengthのバリデーションを行う際のオプションやnumericalityのバリデーションを行う際のオプションもあります。公式ドキュメントを見たことない人がもしいれば、必ず目を通しておいたほうがいいと思います。公式ドキュメントは宝の山です。
私も、オプション使いこなして、読みやすくて無駄のない適切なコード目指してレベルアップしていきたいと思います！

【参考・引用】

https://railsguides.jp/active_record_validations.html

https://railsdoc.com/page/validates