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

###一意性を検証する
メールアドレスの一意性を強制するために（ユーザー名として使うために）、validatesメソッドの:uniquenessオプションを使います。
ただしここで重大な警告があります。次の文面は流し読みせず、必ず注意深く読んでください。

まずは小さなテストから書いていきます。
モデルのテストではこれまで、主にUser.newを使ってきました。
このメソッドは単にメモリ上にRubyのオブジェクトを作るだけです。
しかし、一意性のテストのためには、メモリ上だけではなく、実際に```レコードをデータベースに登録```する必要があります14 。
そのため、まずは```重複したメールアドレスからテスト```していきます。
####重複するメールアドレス拒否のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードでは、@userと同じメールアドレスのユーザーは作成できないことを、```@user.dup```を使ってテストしています。
dupは、```同じ属性を持つデータを複製```するためのメソッドです。
@userを保存した後では、複製されたユーザーのメールアドレスが既にデータベース内に存在するため、```ユーザの作成は無効```になるはずです。

テストをパスさせるために、emailのバリデーションに```uniqueness: true ```というオプションを追加します。

###メールアドレスの一意性を検証する 

```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 },
　　　　　　　　　　uniqueness: true
　　　　　　　　　　#???
end
```
実装の途中ですが、ここで１つ補足します。
通常、メールアドレスでは```大文字小文字が区別されません```。
すなわち、foo@bar.comはFOO@BAR.COMやFoO@BAr.coMと書いても```扱いは同じ```です。
したがって、メールアドレスの検証ではこのような場合も考慮する必要があります。
この性質のため、```大文字を区別しないでテストすることが重要```になり、実際のテストコードは下のようにしなければなりません。

####大文字小文字を区別しない、一意性のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    duplicate_user.email = @user.email.upcase
    # 大文字にする
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードではStringの```upcase```メソッドを使っています。
このテストは最初のメールアドレスの重複テストと同じことをしていますが、大文字に変換したメールアドレスを使っている点が異なります。
もしこのテストが少し抽象的すぎると感じるなら、Railsコンソールを起動して確認しましょう。

```
user = User.create(name: "Example User", email: "user@example.com")
   (0.1ms)  begin transaction
   (0.1ms)  SAVEPOINT active_record_1
  User Create (3.0ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Example User"], ["email", "user@example.com"], ["created_at", "2021-09-25 13:46:01.403754"], ["updated_at", "2021-09-25 13:46:01.403754"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Example User", email: "user@example.com", created_at: "2021-09-25 13:46:01", updated_at: "2021-09-25 13:46:01">

>> user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user = user.dup
=> #<User id: nil, name: "Example User", email: "user@example.com", created_at: nil, updated_at: nil>

>> duplicate_user.email = user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user.valid?
=> true
```

現在の一意性検証では大文字小文字を区別しているため、``Pduplicate_user.valid?はtrue```になります。
しかし、ここでは```false```になる必要があります。
幸い、:uniquenessでは:case_sensitiveという打ってつけのオプションが使えます。
####メールアドレスの大文字小文字を無視した一意性の検証

```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 },
　　　　　　　　　　uniqueness: { case_sensitive: false}
　　　　　　　　　　#???
end
```
ここで、trueを```case_sensitive: false```に置き換えただけであることに注目してください。
Railsはこの場合、```:uniqueness```をtrueと判断します。

この時点で、アプリケーションは重要な警告と共にメールアドレスの```一意性を強制```し、```テストスイート```もパスするはずです。

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

  16/16: [============================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.38493s
16 tests, 35 assertions, 0 failures, 0 errors, 0 skips
```

###一意性を検証する
メールアドレスの一意性を強制するために（ユーザー名として使うために）、validatesメソッドの:uniquenessオプションを使います。
ただしここで重大な警告があります。次の文面は流し読みせず、必ず注意深く読んでください。

まずは小さなテストから書いていきます。
モデルのテストではこれまで、主にUser.newを使ってきました。
このメソッドは単にメモリ上にRubyのオブジェクトを作るだけです。
しかし、一意性のテストのためには、メモリ上だけではなく、実際に```レコードをデータベースに登録```する必要があります14 。
そのため、まずは```重複したメールアドレスからテスト```していきます。
####重複するメールアドレス拒否のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードでは、@userと同じメールアドレスのユーザーは作成できないことを、```@user.dup```を使ってテストしています。
dupは、```同じ属性を持つデータを複製```するためのメソッドです。
@userを保存した後では、複製されたユーザーのメールアドレスが既にデータベース内に存在するため、```ユーザの作成は無効```になるはずです。

