Railsチュートリアル6章まとめ

# 目的
ユーザー登録ページを作成する。
一番重要なステップであるユーザー用のデータモデルの作成と、
データを保存する手段の確保について学ぶ。

# 6.1ユーザーモデル
**・Active Record**
データベースとやりとりをするデフォルトのRailsライブラリ

**・マイグレーション（Migration）**
直接SQLを使わずにデータベースのテーブルやカラムなどの構造を変更できる仕組み

# 6.1.1　データベースのマイグレーション
簡単に消えることのないユーザーのモデルを構築を行う

**・Usersコントローラーを作成**
rails generate controller Users new

**・Userモデルを作成**
generate modelとするとモデルを作成できる
rails generate model User name:string email:string

※コントローラ名には複数形を使い、モデル名には単数形を用いる

**・マイグレーションファイル**
generateコマンドを実行するとマイグレーションファイルも新しく生成される
内容はデータベースに与える変更を定義したchangeメソッドの集まり

**・changeメソッド**
create_tableというRailsのメソッドを呼び、ユーザーを保存するためのテーブルをデータベースに作成している

```
class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end
```

**・マイグレーションの適用（migrating up）**
rails db:migrateで実行できる

# 6.1.3　ユーザーオブジェクトを作成する
**・サンドボックスモード**
rails console --sandbox
データベースへの変更をコンソールの終了時にすべて “ロールバック”（取り消し）してくれる。

４章ではUser.newで新しいユーザーオブジェクトを生成していたが、
example_userファイルを明示的にrequireするまでこのオブジェクトにアクセスできなかった。
モデルを使うとRailsコンソールは起動時にRailsの環境を自動的に読み込み、その環境にモデルも含まれているため、新しいユーザーオブジェクトを作成するときに余分な作業を行わずに済む。

今まで　
```
>> require './example_user'     # example_userのコードを読み込む方法
=> true
>> example = User.new
```


モデルを使う場合
```
>> User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
```


**・有効性（Validity）**
オブジェクトが有効かどうか確認できる
データベースにデータが存在するかどうかは確認できない
```
>> user.valid?
true
```
**・save**
データベースにUserオブジェクトを保存する
データベースにデータが存在するかどうかできる
```
>> user.save
   (0.1ms)  SAVEPOINT active_record_1
  SQL (0.8ms)  INSERT INTO "users" ("name", "email", "created_at",
  "updated_at") VALUES (?, ?, ?, ?)  [["name", "Michael Hartl"],
  ["email", "michael@example.com"], ["created_at", "2022-03-11 01:51:03.453035"],
  ["updated_at", "2022-03-11 01:51:03.453035"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> true
```
**・create**
モデルの生成と保存を同時におこなうことができる
```
>> User.create(name: "A Nother", email: "another@example.org")
#<User id: 2, name: "A Nother", email: "another@example.org", created_at:
"2022-03-11 01:53:22", updated_at: "2022-03-11 01:53:22">
>> foo = User.create(name: "Foo", email: "foo@bar.com")
#<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2022-03-11
01:54:03", updated_at: "2022-03-11 01:54:03">
```


**・destroy**
データベースのオブジェクトを削除する
削除されたオブジェクトはメモリ上には残る
```
>> foo.destroy
   (0.1ms)  SAVEPOINT active_record_1
  SQL (0.2ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 3]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2022-03-11
01:54:03", updated_at: "2022-03-11 01:54:03">
```

# 6.1.4ユーザーオブジェクトを検索する
・find
```
User.find(1)
```
ユーザーのidを渡して、そのidを持つユーザーを返す。

**・find_by**
属性を指定してユーザーを検索する
```
>> User.find_by(email: "michael@example.com")
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.com",
created_at: "2022-03-11 01:51:03", updated_at: "2022-03-11 01:51:03">
```


