わたしがRailsチュートリアルで学んだこと【6章】

* 注意：プログラミング歴31日の初心者が書いています

* 注意：間違っていたら優しく教えてください（喜びます）

「Ruby on Rails チュートリアル実例を使ってRailsを学ぼう」
 https://railstutorial.jp/

素晴らしいチュートリアルに感謝します。

#6.1.1 データベースの移行

##リレーショナルデータベースについて

テーブル型になっているデータベース。
よくあるデータベースの形です。
1つの行は1つのIDを示しており、それぞれのIDがnameやemailといったデータを持ちます。


##マイグレーションについて

マイグレーションはデータベースへの追加や変更を行います。

`rails generate model User name:string email:string`でUserモデル（データベース）が自動生成されますが、`user.rb`はまだ空っぽです。
そのかわり、`db/migrate`ディレクトリにマイグレーションファイルが生成されています。

```rb
rails db:migrate
```

上記を実行することで、テーブルの作成を行います。
また、このマイグレーションファイルを使ってテーブルへの変更を元に戻す（ロールバックする）ことも可能です。

```rb
rails db:rollback
```

上記を実行すると、テーブルの作成を元に戻すことが可能です。


マイグレーションやロールバックを行うたびに`db`ディレクトリ内の`development.sqlite3`というファイルが更新されます。「DB browser」を利用してデータベースの中身を確認することができます。
https://sqlitebrowser.org/dl/

#6.1.3 ユーザーオブジェクトを作成する

```rb
User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
```
作成したUserクラスに対して、`.new`メソッドを使用すると、Userオブジェクト（Userインスタンス）を作成できます。
引数なしで呼び出した場合、`id`、`name`、`email`、`created_at`、`updated_at`のプロパティはいずれも`nill`です。

```rb
user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
=> #<User id: nil, name: "Michael Hartl", email: "mhartl@example.com",
created_at: nil, updated_at: nil>
```

引数に`name`と`email`を指定する場合は、上記のように記述します。
ただし、`id`、`created_at`、`updated_at`のプロパティはいずれも`nill`です。


`.new`で作成したオブジェクトに対して`.save`メソッドを使用することで、データベースに保存されます。
`id`、`created_at`、`updated_at`のプロパティは、この段階で決定されます。

```rb
User.create(name: "A Nother", email: "another@example.org")
#<User id: 2, name: "A Nother", email: "another@example.org", created_at:
"2016-05-23 19:18:46", updated_at: "2016-05-23 19:18:46">
```

`.create`メソッドを使用すれば、`.new`と`.save`を同時に行うことができます。

`.destroy`メソッドは、反対にオブジェクトを削除します。

#6.1.4 ユーザーオブジェクトを検索する

```rb
User.find(1) #id==1 のオブジェクトを返す
```
`.find（）`を使用すると、引数に合致するオブジェクトを返します。

```rb
User.find_by(email: "mhartl@example.com")
```

特定の属性で検索する場合は、`.find_by`が有効です。

```rb
 user.email
=> "mhartl@example.net"
 user.email = "foo@bar.com"
=> "foo@bar.com"
 user.reload.email
=> "mhartl@example.net"
```

`.プロパティ名`でそのオブジェクトのプロパティにアクセスし、上書きすることも可能です。
また、`.reload`を実行すると、データベースの情報を元にオブジェクトを再読み込みするので、.save前であれば変更が取り消されたように見えます。
（実際は、保存前のデータをDBから再読み込みしているだけ）


#6.2 ユーザーを検証する

##6.2.1 有効性を検証する

```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
end
```

UserTestクラス内の`setup`メソッドは、テストが実行される直前に実行されます。
ここでは、Userオブジェクトがちゃんと作成できるかを確認するために、`.new`して
`@user`インスタンス変数に格納しています。

"should be valid"と名付けられたテストでは、`.valid?`メソッドで`@user`が有効かどうかを確認します。
`.valid?`メソッドは、後述する「バリデーション」を確認し、オブジェクトにエラーがない場合はtrueが返され、そうでなければfalseが返されます。

ここでは何もバリデーションを設定していないので、`@user.valid?`はtrueになります。

##6.2.2 存在性を検証する

```rb
 test "name should be present" do
    @user.name = "     "
    assert_not @user.valid?
 end
```

まず、`@user.name`にからの文字列を代入しています。名無しです。

`assert_not`は、引数が`failse`もしくは`nill`のとき成功となります。

ここで注意したいのは、あなたはまだバリデーションをなにも設定していないので、名前が空欄だろうがなんだろうが、`.@user.valid?`は`true`だということです。

空欄を認めないバリデーションを設定しない限り、`.@user.valid?`は`true`なので、`assert_not`を含むテストは失敗します。

#6.2.4 フォーマットを検証する

##%w[]について

Rubyの%記法と呼ばれているものです。
以下にまとまっています。
『Rubyで%記法（パーセント記法）を使う』　@mogulla3
https://qiita.com/mogulla3/items/46bb876391be07921743

`%w[]`は配列を作ります。スペースで区切りを指定します。
カンマやクオーテーションを省略して配列を作ることができます。


