railsチュートリアル第6章 フォーマットを検証する

###フォーマットを検証する
name属性の検証には、空文字でない、名前が51文字未満であるという```最小限の制約しか与えていません```でした。
email属性の場合は、有効なメールアドレスかどうかを判定するために、```もっと厳重な要求を満たさなければなりません```。
これまでは空のメールアドレスのみを禁止してきましたが、ここではメールアドレスに```おなじみのパターン```user@example.comに合っているかどうかも確認することを要求します。
最初に、```有効なメールアドレスと無効なメールアドレスのコレクションに対するテスト```を行いましょう。
このコレクションを作る方法として、次に示すように、文字列の配列を簡単に作れる```%w[]```という便利なテクニックを知っておくと良いでしょう。

```rb
>> %w[foo bar baz]
=> ["foo", "bar", "baz"]
>> addresses = %w[USER@foo.COM THE_US-ER@foo.bar.org  first.last@foo.jp]
=> ["USER@foo.COM", "THE_US-ER@foo.bar.org", "first.last@foo.jp"]
>> addresses.each do |address|
?>   puts address
>> end
USER@foo.COM
THE_US-ER@foo.bar.org
first.last@foo.jp
=> ["USER@foo.COM", "THE_US-ER@foo.bar.org", "first.last@foo.jp"]
```
```each ```メソッドを使って```addresses配列の各要素```を繰り返し取り出しました。
このテクニックを学んだことで、基本となる```メールアドレスフォーマット検証```のテストを書く準備が整いました。

メールアドレスのバリデーションは扱いが難しく、エラーが発生しやすい部分なので、```有効なメールアドレスと無効なメールアドレスをいくつか用意```して、バリデーション内のエラーを検知していきます。
具体的には、user@example,comのような無効なメールアドレスが弾かれることと、user@example.comのような有効なメールアドレスが通ることを確認しながら、バリデーションを実装していきます。 
まずは、有効なメールアドレスを示します。
####有効なメールフォーマットをテストする 

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end

  test "should be valid" do
    assert @user.valid?
  end
  
  test "name should be present" do
    @user.name = ""
    assert_not @user.valid?
  end  
  
  test "email should be present" do
    @user.email = "     "
    assert_not @user.valid?
  end
  
  test "name should not be too long" do
    @user.name = "a" * 51
    #51字以内
    assert_not @user.valid?
  end

  test "email should not be too long" do
    @user.email = "a" * 244 + "@example.com"
    #244字以内
    assert_not @user.valid?
  end
  
  test "email validation should accept valid addresses" do
    valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org
                         first.last@foo.jp alice+bob@baz.cn]
    #有効、無効のメールアドレスを用意
    valid_addresses.each do |valid_address|
      @user.email = valid_address
      assert @user.valid?, "#{valid_address.inspect} should be valid"
      #メールアドレスが有効ならば 以降の処理をする
    end
  end
end
```

```rb
assert @user.valid?, "#{valid_address.inspect} should be valid"
```
（詳細な文字列を調べるために4.3.3で紹介したinspectメソッドを使っています。）
```どのメールアドレスで失敗したのかを知ることは非常に便利```です。
そこでは、eachメソッドを使って各メールアドレスを順にテストしています。
ループさせずにテストすると、```失敗した行番号```と```メールアドレスの行数```を照らし合わせて、失敗したメールアドレスを特定するといった作業が発生してしまいます。

次に、user@example,com（ドットではなくカンマになっている）やuser_at_foo.org（アットマーク ‘@’ がない）といった無効なメールアドレスを使って 「```無効性（Invalidity）```」についてテストしていきます。
エラーメッセージをカスタマイズして、どのメールアドレスで失敗したのかすぐに特定できるようにしておきます。
####メールフォーマットの検証に対するテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end

  test "should be valid" do
    assert @user.valid?
  end
  
  test "name should be present" do
    @user.name = ""
    assert_not @user.valid?
  end  
  
  test "email should be present" do
    @user.email = "     "
    assert_not @user.valid?
  end
  
  test "name should not be too long" do
    @user.name = "a" * 51
    #51字以内
    assert_not @user.valid?
  end

  test "email should not be too long" do
    @user.email = "a" * 244 + "@example.com"
    #244字以内
    assert_not @user.valid?
  end
  
  test "email validation should accept valid addresses" do
  #有効なメールフォーマットを検証する
    valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org
                         first.last@foo.jp alice+bob@baz.cn]
    #有効、無効のメールアドレスを用意
    valid_addresses.each do |valid_address|
      @user.email = valid_address
      assert @user.valid?, "#{valid_address.inspect} should be valid"
      #メールアドレスが有効ならば 有効のメールアドレスを表示する
    end
  end
  
  test "email validation should reject invalid addresses" do
  #メールフォーマットの検証に対するテスト
    invalid_addresses = %w[user@example,com user_at_foo.org user.name@example.
                           foo@bar_baz.com foo@bar+baz.com]
    invalid_addresses.each do |invalid_address|
      @user.email = invalid_address
      assert_not @user.valid?, "#{invalid_address.inspect} should be invalid"
      #有効でなければ 無効のメールアドレスを表示する
    end
  end
end
```

