Rubyでvalidationするdry-validationの基本のキ

# はじめに
RubyでAPIを作成する際にvalidationをいい感じにしてくれるgemを探したところ、dry-validationに行き着きました。
ただし、公式のリファレンスではよく分からないとか調べても古いバージョンの話だったとかがあったので、メモとして執筆時点で分かったことをメモとして残します。

# 環境
dry-validation 1.4
https://dry-rb.org/gems/dry-validation/1.4/

# Gemのインストール

```
gem install dry-validation
gem install dry-struct
```

# アプリケーションで使用するための基底クラスの作成
デフォルトだとメッセージが英語なので、日本語にするためにはconfigの設定が必要です。
なので基底クラスを使ってまとめて設定します。

``` ruby
class ApplicationContract < Dry::Validation::Contract
  config.messages.default_locale = :ja
  config.messages.backend = :yaml
  config.messages.load_paths << 'config/contract/error.yaml'
end
```

メッセージは`config.messages.load_paths`に設定した箇所に置いてください。
中身は以下のような感じです。
詳しいconfigの意味とかは公式のリファレンスで十分なので、そちらをご覧ください。

https://dry-rb.org/gems/dry-validation/1.4/messages/#using-localized-messages-backend

``` yaml
ja:
  dry_validation:
    errors:
      rules:
        {column_name}:
          filled?: '必須です'
          int?: '数字じゃありません'
          date?: '日付じゃありません'
          string?: '文字列じゃありません'
``` 

一番上にロケールを書く以外はフォーマットに沿って書いてあげる必要があります。

# validation class
基本的な設定ができたので、実際にvalidationするためのクラスを例に使い方を書いていきます。
まずはサンプルをご覧ください。

``` ruby
class FormContract < ApplicationContract
  params do
    required(:start_date).filled(:date)
    required(:end_date).filled(:date)
    optional(:id).filled(:integer)
    required(:name).maybe(:string)
  end

  rule(:end_date, :start_date) do
    key(:start_date).failure('開始日は終了日よりも後ろにしてください') if values[:end_date] < values[:start_date]
  end
```

## 単項目チェック
単項目はprams部分に記述します。

```ruby

  params do
    required(:start_date).value(:date)
    required(:end_date).filled(:date)
    optional(:id).filled(:integer)
    required(:name).maybe(:string)
  end 
```

### パラメータチェック
パラメータが必須であるか任意であるかを記述します。
必須である場合は`required`、任意である場合は`optional`で宣言します。

### 値チェック
値チェックはいくつか種類があります。
ここでは簡単なチェックのみで、パラメータのチェックを正規表現使ってやりたいといったチェックは別で行う必要があります。(後述)

#### filled
値の存在チェック + 型変換しての型チェックです。
値が存在しない場合は存在しないエラーとなり、値があり型変換後の型が違ったら型エラーとなります

``` ruby
# emptyエラー
{locale}.dry_validation.errors.filled?

# 型エラー
{locale}.dry_validation.errors.rules.{column_name}.int?
{locale}.dry_validation.errors.rules.{column_name}.str?
{locale}.dry_validation.errors.rules.{column_name}.date?
```

ちなみにfilledの引数に型を入れてるから型チェックしてくれるだけで、引数がなければ型チェックをスキップすることも可能です。

``` ruby
# 型チェックあり
required(:end_date).filled(:date)

# 型チェックなし
required(:end_date).filled()
```

#### value
厳密な型チェックです。

こちらはfilledと違って厳密な型チェックが行われます。
厳密な型チェックなので、formのパラメータをそのまま与えてvalidationするという用途には向いてません。
(formから渡ってきた値はstring扱いとなるため)

型チェックのみのため、型が違ったエラーしか返ってきません。

``` ruby
{locale}.dry_validation.errors.rules.{column_name}.int?
{locale}.dry_validation.errors.rules.{column_name}.str?
{locale}.dry_validation.errors.rules.{column_name}.date?
```


#### maybe
こちらも厳密な型チェックですが、値がnilの場合はチェックされません。
ただし、stringのblankはチェック対象となるため、注意が必要となります。

``` ruby
{locale}.dry_validation.errors.rules.{column_name}.int?
{locale}.dry_validation.errors.rules.{column_name}.str?
{locale}.dry_validation.errors.rules.{column_name}.date?
```

#### チェックをしたくない場合
チェックを行いたくない場合は何も書かなければOKです。
パラメータとしては必須だけど、2つの値のどちらかが入っていれば良いといったケースですね。

## 相関チェックや複雑なチェック
そういうのは`rule`に記載していきます。
エラーが発生した場合は、どのkeyにどんなエラーがあったかを設定していく形です。
`rule`に設定するキーも1〜n個引数が取れるので、全部こっちでvalidationさせることも可能です。

``` Ruby
  rule(:end_date, :start_date) do
    key(:start_date).failure('開始日は終了日よりも後ろにしてください') if values[:end_date] < values[:start_date]
  end
```

# 呼び出し方
呼び出しは`new`して`call`すれば良いです。
下のサンプルはrailsのparamsをそのまま引数として使う場合です。
`call`には`Hash`を引数としてあげればあとはやってくれます。

``` Ruby
FormContract.new.call(params.permit!.to_h)
```