```rb
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
```

このテストでは、

* まず、`valid_address`に有効と思われるメールアドレスの例を配列として定義します。

* 次に、`.each`メソッドで配列の中身をひとつずつ取り出してassertにかけます。

* assertメソッドの第2引数は、エラーメッセージになっています。assartが失敗した際に呼び出されます。

ちなみに、`.inspect`メソッドはオブジェクトや配列などに対して使用すると、対象の型に沿った文字列を返します。


##正規表現について


```rb
validates :email, format: { with: /<regular expression>/ }
```
formatというオプションは引数に「正規表現（regex）」をとります。
正規表現は、文字列のマッチングに使うと便利な言語です。

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

上記はメールアドレスを正規表現で表したものです。
「英数字の組み合わせ」＠「英数字の組み合わせ」.「英数字の組み合わせ」

という内容です。


#6.2.5 一意性を検証する

```rb
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    @user.save
    assert_not duplicate_user.valid?
  end
```

`@user.dup`の`.dup`メソッドはオブジェクトのコピーを作成して返すメソッドです。
@userのコピーを作成し、変数`duplicate_user`に格納しています。

`@user`と同じ情報を持つオブジェクトは、作成できてはなりません。

`duplicate_user.valid?`が`true`つまり有効なメールアドレスとして判定されると、テストは失敗します。

`@user`と同じ情報を持つオブジェクトの作成を許可しないよう、Userクラスにバリデーションを追加してテストが通るように修正します。


##データベースのインデックスを追加する

```rb
class AddIndexToUsersEmail < ActiveRecord::Migration[5.0]
  def change
    add_index :users, :email, unique: true
  end
end
```

`add_index`はRailsの関数です。テーブルにインデックスを追加します。

```rb
add_index :users, :email, unique: true
```

* 第一引数はインデックスを追加するテーブル名（`users`）

* 第二引数はインデックスを追加するカラム名（`email`）

* 第三引数はオプション追加（`unique: true`）

以下のようなオプションを設定できます。

```rb
:name #インデックスの名前
:unique #trueを指定するとユニークなインデックス
:length #インデックスに含まれるカラムの長さ
```

例えば、あるメールアドレスを使ってログインする場合、

* インデックスを使わない場合だと、データベースの隅から隅まで検索をおこなわなければいけません。

*　インデックスを貼っておけば、アルファベット順に検索したり、長さ順に検索したりできます。


このチュートリアルでは、ユニークであることを保証するためにインデックスを使っています。


###6.32: email属性を小文字に変換してメールアドレスの一意性を保証する

```rb
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: { case_sensitive: false }
end
```


`before_save`はデータベースへの保存前に、引数の関数（コールバック関数）を実行します。

`before_save`は`new`されるたびに起動するので、`self`は`new`したインスタンス自身です。自分自身のメールアドレスに対して`.downcase`で小文字に変換します。

なお、

```rb
self.email = self.email.downcase
```
の右辺`self`を省略した書き方になっています。


###6.33: リスト 6.32のメールアドレスの小文字化に対するテスト

```rb
  test "email addresses should be saved as lower-case" do
    mixed_case_email = "Foo@ExAMPle.CoM"
    @user.email = mixed_case_email
    @user.save
    assert_equal mixed_case_email.downcase, @user.reload.email
  end
```

* まず、テスト用に`"Foo@ExAMPle.CoM"`大文字小文字を含むアドレスを用意

* `@user.email`に上書きし、`.save`でデータベースに保存する...けど、

* でも、データベースへの保存の前に、Userクラスの`before_save { self.email = email.downcase }`が起動。小文字にしてから`@user`は保存される。

* 「`"Foo@ExAMPle.CoM"`に`.downcase`を使用した文字列」と、「データベースに保存された文字列」が同じかどうか`assert_equal`で検証する。


#【補足】メールアドレスの検証まとめ

1. メールアドレスの長さは255文字を上限とするバリデーション追加
2. 正規表現でメールアドレスを表現し、それに合わない文字列はNGとするバリデーション追加
3. 大文字・小文字を区別せず、ユニークであるようバリデーション追加
4. データベース内でもユニークであることを保証するため、インデックスをモデルに追加
5. データベースへの保存の際に、データベースに保存される直前にすべての文字列を小文字に変換し、大文字・小文字の区別をさらに安全にする


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

```rb
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: { case_sensitive: false }
  has_secure_password #これ
end
```

Railsにおいてパスワードの実装は、以下をまず実装する。

* データベースに`password_digest`カラムを追加する

* モデルに`has_secure_password`を追加する

* bcryptをGemfileに追加する（よりセキュアに）

* テストコード内の仮想インスタンスのプロパティに`password: "文字列", password_confirmation: "文字列"`を追加する（`has_secure_password`によって仮想属性が追加されているため）

#6.3.3 パスワードの最小文字数

##多重代入について

```rb
  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
```

`password`と`password_confirmation`の２つの属性に同時に代入を行っています。

* 上のテストは、スペースが６文字続く場合

* 下のテストは、パスワードが５文字しかない場合

がfalseになるようテストしています。