```
ubuntu:~/environment/sample_app (modeling-users) $ rails t
Running via Spring preloader in process 8260
Started with run options --seed 20507

 FAIL["test_email_validation_should_reject_invalid_addresses", #<Minitest::Reporters::Suite:0x00007efe74ee67f8 @name="UserTest">, 0.03949682099982965]
 test_email_validation_should_reject_invalid_addresses#UserTest (0.04s)
        "user@example,com" should be invalid
        test/models/user_test.rb:50:in `block (2 levels) in <class:UserTest>'
        test/models/user_test.rb:48:in `each'
        test/models/user_test.rb:48:in `block in <class:UserTest>'

  15/15: [============================] 100% Time: 00:00:02, Time: 00:00:02

Finished in 2.77939s
15 tests, 30 assertions, 1 failures, 0 errors, 0 skips
```
```test_email_validation_should_reject_invalid_addresses ```がダメらしい。

メールアドレスのフォーマットを検証するためには、次のようにformatというオプションを使います。

```
validates :email, format: { with: /<regular expression>/ }
```

このオプションは引数に```正規表現（Regular Expression）（regexとも呼ばれます）```を取ります。
正規表現は一見謎めいて見えますが、```文字列のパターンマッチング```においては非常に強力な言語です。
つまり、有効なメールアドレスだけにマッチして、```無効なメールアドレスにはマッチしない正規表現を組み立てる```必要があります。

本チュートリアルでは、実践的でシンプルな正規表現を採用します。これがその正規表現です。

```
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
 ```

正規表現を本当に理解するためには実際に使って見るのが一番です。
例えば```Rubular```という対話的に正規表現を試せるWebサイトがあります。
このWebサイトはインタラクティブ性に富んだインターフェイスを持っていて、また、正規表現のクイックリファレンスも兼ね備えています。Rubularをブラウザで開き、実際に試してみることを強くお勧めします。
正規表現は、```読んで学ぶより対話的に学んだほうが早い```です。

正規表現を適用してemailのフォーマットを検証した結果を示します。
####メールフォーマットを正規表現で検証する

```rb
class User < ApplicationRecord
  validates :name,  presence: true, length: { maximum: 50 }
  #属性はname,属性の存在を検証、 最大50字まで
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
  #最大255字まで
                    format: { with: VALID_EMAIL_REGEX }
end

```
正規表現VALID_EMAIL_REGEXは```定数```です。
```大文字で始まる名前はRubyでは定数```を意味します。次のコードは、

```
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX }
```
これで、```このパターンに一致するメールアドレスだけが有効であることをチェック```できるようになります。
ただ上の正規表現には、少しだけ残念な点があります。
それは```foo@bar..comのようなドットの連続を誤り```として検出できない、という点です。
正規表現を修正すると直りますが、これについては演習に持ち越すことにしましょう
（訳注: ドットの連続だけでなく、国際化ドメインにも対応できた方が良いでしょう。指摘してくれた@znzさん、ありがとうございます!）。

この時点では、テストは green になるはずです。

```
Started with run options --seed 41242

  7/7: [==============================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.03163s
7 tests, 15 assertions, 0 failures, 0 errors, 0 skips
```

###演習
1.リスト 6.18にある有効なメールアドレスのリストと、リスト 6.19にある無効なメールアドレスのリストをRubularのYour test string:に転記してみてください。
lその後、リスト 6.21の正規表現をYour regular expression:に転記して、有効なメールアドレスのみがすべてマッチし、無効なメールアドレスはすべてマッチしないことを確認してみましょう。

2.先ほど触れたように、リスト 6.21のメールアドレスチェックする正規表現は、foo@bar..comのようにドットが連続した無効なメールアドレスを許容してしまいます。まずは、このメールアドレスをリスト 6.19の無効なメールアドレスリストに追加し、これによってテストが失敗することを確認してください。次に、リスト 6.23で示した、少し複雑な正規表現を使ってこのテストがパスすることを確認してください。

3.foo@bar..comをRubularのメールアドレスのリストに追加し、リスト 6.23の正規表現をRubularで使ってみてください。有効なメールアドレスのみがすべてマッチし、無効なメールアドレスはすべてマッチしないことを確認してみましょう。

1からNo matchesになり、できなかった。