テストをパスさせるために、emailのバリデーションに```uniqueness: true ```というオプションを追加します。

###メールアドレスの一意性を検証する 

```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 },
　　　　　　　　　　uniqueness: true
　　　　　　　　　　#???
end
```
実装の途中ですが、ここで１つ補足します。
通常、メールアドレスでは```大文字小文字が区別されません```。
すなわち、foo@bar.comはFOO@BAR.COMやFoO@BAr.coMと書いても```扱いは同じ```です。
したがって、メールアドレスの検証ではこのような場合も考慮する必要があります。
この性質のため、```大文字を区別しないでテストすることが重要```になり、実際のテストコードは下のようにしなければなりません。

####大文字小文字を区別しない、一意性のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    duplicate_user.email = @user.email.upcase
    # 大文字にする
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードではStringの```upcase```メソッドを使っています。
このテストは最初のメールアドレスの重複テストと同じことをしていますが、大文字に変換したメールアドレスを使っている点が異なります。
もしこのテストが少し抽象的すぎると感じるなら、Railsコンソールを起動して確認しましょう。

```
user = User.create(name: "Example User", email: "user@example.com")
   (0.1ms)  begin transaction
   (0.1ms)  SAVEPOINT active_record_1
  User Create (3.0ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Example User"], ["email", "user@example.com"], ["created_at", "2021-09-25 13:46:01.403754"], ["updated_at", "2021-09-25 13:46:01.403754"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Example User", email: "user@example.com", created_at: "2021-09-25 13:46:01", updated_at: "2021-09-25 13:46:01">

>> user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user = user.dup
=> #<User id: nil, name: "Example User", email: "user@example.com", created_at: nil, updated_at: nil>

>> duplicate_user.email = user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user.valid?
=> true
```

現在の一意性検証では大文字小文字を区別しているため、``Pduplicate_user.valid?はtrue```になります。
しかし、ここでは```false```になる必要があります。
幸い、:uniquenessでは:case_sensitiveという打ってつけのオプションが使えます。
####メールアドレスの大文字小文字を無視した一意性の検証

```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 },
　　　　　　　　　　uniqueness: { case_sensitive: false}
　　　　　　　　　　#???
end
```
ここで、trueを```case_sensitive: false```に置き換えただけであることに注目してください。
Railsはこの場合、```:uniqueness```をtrueと判断します。

この時点で、アプリケーションは重要な警告と共にメールアドレスの```一意性を強制```し、```テストスイート```もパスするはずです。

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

  16/16: [============================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.38493s
16 tests, 35 assertions, 0 failures, 0 errors, 0 skips
```
しかし、依然としてここには1つの問題が残っています。
それは```Active Recordはデータベースのレベルでは一意性を保証していない```という問題です。
具体的なシナリオを使ってその問題を説明します。
同じメールアドレスを持つ2つのユーザーレコードが作成されてしまいます。

解決策の実装は簡単です。
実は、この問題はデータベースレベルでも一意性を強制するだけで解決します。
具体的にはデータベース上のemailのカラムにインデックス（index）を追加し、その```インデックスが一意```であるようにすれば解決します

#####データベースのインデックス
データベースにカラムを作成するとき、そのカラムでレコードを```検索する（find）必要```があるかどうかを考えることは重要です。
残念なことに、（インデックスなどの機能を持たない）素朴なデータモデルにおいてユーザーをメールアドレスで検索するには、データベースのひとりひとりのユーザーの行を```端から順```に読み出し、そのemail属性と渡されたメールアドレスを比較するという```非効率的```な方法しかありません。
emailカラムにインデックスを追加することで、この問題を解決することができます。
データベースのインデックスを理解するためには、```本の索引との類似性```を考えるとよいでしょう。
しかし索引のある本であれば、```"foobar"を含むすべてのページを索引の中から```探すだけで済みます。

emailインデックスを追加すると、```データモデリングの変更が必要```になります。
今回の場合は、既に存在するモデルに構造を追加するので、次のように```migrationジェネレーター```を使ってマイグレーションを直接作成する必要があります。

```
$ rails generate migration add_index_to_users_email
```
ユーザー用のマイグレーションと異なり、メールアドレスの一意性のマイグレーションは未定義になっています。
下のように定義を記述する必要があります。

####メールアドレスの一意性を強制するためのマイグレーション

