注意:プログラミング歴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
ディレクトリにマイグレーションファイルが生成されています。
rails db:migrate
上記を実行することで、テーブルの作成を行います。
また、このマイグレーションファイルを使ってテーブルへの変更を元に戻す(ロールバックする)ことも可能です。
rails db:rollback
上記を実行すると、テーブルの作成を元に戻すことが可能です。
マイグレーションやロールバックを行うたびにdb
ディレクトリ内のdevelopment.sqlite3
というファイルが更新されます。「DB browser」を利用してデータベースの中身を確認することができます。
https://sqlitebrowser.org/dl/
6.1.3 ユーザーオブジェクトを作成する
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
です。
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
のプロパティは、この段階で決定されます。
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 ユーザーオブジェクトを検索する
User.find(1) #id==1 のオブジェクトを返す
.find()
を使用すると、引数に合致するオブジェクトを返します。
User.find_by(email: "mhartl@example.com")
特定の属性で検索する場合は、.find_by
が有効です。
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 有効性を検証する
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 存在性を検証する
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[]
は配列を作ります。スペースで区切りを指定します。
カンマやクオーテーションを省略して配列を作ることができます。
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
メソッドはオブジェクトや配列などに対して使用すると、対象の型に沿った文字列を返します。
正規表現について
validates :email, format: { with: /<regular expression>/ }
formatというオプションは引数に「正規表現(regex)」をとります。
正規表現は、文字列のマッチングに使うと便利な言語です。
/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
上記はメールアドレスを正規表現で表したものです。
「英数字の組み合わせ」@「英数字の組み合わせ」.「英数字の組み合わせ」
という内容です。
6.2.5 一意性を検証する
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クラスにバリデーションを追加してテストが通るように修正します。
データベースのインデックスを追加する
class AddIndexToUsersEmail < ActiveRecord::Migration[5.0]
def change
add_index :users, :email, unique: true
end
end
add_index
はRailsの関数です。テーブルにインデックスを追加します。
add_index :users, :email, unique: true
第一引数はインデックスを追加するテーブル名(
users
)第二引数はインデックスを追加するカラム名(
email
)第三引数はオプション追加(
unique: true
)
以下のようなオプションを設定できます。
:name #インデックスの名前
:unique #trueを指定するとユニークなインデックス
:length #インデックスに含まれるカラムの長さ
例えば、あるメールアドレスを使ってログインする場合、
- インデックスを使わない場合だと、データベースの隅から隅まで検索をおこなわなければいけません。
* インデックスを貼っておけば、アルファベット順に検索したり、長さ順に検索したりできます。
このチュートリアルでは、ユニークであることを保証するためにインデックスを使っています。
6.32: email属性を小文字に変換してメールアドレスの一意性を保証する
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
で小文字に変換します。
なお、
self.email = self.email.downcase
の右辺self
を省略した書き方になっています。
6.33: リスト 6.32のメールアドレスの小文字化に対するテスト
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
で検証する。
【補足】メールアドレスの検証まとめ
- メールアドレスの長さは255文字を上限とするバリデーション追加
- 正規表現でメールアドレスを表現し、それに合わない文字列はNGとするバリデーション追加
- 大文字・小文字を区別せず、ユニークであるようバリデーション追加
- データベース内でもユニークであることを保証するため、インデックスをモデルに追加
- データベースへの保存の際に、データベースに保存される直前にすべての文字列を小文字に変換し、大文字・小文字の区別をさらに安全にする
6.3.1 ハッシュ化されたパスワード
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 パスワードの最小文字数
多重代入について
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
の2つの属性に同時に代入を行っています。
上のテストは、スペースが6文字続く場合
下のテストは、パスワードが5文字しかない場合
がfalseになるようテストしています。