**・first**
データベースの最初のユーザーを返す
```
>> User.find(1)
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.com",
created_at: "2022-03-11 01:51:03", updated_at: "2022-03-11 01:51:03">
```


**・all**
データベースのすべてのUserオブジェクトを返す
```
>> User.all
=> [#<User id: 1, name: "Michael Hartl", email:
"michael@example.com", created_at: "2022-03-11 01:51:03", updated_at:
"2022-03-11 01:51:03">, #<User id: 2, name: "A Nother", email:
"another@example.org", created_at: "2022-03-11 01:53:22", updated_at:
"2022-03-11 01:53:22">]>
```
# 6.1.5 ユーザーオブジェクトを更新する
**・属性を個別に代入する**
```
user.email = "mhartl@example.net"
user.save
```
最後にsaveを実行して変更をデータベースに保存する
保存を行わずにreloadを実行すると、データベースの情報を元にオブジェクトを再読み込みするため、変更が取り消される

```
>> user           # userオブジェクトが持つ情報のおさらい
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.com",
created_at: "2022-03-11 01:51:03", updated_at: "2022-03-11 01:51:03">
>> user.email = "mhartl@example.net"
=> "mhartl@example.net"
>> user.save
=> true
```


**・updateを使い更新する**
```
user.update(name: "The Dude", email: "dude@abides.org")
```
**・update_attribute**
特定の属性のみを更新
user.update_attribute(:name, "El Duderino")

# 6.2 ユーザーを検証する
nameとemailにあらゆる文字列を許さないようにする

# 6.2.1
有効性を検証する
**・モデルの有効性をテストする手順**
有効なモデルのオブジェクトを作成
↓
その属性のうちの1つを有効でない属性に意図的に変更
↓
そして、バリデーションで失敗するかどうかをテストする

テストコード
```
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
end
```


**・setup**
有効なUserオブジェクト（@user）を作成する
```
def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end
```


# 6.2.2 存在性を検証する
**・存在性（Presence）**
渡された属性が存在することを検証する
章の内容では
ユーザーがデータベースに保存される前にnameとemailフィールドの両方が存在することを保証する

**・name属性の存在性に関するテスト**
テストコード
```
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
end
```


・validates メソッドにpresence: trueという引数を与えて使うことで
name属性の存在を検査できる
```
class User < ApplicationRecord
  validates :name, presence: true
end
```
↓
```
class User < ApplicationRecord
  validates(:name, presence: true)
end
```
と同義

実際の動き
```
$ rails console --sandbox
>> user = User.new(name: "", email: "michael@example.com")
>> user.valid?
=> false
```

・失敗したときに作られるerrorsオブジェクトを使って確認すれば
どのバリデーションが失敗したかわかる
```
user.errors.full_messages
>> user.errors.full_messages
=> ["Name can't be blank"]
```

# 6.2.3　長さを検証する
ユーザーの名前の長さに制限を与える

```
validates :name,  presence: true, length: { maximum: 50 }
validates :email, presence: true, length: { maximum: 255 }
```


{ maximum:  }で上限を決めている

# 6.2.4 フォーマットを検証する
メールアドレスにおなじみのパターンuser@example.comに合っているかどうかの確認をする
user@example,comのような無効なメールアドレスが弾かれるようにする

```
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX }
```


上のコードを使うと
このパターンに一致するメールアドレスだけが有効であることをチェックできるようになる

# 6.2.5　一意性を検証する
重複したメールアドレスのチェック
@user.dupで
@userと同じメールアドレスのユーザーは作成できないことをチェックしている

```
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
    @user.save
    assert_not duplicate_user.valid?
  end
end
```
**・メールアドレスの一意性を有効にする**
uniqueness: true

```
class User < ApplicationRecord
  validates :name,  presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: true
end
```


・メールアドレスでは大文字小文字が区別されない。
すなわち、foo@bar.comはFOO@BAR.COMやFoO@BAr.coMと書いても扱いは同じ
そのためテストでメールアドレスを一度大文字に変換してから確認する
duplicate_user.email = @user.email.upcase