```rb
class AddIndexToUsersEmail < ActiveRecord::Migration[6.0]
  def change
    add_index :users, :email, unique: true
    # usersテーブルのemailカラムにインデックスを追加
  end
end
```
上のコードでは、usersテーブルのemailカラムにインデックスを追加するために```add_index```というRailsのメソッドを使っています。
インデックス自体は一意性を強制しませんが、オプションで```unique: trueを指定することで強制```できるようになります。

最後に、データベースをマイグレートします。

```
$ rails db:migrate
```

上のマイグレーションが失敗した場合は、実行中のサンドボックスのコンソールセッションを終了していることを確認してください。
そのセッションがデータベースを```ロックしてマイグレーションを妨げている可能性があります。）

この時点では、テストDB用のサンプルデータが含まれているfixtures内で一意性の制限が保たれていないため、テストは red になります。
つまり、リスト 6.1でユーザー用のfixtureが自動的に生成されていましたが、ここのメールアドレスが一意になっていないことが原因です（リスト 6.30）
（実はこのデータはいずれも有効ではありませんが、fixture内のサンプルデータはバリデーションを通っていなかったので今まで問題にはなっていなかっただけでした）
####Userのデフォルトfixture

```
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
  name: MyString
  email: MyString

two:
  name: MyString
  email: MyString
```
また、このfixtureは第8章になるまで使わない予定なので、今のところはこれらのデータを削除しておき、ユーザー用のfixtureファイルを空にしておきましょう。

#######一意性を検証する
メールアドレスの一意性を強制するために（ユーザー名として使うために）、validatesメソッドの:uniquenessオプションを使います。
まずは小さなテストから書いていきます。
モデルのテストではこれまで、主にUser.newを使ってきました。
このメソッドは単にメモリ上にRubyのオブジェクトを作るだけです。
しかし、一意性のテストのためには、メモリ上だけではなく、実際に```レコードをデータベースに登録```する必要があります。
そのため、まずは```重複したメールアドレスからテスト```していきます。
####重複するメールアドレス拒否のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードでは、@userと同じメールアドレスのユーザーは作成できないことを、```@user.dup```を使ってテストしています。
dupは、```同じ属性を持つデータを複製```するためのメソッドです。
@userを保存した後では、複製されたユーザーのメールアドレスが既にデータベース内に存在するため、```ユーザの作成は無効```になるはずです。

テストをパスさせるために、emailのバリデーションに```uniqueness: true ```というオプションを追加します。

###メールアドレスの一意性を検証する 

```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 },
　　　　　　　　　　uniqueness: true
　　　　　　　　　　#???
end
```
実装の途中ですが、ここで１つ補足します。
通常、メールアドレスでは```大文字小文字が区別されません```。
すなわち、foo@bar.comはFOO@BAR.COMやFoO@BAr.coMと書いても```扱いは同じ```です。
したがって、メールアドレスの検証ではこのような場合も考慮する必要があります。
この性質のため、```大文字を区別しないでテストすることが重要```になり、実際のテストコードは下のようにしなければなりません。

####大文字小文字を区別しない、一意性のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    duplicate_user.email = @user.email.upcase
    # 大文字にする
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードではStringの```upcase```メソッドを使っています。
このテストは最初のメールアドレスの重複テストと同じことをしていますが、大文字に変換したメールアドレスを使っている点が異なります。
もしこのテストが少し抽象的すぎると感じるなら、Railsコンソールを起動して確認しましょう。

```
user = User.create(name: "Example User", email: "user@example.com")
   (0.1ms)  begin transaction
   (0.1ms)  SAVEPOINT active_record_1
  User Create (3.0ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Example User"], ["email", "user@example.com"], ["created_at", "2021-09-25 13:46:01.403754"], ["updated_at", "2021-09-25 13:46:01.403754"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Example User", email: "user@example.com", created_at: "2021-09-25 13:46:01", updated_at: "2021-09-25 13:46:01">

>> user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user = user.dup
=> #<User id: nil, name: "Example User", email: "user@example.com", created_at: nil, updated_at: nil>

>> duplicate_user.email = user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user.valid?
=> true
```

現在の一意性検証では大文字小文字を区別しているため、``Pduplicate_user.valid?はtrue```になります。
しかし、ここでは```false```になる必要があります。
幸い、:uniquenessでは:case_sensitiveという打ってつけのオプションが使えます。
####メールアドレスの大文字小文字を無視した一意性の検証

```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 },
　　　　　　　　　　uniqueness: { case_sensitive: false}
　　　　　　　　　　#???
end
```
ここで、trueを```case_sensitive: false```に置き換えただけであることに注目してください。
Railsはこの場合、```:uniqueness```をtrueと判断します。

この時点で、アプリケーションは重要な警告と共にメールアドレスの```一意性を強制```し、```テストスイート```もパスするはずです。

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

  16/16: [============================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.38493s
16 tests, 35 assertions, 0 failures, 0 errors, 0 skips
```

###一意性を検証する
メールアドレスの一意性を強制するために（ユーザー名として使うために）、validatesメソッドの:uniquenessオプションを使います。
ただしここで重大な警告があります。次の文面は流し読みせず、必ず注意深く読んでください。

まずは小さなテストから書いていきます。
モデルのテストではこれまで、主にUser.newを使ってきました。
このメソッドは単にメモリ上にRubyのオブジェクトを作るだけです。
しかし、一意性のテストのためには、メモリ上だけではなく、実際に```レコードをデータベースに登録```する必要があります14 。
そのため、まずは```重複したメールアドレスからテスト```していきます。
####重複するメールアドレス拒否のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードでは、@userと同じメールアドレスのユーザーは作成できないことを、```@user.dup```を使ってテストしています。
dupは、```同じ属性を持つデータを複製```するためのメソッドです。
@userを保存した後では、複製されたユーザーのメールアドレスが既にデータベース内に存在するため、```ユーザの作成は無効```になるはずです。

テストをパスさせるために、emailのバリデーションに```uniqueness: true ```というオプションを追加します。

###メールアドレスの一意性を検証する 

```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 },
　　　　　　　　　　uniqueness: true
　　　　　　　　　　#???
end
```
実装の途中ですが、ここで１つ補足します。
通常、メールアドレスでは```大文字小文字が区別されません```。
すなわち、foo@bar.comはFOO@BAR.COMやFoO@BAr.coMと書いても```扱いは同じ```です。
したがって、メールアドレスの検証ではこのような場合も考慮する必要があります。
この性質のため、```大文字を区別しないでテストすることが重要```になり、実際のテストコードは下のようにしなければなりません。

####大文字小文字を区別しない、一意性のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    duplicate_user.email = @user.email.upcase
    # 大文字にする
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードではStringの```upcase```メソッドを使っています。
このテストは最初のメールアドレスの重複テストと同じことをしていますが、大文字に変換したメールアドレスを使っている点が異なります。
もしこのテストが少し抽象的すぎると感じるなら、Railsコンソールを起動して確認しましょう。

```
user = User.create(name: "Example User", email: "user@example.com")
   (0.1ms)  begin transaction
   (0.1ms)  SAVEPOINT active_record_1
  User Create (3.0ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Example User"], ["email", "user@example.com"], ["created_at", "2021-09-25 13:46:01.403754"], ["updated_at", "2021-09-25 13:46:01.403754"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Example User", email: "user@example.com", created_at: "2021-09-25 13:46:01", updated_at: "2021-09-25 13:46:01">

>> user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user = user.dup
=> #<User id: nil, name: "Example User", email: "user@example.com", created_at: nil, updated_at: nil>

>> duplicate_user.email = user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user.valid?
=> true
```

現在の一意性検証では大文字小文字を区別しているため、``Pduplicate_user.valid?はtrue```になります。
しかし、ここでは```false```になる必要があります。
幸い、:uniquenessでは:case_sensitiveという打ってつけのオプションが使えます。
####メールアドレスの大文字小文字を無視した一意性の検証

```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 },
　　　　　　　　　　uniqueness: { case_sensitive: false}
　　　　　　　　　　#???
end
```
ここで、trueを```case_sensitive: false```に置き換えただけであることに注目してください。
Railsはこの場合、```:uniqueness```をtrueと判断します。

この時点で、アプリケーションは重要な警告と共にメールアドレスの```一意性を強制```し、```テストスイート```もパスするはずです。

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

  16/16: [============================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.38493s
16 tests, 35 assertions, 0 failures, 0 errors, 0 skips
```
しかし、依然としてここには1つの問題が残っています。
それは```Active Recordはデータベースのレベルでは一意性を保証していない```という問題です。
具体的なシナリオを使ってその問題を説明します。
同じメールアドレスを持つ2つのユーザーレコードが作成されてしまいます。

解決策の実装は簡単です。
実は、この問題はデータベースレベルでも一意性を強制するだけで解決します。
具体的にはデータベース上のemailのカラムにインデックス（index）を追加し、その```インデックスが一意```であるようにすれば解決します

#####データベースのインデックス
データベースにカラムを作成するとき、そのカラムでレコードを```検索する（find）必要```があるかどうかを考えることは重要です。
残念なことに、（インデックスなどの機能を持たない）素朴なデータモデルにおいてユーザーをメールアドレスで検索するには、データベースのひとりひとりのユーザーの行を```端から順```に読み出し、そのemail属性と渡されたメールアドレスを比較するという```非効率的```な方法しかありません。
emailカラムにインデックスを追加することで、この問題を解決することができます。
データベースのインデックスを理解するためには、```本の索引との類似性```を考えるとよいでしょう。
索引のない本では、渡された言葉（例えば、"foobar"）が出てくる箇所をすべて見つけるためには、ページを端から順にめくって最後まで探す必要があります（紙バージョンの全表スキャン）。
しかし索引のある本であれば、```"foobar"を含むすべてのページを索引の中から```探すだけで済みます。

emailインデックスを追加すると、```データモデリングの変更が必要```になります。
今回の場合は、既に存在するモデルに構造を追加するので、次のように```migrationジェネレーター```を使ってマイグレーションを直接作成する必要があります。

```
$ rails generate migration add_index_to_users_email
```
ユーザー用のマイグレーションと異なり、メールアドレスの一意性のマイグレーションは未定義になっています。
下のように定義を記述する必要があります。

####メールアドレスの一意性を強制するためのマイグレーション

```rb
class AddIndexToUsersEmail < ActiveRecord::Migration[6.0]
  def change
    add_index :users, :email, unique: true
    # usersテーブルのemailカラムにインデックスを追加
  end
end
```
上のコードでは、usersテーブルのemailカラムにインデックスを追加するために```add_index```というRailsのメソッドを使っています。
インデックス自体は一意性を強制しませんが、オプションで```unique: trueを指定することで強制```できるようになります。

最後に、データベースをマイグレートします。

```
$ rails db:migrate
```

上のマイグレーションが失敗した場合は、実行中のサンドボックスのコンソールセッションを終了していることを確認してください。
そのセッションがデータベースを```ロックしてマイグレーションを妨げている可能性があります。）

この時点では、テストDB用のサンプルデータが含まれているfixtures内で一意性の制限が保たれていないため、テストは red になります。
つまり、リスト 6.1でユーザー用のfixtureが自動的に生成されていましたが、ここのメールアドレスが一意になっていないことが原因です（リスト 6.30）
（実はこのデータはいずれも有効ではありませんが、fixture内のサンプルデータはバリデーションを通っていなかったので今まで問題にはなっていなかっただけでした）
####Userのデフォルトfixture

```
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
  name: MyString
  email: MyString

two:
  name: MyString
  email: MyString
```
また、このfixtureは第8章になるまで使わない予定なので、今のところはこれらのデータを削除しておき、ユーザー用のfixtureファイルを空にしておきましょう（リスト 6.31）。

#######一意性を検証する
メールアドレスの一意性を強制するために（ユーザー名として使うために）、validatesメソッドの:uniquenessオプションを使います。
ただしここで重大な警告があります。次の文面は流し読みせず、必ず注意深く読んでください。

まずは小さなテストから書いていきます。
モデルのテストではこれまで、主にUser.newを使ってきました。
このメソッドは単にメモリ上にRubyのオブジェクトを作るだけです。
しかし、一意性のテストのためには、メモリ上だけではなく、実際に```レコードをデータベースに登録```する必要があります14 。
そのため、まずは```重複したメールアドレスからテスト```していきます。
####重複するメールアドレス拒否のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードでは、@userと同じメールアドレスのユーザーは作成できないことを、```@user.dup```を使ってテストしています。
dupは、```同じ属性を持つデータを複製```するためのメソッドです。
@userを保存した後では、複製されたユーザーのメールアドレスが既にデータベース内に存在するため、```ユーザの作成は無効```になるはずです。

テストをパスさせるために、emailのバリデーションに```uniqueness: true ```というオプションを追加します。

###メールアドレスの一意性を検証する 

```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 },
　　　　　　　　　　uniqueness: true
　　　　　　　　　　#???
end
```
実装の途中ですが、ここで１つ補足します。
通常、メールアドレスでは```大文字小文字が区別されません```。
すなわち、foo@bar.comはFOO@BAR.COMやFoO@BAr.coMと書いても```扱いは同じ```です。
したがって、メールアドレスの検証ではこのような場合も考慮する必要があります。
この性質のため、```大文字を区別しないでテストすることが重要```になり、実際のテストコードは下のようにしなければなりません。

####大文字小文字を区別しない、一意性のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    duplicate_user.email = @user.email.upcase
    # 大文字にする
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードではStringの```upcase```メソッドを使っています。
このテストは最初のメールアドレスの重複テストと同じことをしていますが、大文字に変換したメールアドレスを使っている点が異なります。
もしこのテストが少し抽象的すぎると感じるなら、Railsコンソールを起動して確認しましょう。

```
user = User.create(name: "Example User", email: "user@example.com")
   (0.1ms)  begin transaction
   (0.1ms)  SAVEPOINT active_record_1
  User Create (3.0ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Example User"], ["email", "user@example.com"], ["created_at", "2021-09-25 13:46:01.403754"], ["updated_at", "2021-09-25 13:46:01.403754"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Example User", email: "user@example.com", created_at: "2021-09-25 13:46:01", updated_at: "2021-09-25 13:46:01">

>> user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user = user.dup
=> #<User id: nil, name: "Example User", email: "user@example.com", created_at: nil, updated_at: nil>

>> duplicate_user.email = user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user.valid?
=> true
```

現在の一意性検証では大文字小文字を区別しているため、``Pduplicate_user.valid?はtrue```になります。
しかし、ここでは```false```になる必要があります。
幸い、:uniquenessでは:case_sensitiveという打ってつけのオプションが使えます。
####メールアドレスの大文字小文字を無視した一意性の検証

```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 },
　　　　　　　　　　uniqueness: { case_sensitive: false}
　　　　　　　　　　#???
end
```
ここで、trueを```case_sensitive: false```に置き換えただけであることに注目してください。
Railsはこの場合、```:uniqueness```をtrueと判断します。

この時点で、アプリケーションは重要な警告と共にメールアドレスの```一意性を強制```し、```テストスイート```もパスするはずです。

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

  16/16: [============================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.38493s
16 tests, 35 assertions, 0 failures, 0 errors, 0 skips
```

###一意性を検証する
メールアドレスの一意性を強制するために（ユーザー名として使うために）、validatesメソッドの:uniquenessオプションを使います。
ただしここで重大な警告があります。次の文面は流し読みせず、必ず注意深く読んでください。

まずは小さなテストから書いていきます。
モデルのテストではこれまで、主にUser.newを使ってきました。
このメソッドは単にメモリ上にRubyのオブジェクトを作るだけです。
しかし、一意性のテストのためには、メモリ上だけではなく、実際に```レコードをデータベースに登録```する必要があります14 。
そのため、まずは```重複したメールアドレスからテスト```していきます。
####重複するメールアドレス拒否のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードでは、@userと同じメールアドレスのユーザーは作成できないことを、```@user.dup```を使ってテストしています。
dupは、```同じ属性を持つデータを複製```するためのメソッドです。
@userを保存した後では、複製されたユーザーのメールアドレスが既にデータベース内に存在するため、```ユーザの作成は無効```になるはずです。

テストをパスさせるために、emailのバリデーションに```uniqueness: true ```というオプションを追加します。

###メールアドレスの一意性を検証する 

```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 },
　　　　　　　　　　uniqueness: true
　　　　　　　　　　#???
end
```
実装の途中ですが、ここで１つ補足します。
通常、メールアドレスでは```大文字小文字が区別されません```。
すなわち、foo@bar.comはFOO@BAR.COMやFoO@BAr.coMと書いても```扱いは同じ```です。
したがって、メールアドレスの検証ではこのような場合も考慮する必要があります。
この性質のため、```大文字を区別しないでテストすることが重要```になり、実際のテストコードは下のようにしなければなりません。

####大文字小文字を区別しない、一意性のテスト

```rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  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
  
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    duplicate_user.email = @user.email.upcase
    # 大文字にする
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```
上のコードではStringの```upcase```メソッドを使っています。
このテストは最初のメールアドレスの重複テストと同じことをしていますが、大文字に変換したメールアドレスを使っている点が異なります。
もしこのテストが少し抽象的すぎると感じるなら、Railsコンソールを起動して確認しましょう。

```
user = User.create(name: "Example User", email: "user@example.com")
   (0.1ms)  begin transaction
   (0.1ms)  SAVEPOINT active_record_1
  User Create (3.0ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Example User"], ["email", "user@example.com"], ["created_at", "2021-09-25 13:46:01.403754"], ["updated_at", "2021-09-25 13:46:01.403754"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Example User", email: "user@example.com", created_at: "2021-09-25 13:46:01", updated_at: "2021-09-25 13:46:01">

>> user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user = user.dup
=> #<User id: nil, name: "Example User", email: "user@example.com", created_at: nil, updated_at: nil>

>> duplicate_user.email = user.email.upcase
=> "USER@EXAMPLE.COM"

>> duplicate_user.valid?
=> true
```

現在の一意性検証では大文字小文字を区別しているため、``Pduplicate_user.valid?はtrue```になります。
しかし、ここでは```false```になる必要があります。
幸い、:uniquenessでは:case_sensitiveという打ってつけのオプションが使えます。
####メールアドレスの大文字小文字を無視した一意性の検証

```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 },
　　　　　　　　　　uniqueness: { case_sensitive: false}
　　　　　　　　　　#???
end
```
ここで、trueを```case_sensitive: false```に置き換えただけであることに注目してください。
Railsはこの場合、```:uniqueness```をtrueと判断します。

この時点で、アプリケーションは重要な警告と共にメールアドレスの```一意性を強制```し、```テストスイート```もパスするはずです。

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

  16/16: [============================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.38493s
16 tests, 35 assertions, 0 failures, 0 errors, 0 skips
```
しかし、依然としてここには1つの問題が残っています。
それは```Active Recordはデータベースのレベルでは一意性を保証していない```という問題です。
具体的なシナリオを使ってその問題を説明します。
同じメールアドレスを持つ2つのユーザーレコードが作成されてしまいます。

解決策の実装は簡単です。
実は、この問題はデータベースレベルでも一意性を強制するだけで解決します。
具体的にはデータベース上のemailのカラムにインデックス（index）を追加し、その```インデックスが一意```であるようにすれば解決します

#####データベースのインデックス
データベースにカラムを作成するとき、そのカラムでレコードを```検索する（find）必要```があるかどうかを考えることは重要です。
残念なことに、（インデックスなどの機能を持たない）素朴なデータモデルにおいてユーザーをメールアドレスで検索するには、データベースのひとりひとりのユーザーの行を```端から順```に読み出し、そのemail属性と渡されたメールアドレスを比較するという```非効率的```な方法しかありません。
emailカラムにインデックスを追加することで、この問題を解決することができます。
データベースのインデックスを理解するためには、```本の索引との類似性```を考えるとよいでしょう。
索引のない本では、渡された言葉（例えば、"foobar"）が出てくる箇所をすべて見つけるためには、ページを端から順にめくって最後まで探す必要があります。
しかし索引のある本であれば、```"foobar"を含むすべてのページを索引の中から```探すだけで済みます。

emailインデックスを追加すると、```データモデリングの変更が必要```になります。
今回の場合は、既に存在するモデルに構造を追加するので、次のように```migrationジェネレーター```を使ってマイグレーションを直接作成する必要があります。

```
$ rails generate migration add_index_to_users_email
```
ユーザー用のマイグレーションと異なり、メールアドレスの一意性のマイグレーションは未定義になっています。
下のように定義を記述する必要があります。

####メールアドレスの一意性を強制するためのマイグレーション

```rb
class AddIndexToUsersEmail < ActiveRecord::Migration[6.0]
  def change
    add_index :users, :email, unique: true
    # usersテーブルのemailカラムにインデックスを追加
  end
end
```
上のコードでは、usersテーブルのemailカラムにインデックスを追加するために```add_index```というRailsのメソッドを使っています。
インデックス自体は一意性を強制しませんが、オプションで```unique: trueを指定することで強制```できるようになります。

最後に、データベースをマイグレートします。

```
$ rails db:migrate
```

上のマイグレーションが失敗した場合は、実行中のサンドボックスのコンソールセッションを終了していることを確認してください。
そのセッションがデータベースをロックしてマイグレーションを妨げている可能性があります。

この時点では、テストDB用のサンプルデータが含まれているfixtures内で一意性の制限が保たれていないため、テストは red になります。
ユーザー用のfixtureが自動的に生成されていましたが、ここのメールアドレスが```一意になっていない```ことが原因です

####Userのデフォルトfixture

```
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
  name: MyString
  email: MyString

two:
  name: MyString
  email: MyString
```
また、このfixtureは第8章になるまで使わない予定なので、今のところはこれらのデータを削除しておき、ユーザー用のfixtureファイルを空にしておきましょう。

####空のfixtureファイル

```
#空にする
```

これで1つの問題が解決されましたが、メールアドレスの一意性を保証するためには、もう1つやらなければならないことがあります。
それは、いくつかのデータベースのアダプタが、```常に大文字小文字を区別するインデックス を使っているとは限らない```問題への対処です。
この問題を避けるために、今回は「データベースに保存される直前に```すべての文字列を小文字に変換する```」という対策を採ります。
これを実装するためにActive Recordの```コールバック（callback）```メソッドを利用します。
このメソッドは、ある特定の時点で呼び出されるメソッドです。
今回の場合は、オブジェクトが保存される時点で処理を実行したいので、```before_save```というコールバックを使います。
これを使って、ユーザーをデータベースに保存する前にemail属性を```強制的に小文字に変換```します 。
作成したコードを下示します。
####email属性を小文字に変換してメールアドレスの一意性を保証する 

```rb
class User < ApplicationRecord
  before_save { self.email = email.downcase }
  # データベースに保存する前に処理をする。
  # 入力されたメールアドレスを小文字にする。
  #　コールバックの一つ
  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 },
                    uniqueness: true
                    #???
end
```
コードはbefore_saveコールバックのブロックまでパスし、downcaseという文字列メソッドを用いてユーザーのメールアドレスの```現在の値を小文字に変換```します。

リスト 6.32ではuniqueness制約をtrueに戻していることにご注目ください。
メールアドレスが小文字で統一されれば、大文字小文字を```区別するマッチが問題なく```動作できるからです。実際、この手法によってデータベースインデックスを適用する際の問題を防止しています。
制約を元に戻すとテストが失敗しますが、前の形に戻すだけで簡単に修正できます。修正結果をリスト 6.33に改めて示します。
####元のメールuniquenessテストに戻す

```rb
require 'test_helper'

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

  test "email addresses should be unique" do
    duplicate_user = @user.dup
    #dup データを複製する
    @user.save
    assert_not duplicate_user.valid?
    # 複製されたデータの存在は有効じゃないかを確認
  end
end
```

```rb
self.email = self.email.downcase
```
Userモデルの中では右式の```selfを省略できる```ので、今回は次のように書きました
（ちなみにこのselfは現在のユーザーを指します）

```rb
self.email = email.downcase
```
palindrome内でreverseメソッドを使っていたときも、同様のケースであったことを思い出してください。
そのときと同様で、```左式ではselfを省略することはできません```。したがって、

```rb
email = email.downcase
```
と書くとうまく動きません。

これで、先に述べたアリスのシナリオはうまくいくようになります。
データベースは、最初のリクエストに基づいてユーザーのレコードを保存しますが、2度目の保存は一意性の制約に反するので拒否します。
email属性にインデックスを付与したことによって、メールアドレスからユーザーを引くときに全表スキャンを使わずに済むようになった。

###演習
1.リスト 6.34のように、メールアドレスを小文字にするテストをリスト 6.26に追加してみましょう。
ちなみに追加するテストコードでは、データベースの値に合わせて更新するreloadメソッドと、値が一致しているかどうか確認するassert_equalメソッドを使っています。
リスト 6.34のテストがうまく動いているか確認するためにも、before_saveの行をコメントアウトして red になることを、また、コメントアウトを解除すると green になることを確認してみましょう。

```rb
require 'test_helper'

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

  test "email addresses should be saved as lower-case" do
    mixed_case_email = "Foo@ExAMPle.CoM"
    @user.email = mixed_case_email
    #入力されるemail
    @user.save
    assert_equal mixed_case_email.downcase, @user.reload.email
  　# 小文字にしたemailと元のデータベースと等しいかを確かめる
  end
end
```

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

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

Finished in 2.07393s
17 tests, 36 assertions, 0 failures, 0 errors, 0 skips
```
比べる相手がないからテストは失敗しそう。

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

ERROR["test_email_addresses_should_be_saved_as_lower-case", #<Minitest::Reporters::Suite:0x00007fc074cfef40 @name="UserTest">, 0.07783683099978589]
 test_email_addresses_should_be_saved_as_lower-case#UserTest (0.08s)
ActiveRecord::RecordNotFound:         ActiveRecord::RecordNotFound: Couldn't find User without an ID
            test/models/user_test.rb:68:in `block in <class:UserTest>'

  17/17: [============================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.59611s
17 tests, 35 assertions, 0 failures, 1 errors, 0 skips
```
```Couldn't find User without an ID ```
多分予想通りだと思う。

2.テストスイートの実行結果を確認しながら、before_saveコールバックをemail.downcase!に書き換えてみましょう。
ヒント: メソッドの末尾に!を付け足すことにより、email属性を直接変更できるようになります（リスト 6.35）。

```rb
class User < ApplicationRecord
  before_save { email.downcase! }
  # データベースに保存する前に処理をする。
  # 入力されたメールアドレスを小文字にする。
  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 },
                    uniqueness: true
                    #???
end
```

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

  17/17: [============================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.56079s
17 tests, 36 assertions, 0 failures, 0 errors, 0 skips
```