```
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
    duplicate_user.email = @user.email.upcase
    @user.save
    assert_not duplicate_user.valid?
  end
end
```


メールアドレスの大文字小文字を無視した一意性の検証
uniqueness: { case_sensitive: false }に置き換える

```
class User < ApplicationRecord
  validates :name,  presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
end
```


・データベースレベルで一意性を保証する
データベース上のemailのカラムにインデックス（index）を追加し、そのインデックスが一意であるようにする

Emailにカラムを追加
```
rails generate migration add_index_to_users_email
```


add_indexというRailsのメソッドを使っています。
インデックス自体は一意性を強制しませんが、オプションでunique: trueを指定することで強制できるようになります。
```
class AddIndexToUsersEmail < ActiveRecord::Migration[7.0]
  def change
    add_index :users, :email, unique: true
  end
end
```

・大文字小文字を区別する
before_saveコールバックにブロックを渡し、downcaseという文字列メソッドでメールアドレスを小文字に変換している
→データベースに保存される直前にすべての文字列を小文字に変換している
```
class User < ApplicationRecord
  before_save { self.email = email.downcase }
  validates :name,  presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: true
end
```

# 6.3セキュアなパスワードを追加する

**・セキュアパスワード**
各ユーザーにパスワードとパスワードの確認を入力させ、それをそのままではなくハッシュ化したものをデータベースに保存する。

# 6.3.1 ハッシュ化されたパスワード

**・has_secure_password**
セキュアなパスワードの実装が行えるRailsのメソッド
* セキュアにハッシュ化したパスワードを、データベース内のpassword_digest属性に保存できるようになる。
* 2つの仮想的な属性（passwordとpassword_confirmation）が使えるようになる。また、存在性と値が一致するかどうかのバリデーションも追加される2。
* authenticateメソッドが使えるようになる（引数の文字列がパスワードと一致するとUserオブジェクトを返し、一致しない場合はfalseを返すメソッド）。

has_secure_password機能を使えるようにするには
モデル内にpassword_digestという属性が含まれている必要がある
↓
```
rails generate migration add_password_digest_to_users password_digest:string
```
でpassword_digest:stringという引数を渡せば指定できる

# 6.3.3 パスワードの最小文字数
パスワードが空でないことと最小文字数（6文字）の2つを設定する

```
require "test_helper"

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com",
                     password: "foobar", password_confirmation: "foobar")
  end
  .
  .
  .
  test "password should be present (nonblank)" do
    @user.password = @user.password_confirmation = " " * 6
    assert_not @user.valid?
  end

  test "password should have a minimum length" do
    @user.password = @user.password_confirmation = "a" * 5
    assert_not @user.valid?
  end
end
```
**・多重代入（Multiple Assignment）**
@user.password = @user.password_confirmation = "a" * 5
パスワードとパスワード確認に対して同時に代入を行っている

**・minimumオプション**
validates :password, length: { minimum: 6 }で
最小文字数のバリデーションを実装できる
```
class User < ApplicationRecord
  before_save { self.email = email.downcase }
  validates :name, presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: true
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }
end
```


# 6.3.4 ユーザーの認証と作成
・Railsコンソールを使ってユーザーを手動で作成する

```
User.create(name: "Michael Hartl", email: "michael@example.com",password: "foobar123456, password_confirmation: "foobar123456”)

User.create(name: "Michael Hartl", email: "michael@example.com",
             password: "foobar", password_confirmation: "foobar")
```

# 感想
今回はユーザーモデルの作成方法とデータの保存の仕方を学ぶことができました。
普段自分がログインする際にデータがどのようにチェックされて保管されているのかが、今回の学習でイメージできるようになりました。
普段何気なく使っている機能にも目を凝らして学習していけたらと思います！