railsチュートリアル挑戦記 第6章 ユーザーのモデルを作成する
railsチュートリアルをやりながらメモしたことをそのまま記述しています。
・6 1 Userモデル
前章ではユーザーを作成するためのスタブページを作成した
本章ではユーザーのデータモデルの作成と、データベースに保存する手段の確保を行う
次章ではユーザーがサイトにユーザー登録できるようにし、ユーザープロフィールのためのページを作成する
8章と9章でログインとログアウトの仕組みを作る
10章で不正なアクセスを取り扱う方法を学ぶ
11章と12章でメールアドレスを使ってアカウントを有効化する方法とパスワードを再設定する方法を学ぶ
認証 (authentication) と認可 (authorization) のシステムの例だと、Clearance、Authlogic、Devise、CanCanなどがある
データベースとやりとりをするデフォルトのRailsライブラリはActive Recordと呼ぶ
SQLのDDL (Data Definition Language) を新たに学ぶ必要はない
開発環境ではSQLiteを使い、
本番環境(Heroku)ではPostgreSQLを使う
トピックブランチを作成する
git checkout -b modeling-users
・6 1 1 データベースの移行
ユーザーモデルを作ろう
rails generate model User name:string email:string
ログ
----------------------------
-users) $ rails generate model User name:string email:string
Running via Spring preloader in process 6546
invoke active_record
create db/migrate/20200101042458_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
4つのファイルが作られているっぽい
このとき、コントローラは複数形を使い、モデルは単数形を使うことを必ず覚える。
さっきのコマンドで作られる1つのファイルは、マイグレーションファイル
(usersテーブルを作るための) Userモデルのマイグレーション
db/migrate/[timestamp]_create_users.rb
----------------------------
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
----------------------------
[timestamp]のところは日時が追加される
以前は1ずつ増えてく整数がつけられてたけど複数の開発者によるチームでの作業時に
同じ整数を持つマイグレーションファイルがよく作られちゃうため、ファイル名が衝突しちゃうことがあった
マイグレーションはchangeメソッドの集まり
changeメソッドはcreate_tableというRailsのメソッドを呼ぶ
t.timestampsはマジックカラムと呼ばれる特別なコマンド
rails db:migrateは、マイグレーションファイルを用いてマイグレーションの適用を行う
ログ
----------------------------
-users) $ rails db:migrate
== 20200101042458 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0024s
== 20200101042458 CreateUsers: migrated (0.0029s) =============================
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
あれ?rails db:migrate前はdb/schema.rbがなかったけどrails db:migrateで作成された!
ログではそのこと確認できないけどこれは覚えておこう!
db/development.sqlite3という名前のファイルが生成される
development.sqlite3は、
DB Browser for SQLiteという素晴らしいツールを使うと、データベースの構造を見ることができる
http://sqlitebrowser.org/
DB Browser for SQLiteをインストールした。
開発環境構築備忘録 DB Browser for SQLite編
https://note.com/el93019205/n/n5b8572c1fb7e
rails db:rollbackを試してみる
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails db:rollback
== 20200101042458 CreateUsers: reverting ======================================
-- drop_table(:users)
-> 0.0028s
== 20200101042458 CreateUsers: reverted (0.0064s) =============================
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
schemaの中身が消えた
もう一度rails db:migrateで元に戻しておく
rails db:migrateでは、データベースからusersテーブルを削除するためにdrop_tableコマンドを内部で呼び出している。
これがうまくいくのは、drop_tableとcreate_tableがそれぞれ対応していることをchangeメソッドが知っているため。
この対応関係を知っているため、ロールバック用の逆方向のマイグレーションを簡単に実現することができる。
あるカラムを削除するような不可逆なマイグレーションの場合は、changeメソッドのかわりにupとdownのメソッドを別々に定義する必要がある。
詳細は下記にあるとのこと。
https://railsguides.jp/active_record_migrations.html
・6 1 2 modelファイル
ApplicationRecordはActiveRecord::Baseを継承している
・6 1 3 ユーザーオブジェクトを作成する
データベースを変更したくないときは、railsコンソールを立ち上げる時に下記のようにする
rails console --sandbox
サンドボックスは基本情報か応用情報で出てきた言葉だ!
ここから、railsコンソール上で作業する
User.newの結果を見てみる
----------------------------
>> User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
----------------------------
userを作ってみる
----------------------------
>> 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>
----------------------------
userが存在するか確認してみる
----------------------------
>> user.valid?
true
----------------------------
userをデータベースに保存してみる
ログ
----------------------------
>> user.save
(0.1ms) SAVEPOINT active_record_1
SQL (2.3ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Michael Hartl"], ["email", "mhartl@example.com"], ["created_at", "2020-01-01 05:47:10.617824"], ["updated_at", "2020-01-01 05:47:10.617824"]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> true
----------------------------
user.saveに対応するSQLも表示されてる!
userを表示してみる
----------------------------
>> user
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-01-01 05:47:10", updated_at: "2020-01-01 05:47:10">
>>
----------------------------
セーブした時点で、idとか時間が更新されてる!
user.createという作成と保存を一気にやってしまう方法もある
----------------------------
>> User.create(name: "A Nother", email: "another@example.org")
(0.1ms) SAVEPOINT active_record_1
SQL (0.2ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "A Nother"], ["email", "another@example.org"], ["created_at", "2020-01-01 05:53:47.426527"], ["updated_at", "2020-01-01 05:53:47.426527"]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2020-01-01 05:53:47", updated_at: "2020-01-01 05:53:47">
>> >> foo = User.create(name: "Foo", email: "foo@bar.com")
Traceback (most recent call last):
SyntaxError ((irb):5: syntax error, unexpected >>)
>> foo = User.create(name: "Foo...
^~
>> foo = User.create(name: "Foo", email: "foo@bar.com")
(0.1ms) SAVEPOINT active_record_1
SQL (0.1ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Foo"], ["email", "foo@bar.com"], ["created_at", "2020-01-01 05:54:31.296984"], ["updated_at", "2020-01-01 05:54:31.296984"]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2020-01-01 05:54:31", updated_at: "2020-01-01 05:54:31">
>>
----------------------------
データベースから削除する
----------------------------
>> foo.destroy
(0.1ms) SAVEPOINT active_record_1
SQL (0.3ms) 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: "2020-01-01 05:54:31", updated_at: "2020-01-01 05:54:31">
>>
----------------------------
ただ、railsコンソール上にはまだ残ってる
----------------------------
>> foo
=> #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2020-01-01 05:54:31", updated_at: "2020-01-01 05:54:31">
>>
----------------------------
>> user.name.class
=> String
>> user.email.class
=> String
>> user.created_at.class
=> ActiveSupport::TimeWithZone
>> user.updated_at.class
=> ActiveSupport::TimeWithZone
>> user.updated_at.class.superclass
=> Object
>> user.updated_at.class.superclass.superclass
=> BasicObject
>> user.updated_at.class.superclass.superclass.superclass
=> nil
>>
・6 1 4 ユーザーオブジェクトを検索する
データベースに保存されているかどうかを確認する
User.find(〇)を使う
----------------------------
>> User.find(0)
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 0], ["LIMIT", 1]]
Traceback (most recent call last):
1: from (irb):16
ActiveRecord::RecordNotFound (Couldn't find User with 'id'=0)
>> User.find(1)
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-01-01 05:47:10", updated_at: "2020-01-01 05:47:10">
>> User.find(2)
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
=> #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2020-01-01 05:53:47", updated_at: "2020-01-01 05:53:47">
>> User.find(3)
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
Traceback (most recent call last):
1: from (irb):19
ActiveRecord::RecordNotFound (Couldn't find User with 'id'=3)
>>
----------------------------
User.find(0)は作られてないみたい
User.find(3)は一度作ったはずだから消されてることが確認できた!
メールアドレスで確認する方法
ログ
----------------------------
>> User.find_by(email: "mhartl@example.com")
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "mhartl@example.com"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-01-01 05:47:10", updated_at: "2020-01-01 05:47:10">
>>
----------------------------
データベースから最初の人を取り出す
----------------------------
>> User.first
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-01-01 05:47:10", updated_at: "2020-01-01 05:47:10">
>>
----------------------------
データベースから全員を取り出す
----------------------------
>> User.all
User Load (0.2ms) SELECT "users".* FROM "users" LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-01-01 05:47:10", updated_at: "2020-01-01 05:47:10">, #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2020-01-01 05:53:47", updated_at: "2020-01-01 05:53:47">]>
>>
----------------------------
User.allで返ってくるのはArrayクラスではなくUser::ActiveRecord_Relationクラス
・6 1 5 ユーザーオブジェクトを更新する
----------------------------
>> user
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-01-01 05:47:10", updated_at: "2020-01-01 05:47:10">
>> user.email = "tekitou@tekitou.com"
=> "tekitou@tekitou.com"
>> user.save
(0.1ms) SAVEPOINT active_record_1
SQL (0.1ms) UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ? [["email", "tekitou@tekitou.com"], ["updated_at", "2020-01-01 06:28:20.571764"], ["id", 1]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> true
>> User.all
User Load (0.1ms) SELECT "users".* FROM "users" LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, name: "Michael Hartl", email: "tekitou@tekitou.com", created_at: "2020-01-01 05:47:10", updated_at: "2020-01-01 06:28:20">, #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2020-01-01 05:53:47", updated_at: "2020-01-01 05:53:47">]>
>>
----------------------------
saveしないと保存されない
ユーザーを再読み込みしたあとのemailを確認
user.reload.emailで確認できる
user.saveで更新日時も更新される
更新と保存を一気にやるコマンド
user.update_attributes(name: "The Dude", email: "dude@abides.org")
user.update_attributes(name: "El Duderino")
実は作成日時とか更新日時も更新できる
user.update_attributes(created_at: 1.year.ago)
※saveでは無理よ!
・6 2 ユーザーを検証する
Validation機能を使うと属性の値を制限できる
・6 2 1 有効性を検証する
デフォルトのUserテスト (モックのみ)
----------------------------
test/models/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
----------------------------
有効なオブジェクトに対してテストを書くために、setupという特殊なメソッドを使って有効なUserオブジェクト (@user) を作成する
有効なUserかどうかをテストする green
test/models/user_test.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
----------------------------
@user.valid?がtrueを返すと成功し、falseを返すと失敗
しかし、Userモデルにはまだバリデーションがないため、このテストは成功する
rails test:models
モデルに関するテストだけを走らせるコマンド
ログ
----------------------------
ec2-user:~/environment $ cd ~/environment/sample_app
ec2-user:~/environment/sample_app (modeling-users) $ rails test:models
Started with run options --seed 63517
1/1: [==========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.03391s
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
・6 2 2 存在性を検証する
空白が入ってしまうとエラーになる
name属性にバリデーションに対するテスト red
test/models/user_test.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
end
----------------------------
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test:models
Started with run options --seed 61844
FAIL["test_name_should_be_present", UserTest, 0.03449173200351652]
test_name_should_be_present#UserTest (0.03s)
Expected true to be nil or false
test/models/user_test.rb:14:in `block in <class:UserTest>'
2/2: [==========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.03989s
2 tests, 2 assertions, 1 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
空白が入らないようにバリデーションを追加する
name属性の存在性を検証するgreen
app/models/user.rb
----------------------------
class User < ApplicationRecord
validates :name, presence: true
end
----------------------------
そうすればさっき空白を入れようとしても弾かれてるのでエラーにならない
ログ
----------------------------
>> user = User.new(name: "", email: "mhartl@example.com")
=> #<User id: nil, name: "", email: "mhartl@example.com", created_at: nil, updated_at: nil>
>> user.valid?
=> false
>> user.errors.full_messages
=> ["Name can't be blank"]
>>
>> user.save
(0.1ms) SAVEPOINT active_record_1
(0.1ms) ROLLBACK TO SAVEPOINT active_record_1
=> false
----------------------------
user.errors.full_messagesで、なんでエラーになったのか見れる
saveしようとしてもセーブできないことを確認
ここまでやれば、rails test:modelsもうまくいく
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test:models
Started with run options --seed 9440
2/2: [==========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.04500s
2 tests, 2 assertions, 0 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
emailも同様のことをやってみる
email属性の検証に対するテストred
test/models/user_test.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
end
----------------------------
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test:models
Started with run options --seed 5566
FAIL["test_email_should_be_present", UserTest, 0.053829284999665106]
test_email_should_be_present#UserTest (0.05s)
Expected true to be nil or false
test/models/user_test.rb:18:in `block in <class:UserTest>'
3/3: [==========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.06471s
3 tests, 3 assertions, 1 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
email属性の存在性を検証するgreen
app/models/user.rb
----------------------------
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true
end
----------------------------
ここまでやれば、rails testも成功するはず
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test
Running via Spring preloader in process 19568
Started with run options --seed 10304
11/11: [========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.42679s
11 tests, 22 assertions, 0 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
イイネ!
演習1でエラー全てを表示し、演習2でemailのエラーのみ表示するやり方に挑戦。めっちゃミスった。
ログ
----------------------------
>> u = User.new(name:"test",email:"test")
=> #<User id: nil, name: "test", email: "test", created_at: nil, updated_at: nil>
>> u.valid?
=> true
>> u.errors.full_messages
=> []
>>
(0.1ms) rollback transaction
ec2-user:~/environment/sample_app (modeling-users) $ rails console --sandbox
Running via Spring preloader in process 19661
Loading development environment in sandbox (Rails 5.1.6)
Any modifications you make will be rolled back on exit
>> u = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
>> user.valid?
Traceback (most recent call last):
1: from (irb):2
NameError (undefined local variable or method `user' for main:Object)
Did you mean? super
>> u.valid?
=> false
>> u.errors.messages
=> {:name=>["can't be blank"], :email=>["can't be blank"]}
>> u.errors.messages.email
Traceback (most recent call last):
1: from (irb):5
NoMethodError (undefined method `email' for {:name=>["can't be blank"], :email=>["can't be blank"]}:Hash)
>> u.errors.messages(:email)
Traceback (most recent call last):
2: from (irb):6
1: from (irb):6:in `rescue in irb_binding'
ArgumentError (wrong number of arguments (given 1, expected 0))
>> u.erroes.messages[:email]
Traceback (most recent call last):
2: from (irb):7
1: from (irb):7:in `rescue in irb_binding'
NoMethodError (undefined method `erroes' for #<User:0x0000000004e15f88>)
Did you mean? errors
>> u.errors.messages
=> {:name=>["can't be blank"], :email=>["can't be blank"]}
>> u.errors.messages
=> {:name=>["can't be blank"], :email=>["can't be blank"]}
>> u.errors.messages[:email]
=> ["can't be blank"]
>>
----------------------------
・6 2 3 長さを検証する
まずはテストを書く
名前は50文字まで、emailは255文字まで
nameの長さの検証に対するテスト red
test/models/user_test.rb
----------------------------
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
@user = User.new(name: "Example User", email: "user@example.com")
end
.
.
.
test "name should not be too long" do
@user.name = "a" * 51
assert_not @user.valid?
end
test "email should not be too long" do
@user.email = "a" * 244 + "@example.com"
assert_not @user.valid?
end
end
----------------------------
この時点でテストするとエラーになるはず!
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test
Running via Spring preloader in process 20386
Started with run options --seed 49328
FAIL["test_name_should_not_be_too_long", UserTest, 0.41760054399856017]
test_name_should_not_be_too_long#UserTest (0.42s)
Expected true to be nil or false
test/models/user_test.rb:23:in `block in <class:UserTest>'
FAIL["test_email_should_not_be_too_long", UserTest, 0.42015557999911834]
test_email_should_not_be_too_long#UserTest (0.42s)
Expected true to be nil or false
test/models/user_test.rb:28:in `block in <class:UserTest>'
13/13: [========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.45045s
13 tests, 24 assertions, 2 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
それじゃ、バリデーションを追加しよう!
name属性に長さの検証を追加するgreen
app/models/user.rb
----------------------------
class User < ApplicationRecord
validates :name, presence: true, length: { maximum: 50 }
validates :email, presence: true, length: { maximum: 255 }
end
----------------------------
そして、rails test
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test
Running via Spring preloader in process 20507
Started with run options --seed 39779
13/13: [========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.45370s
13 tests, 24 assertions, 0 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
イイネ!
以下演習
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails console --sandbox
Running via Spring preloader in process 20602
Loading development environment in sandbox (Rails 5.1.6)
Any modifications you make will be rolled back on exit
>> u = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
>> u[:name]
=> nil
>> u[:name]="a"*30
=> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
>> u[:name]
=> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
>> u.errors.messages
=> {}
>> u[:name]="a"*300
=> "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
>> u.errors.messages
=> {}
>> u.save
(0.1ms) SAVEPOINT active_record_1
(0.1ms) ROLLBACK TO SAVEPOINT active_record_1
=> false
>> u.errors.messages
=> {:name=>["is too long (maximum is 50 characters)"], :email=>["can't be blank"]}
>> u.errors.messages[:name]
=> ["is too long (maximum is 50 characters)"]
>> u.errors.messages[:email]
=> ["can't be blank"]
>>
----------------------------
・6 2 4 フォーマットを検証する
文字列の配列を作る方法復習!
%w[foo bar baz]
これは["foo","bar","baz"]という文字列を作る!
メールアドレスのバリデーションはムズい
まずは
有効なメールフォーマットをテストするgreen
test/models/user_test.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
end
----------------------------
assert @user.valid?, "#{valid_address.inspect} should be valid"
この行は、どのメールアドレスがダメだったのかが分かるようになってる
次に、打ち間違いをエラーにするようにする
メールフォーマットの検証に対するテスト red
test/models/user_test.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
end
----------------------------
rails testでエラーになるはず
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test
Running via Spring preloader in process 22679
Started with run options --seed 2042
FAIL["test_email_validation_should_reject_invalid_addresses", UserTest, 0.030785800001467578]
test_email_validation_should_reject_invalid_addresses#UserTest (0.03s)
"user@example,com" should be invalid
test/models/user_test.rb:45:in `block (2 levels) in <class:UserTest>'
test/models/user_test.rb:43:in `each'
test/models/user_test.rb:43:in `block in <class:UserTest>'
15/15: [========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.46033s
15 tests, 30 assertions, 1 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
メールアドレスのフォーマットを検証するためには、次のようにformatというオプションを使う
validates :email, format: { with: /<regular expression>/ }
実用的で堅牢であることが実践で保証されている正規表現がこちら
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
順番にかみ砕くと以下の通り
/正規表現の開始
\A文字列の先頭が
[\w+\-.]+英数字、アンダースコア、プラス、ハイフン、ドットのいずれかを少なくとも1文字以上繰り返し
@ここは必ずアットマークになっており
[a-z\d\-.]+英小文字、数字、ハイフン、ドットのいずれかを少なくとも1文字以上繰り返し
\.ここは必ずドット
[a-z]+英小文字を少なくとも1文字以上繰り返し
\z文字列の末尾があり
/正規表現がここで終わり
i大文字小文字を無視するオプション
正規表現を理解するのに早いサイト
https://rubular.com/
メールフォーマットを正規表現で検証するgreen
app/models/user.rb
----------------------------
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 }
end
----------------------------
英大文字で始まる名前はrubyでは定数を意味する
残る制約は、メールアドレスが一意であることを強制することだけ
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test
Running via Spring preloader in process 22747
Started with run options --seed 16566
15/15: [========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.45501s
15 tests, 34 assertions, 0 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
より堅牢な正規表現
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
テスト成功
・6 2 5 一意性を検証する
メールアドレスの一意性を強制するために、validatesメソッド:uniqueオプションを使用する
重複するメールアドレス拒否のテスト red
test/models/user_test.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
@user.save
assert_not duplicate_user.valid?
end
end
----------------------------
テストで失敗になることを確認
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test
Running via Spring preloader in process 23509
Started with run options --seed 37290
FAIL["test_email_addresses_should_be_unique", UserTest, 0.4261229200055823]
test_email_addresses_should_be_unique#UserTest (0.43s)
Expected true to be nil or false
test/models/user_test.rb:52:in `block in <class:UserTest>'
16/16: [========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.45621s
16 tests, 36 assertions, 1 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
大文字小文字を区別しない一意性のテストをしなければならない
大文字小文字を区別しない、一意性のテスト red
test/models/user_test.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
duplicate_user.email = @user.email.upcase
@user.save
assert_not duplicate_user.valid?
end
end
----------------------------
メールアドレスの大文字小文字を無視した一意性の検証 green
app/models/user.rb
----------------------------
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
----------------------------
rails testで成功することを確認
ここまでやったことは、あくまでもアプリケーションレベルで一意性を強制しただけであるため、素早く2回連続でクリックしたなどのデータベースレベルでは一意性を強制したことにならない。
なので、データベースレベルでも一意性を強制する必要がある
と、いうわけでマイグレーションファイルのみをつくろー!
rails generate migration add_index_to_users_email
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails generate migration add_index_to_users_email
Running via Spring preloader in process 24264
invoke active_record
create db/migrate/20200101133834_add_index_to_users_email.rb
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
db migrateに新しいファイルができると思ったら予想通りだった!
イイネ!
メールアドレスの一意性を強制するためのマイグレーション
db/migrate/[timestamp]_add_index_to_users_email.rb
----------------------------
class AddIndexToUsersEmail < ActiveRecord::Migration[5.0]
def change
add_index :users, :email, unique: true
end
end
----------------------------
schema.rbの修正前後を見てみよう!
修正前
----------------------------
ActiveRecord::Schema.define(version: 20200101042458) do
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
----------------------------
rails db:migrate
修正後
----------------------------
ActiveRecord::Schema.define(version: 20200101133834) do
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
----------------------------
あれ、変わってねえなあんまり・・・
他の部分が変わったか?
test/fixtures/users.ymlは今は消しておく
(でも消す前からテスト大丈夫だったんだよなぁ・・・まぁいいか
一応サンドボックス終了した状態でやってみたけど変わらず)
データベースによって、メールアドレスの大文字と小文字を区別せずにバリデーションが効かないという問題がある
これを、before_saveというコールバックで解決する
email属性を小文字に変換してメールアドレスの一意性を保証する green
app/models/user.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
----------------------------
・・・なぜこれでデータベースレベルで一意性を保てるんだろう・・・?
データの流れが分からないなぁ・・・。
ちなみにemail.downcaseの部分はself.email.downcaseの略
Userモデルの中では右式のselfを省略できる
更ちなでemail = email.downcaseは不可能
テストコードで、データベースの値に合わせて更新するreloadメソッドと
値が一致しているかどうか確認するassert_equalメソッドを使う
メールアドレスの小文字化に対するテスト
test/models/user_test.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
duplicate_user.email = @user.email.upcase
@user.save
assert_not duplicate_user.valid?
end
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
end
----------------------------
before_saveをコメントアウトしてテストしたときのログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test
Running via Spring preloader in process 25667
Started with run options --seed 32339
FAIL["test_email_addresses_should_be_saved_as_lower-case", UserTest, 0.45747772799950326]
test_email_addresses_should_be_saved_as_lower-case#UserTest (0.46s)
Expected: "foo@example.com"
Actual: "Foo@ExAMPle.CoM"
test/models/user_test.rb:60:in `block in <class:UserTest>'
17/17: [========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.49291s
17 tests, 37 assertions, 1 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
before_saveのコメントアウトを外してテストしたときのログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test
Running via Spring preloader in process 25692
Started with run options --seed 63158
17/17: [========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.47327s
17 tests, 37 assertions, 0 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
実は、 self.email = email.downcaseとemail.downcase!は同じ動作をする。なぜなら!は直接データを弄れるためである。
・6 3 セキュアなパスワードを追加する
ハッシュ使うよって言われてる
ユーザーの認証は下記ステップ
パスワードを送信する
ハッシュ化する
データベース内のハッシュ化された値と比較
ハッシュ化する理由は、データベースの値を盗む不届き者がいてものぞき見されることがないため
・6 3 1 ハッシュ化されたパスワード
ハッシュ化は下記で終わり(ちゃんちゃん)
----------------------------
class User < ApplicationRecord
.
.
.
has_secure_password
end
----------------------------
ちゃんちゃん。
これにより、以下の機能が使える
データベース内のpassword_digestという属性にハッシュ化したパスワードが保存される
password属性とpassword_confirmation属性が使えるようになる
存在性チェックバリデーションも追加
値一致チェックバリデーションも追加
引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalseを返すauthenticateメソッドが利用可能
has_secure_password機能を使えるようにするには一つ条件がある。
password_digest属性があること。
今回はUserモデルにそれを追加する
rails generate migration add_password_digest_to_users password_digest:string
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails generate migration add_password_digest_to_users password_digest:string
Running via Spring preloader in process 27925
invoke active_record
create db/migrate/20200101152629_add_password_digest_to_users.rb
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
ん、Progateでもやった気がするけど引数が2つあるな
add_〇〇_to_〇〇の形にしておくと勝手に右丸のテーブルに左丸が追加されてオトク感があるとのこと
型も指定する場合は2つ目の引数で指定できるっぽい
password_digestカラムを追加するマイグレーション
db/migrate/[timestamp]_add_password_digest_to_users.rb
----------------------------
class AddPasswordDigestToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :password_digest, :string
end
end
----------------------------
そしてデータベースを更新するコマンド
rails db:migrate
おおー、schema.rbにpassword_digestが追加された
ハッシュ関数を使えるようにするためにGemfileにbcryptが必要らしい
先に言ってくれよ~~~
gem 'bcrypt', '3.1.12'
そしてbundle installって言われたけどこれ--without productionが実行された前提のコマンドだよね・・・?
まあいいや今回は素直に実行する
ログが長いので割愛
素直に追加されたっぽい
・6 3 2 ユーザーがセキュアなパスワードを持っている
このままだとテスト失敗するらしい
rails test
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test
Running via Spring preloader in process 28242
Started with run options --seed 22432
FAIL["test_email_validation_should_accept_valid_addresses", UserTest, 0.4560623500001384]
test_email_validation_should_accept_valid_addresses#UserTest (0.46s)
"user@example.com" should be valid
test/models/user_test.rb:36:in `block (2 levels) in <class:UserTest>'
test/models/user_test.rb:34:in `each'
test/models/user_test.rb:34:in `block in <class:UserTest>'
FAIL["test_should_be_valid", UserTest, 0.46261816500191344]
test_should_be_valid#UserTest (0.46s)
Expected false to be truthy.
test/models/user_test.rb:10:in `block in <class:UserTest>'
ERROR["test_email_addresses_should_be_saved_as_lower-case", UserTest, 0.46722190900618443]
test_email_addresses_should_be_saved_as_lower-case#UserTest (0.47s)
ActiveRecord::RecordNotFound: ActiveRecord::RecordNotFound: Couldn't find User without an ID
test/models/user_test.rb:60:in `block in <class:UserTest>'
17/17: [========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.50778s
17 tests, 32 assertions, 2 failures, 1 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
ホンマや
しかもなんか色々出てる
それはセットアップメソッドでいろいろと情報が足りてないんじゃ
色々追加するぞ!
パスワードとパスワード確認を追加する green
test/models/user_test.rb
----------------------------
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
.
.
.
end
----------------------------
ここでrails test
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $ rails test
Running via Spring preloader in process 28415
Started with run options --seed 15193
17/17: [========================] 100% Time: 00:00:00, Time: 00:00:00
Finished in 0.51630s
17 tests, 37 assertions, 0 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (modeling-users) $
----------------------------
ふぃ~上手く行った~安心~
演習1
この時点では、userオブジェクトに有効な名前とメールアドレスを与えても、valid?で失敗してしまうことを確認してみてください。
演習2
なぜ失敗してしまうのでしょうか? エラーメッセージを確認してみてください。
ログ
----------------------------
>> u = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil>
>> u.name="a"
=> "a"
>> u.valid?
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" IS NULL LIMIT ? [["LIMIT", 1]]
=> false
>> u.email="a@a.a"
=> "a@a.a"
>> u.valid?
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "a@a.a"], ["LIMIT", 1]]
=> false
>> u.save
(0.1ms) SAVEPOINT active_record_1
User Exists (0.1ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "a@a.a"], ["LIMIT", 1]]
(0.1ms) ROLLBACK TO SAVEPOINT active_record_1
=> false
>> u.errors.messages
=> {:password=>["can't be blank"]}
>>
----------------------------
パスワードがないから?
・6 3 3 パスワードの最小文字数
パスワードが6文字以上であることを検証する
パスワードの最小文字数をテストする red
test/models/user_test.rb
----------------------------
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
----------------------------
セキュアパスワードの完全な実装 green
app/models/user.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
validates :password, presence: true, length: { minimum: 6 }
end
----------------------------
テストはOKになることを確認
演習
有効な名前とメールアドレスでも、パスワードが短すぎるとuserオブジェクトが有効にならないことを確認してみましょう。
上で失敗した時、どんなエラーメッセージになるでしょうか? 確認してみましょう。
ログ
----------------------------
>> u = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil>
>> u.name="a"
=> "a"
>> u.email="a@a.a"
=> "a@a.a"
>> u.valid?
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "a@a.a"], ["LIMIT", 1]]
=> false
>> u.password="a"
=> "a"
>> u.valid?
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "a@a.a"], ["LIMIT", 1]]
=> false
>> u.password="abcdef"
=> "abcdef"
>> u.valid?
User Exists (0.1ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "a@a.a"], ["LIMIT", 1]]
=> true
>>
----------------------------
・6 3 4 ユーザーの作成と認証
とりあえずデータベースに一人追加しておく
今度はrails consoleでサンドボックスでないパターン
操作は要注意ね
User.create(name: "Michael Hartl", email: "mhartl@example.com",
password: "foobar", password_confirmation: "foobar")
ログ
----------------------------
ec2-user:~/environment/sample_app (modeling-users) $
ec2-user:~/environment/sample_app (modeling-users) $ rails console
Running via Spring preloader in process 29322
Loading development environment (Rails 5.1.6)
>> User.create(name: "Michael Hartl", email: "mhartl@example.com",
?> password: "foobar", password_confirmation: "foobar")
(0.1ms) begin transaction
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "mhartl@example.com"], ["LIMIT", 1]]
SQL (2.0ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES (?, ?, ?, ?, ?) [["name", "Michael Hartl"], ["email", "mhartl@example.com"], ["created_at", "2020-01-01 15:55:01.546006"], ["updated_at", "2020-01-01 15:55:01.546006"], ["password_digest", "$2a$10$3bYK77wXYp0SyMoA7FfsHOsuPabfPoNF3ivprqMe.Q715LPQuLTdq"]]
(6.8ms) commit transaction
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-01-01 15:55:01", updated_at: "2020-01-01 15:55:01", password_digest: "$2a$10$3bYK77wXYp0SyMoA7FfsHOsuPabfPoNF3ivprqMe.Q7...">
>>
----------------------------
今度はdevelopment.sqlite3をダウンロードしてDBブラウザで内容を確認
どこに表示されてるか分からないかったけどBrowseDataタブがあってそこで見ることができた!
と思ったら見れてなかった
左上らへんにあるTableのリストボックスをusersに変えたら見れた
ログ
----------------------------
>> u.password
Traceback (most recent call last):
2: from (irb):4
1: from (irb):4:in `rescue in irb_binding'
NameError (undefined local variable or method `u' for main:Object)
>> User.find(1).password
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> nil
>> User.find(1).password_digest
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> "$2a$10$3bYK77wXYp0SyMoA7FfsHOsuPabfPoNF3ivprqMe.Q715LPQuLTdq"
>>
----------------------------
ログ
----------------------------
>> u=User.find(1)
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-01-01 15:55:01", updated_at: "2020-01-01 15:55:01", password_digest: "$2a$10$3bYK77wXYp0SyMoA7FfsHOsuPabfPoNF3ivprqMe.Q7...">
>> u
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-01-01 15:55:01", updated_at: "2020-01-01 15:55:01", password_digest: "$2a$10$3bYK77wXYp0SyMoA7FfsHOsuPabfPoNF3ivprqMe.Q7...">
>> u.authenticate("not_the_right_password")
=> false
>> u.authenticate("foobaz")
=> false
>> u.authenticate("foobar")
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2020-01-01 15:55:01", updated_at: "2020-01-01 15:55:01", password_digest: "$2a$10$3bYK77wXYp0SyMoA7FfsHOsuPabfPoNF3ivprqMe.Q7...">
>>
>> !!u.authenticate("foobar")
=> true
----------------------------
・6 4 最後に
gitとherokuにpushしておこう
できた!
https://jun6.herokuapp.com/
でも特に見た目変わってる訳ではないからパッとしない(笑)
・6 4 1 本章のまとめ
マイグレーションを使うことで、アプリケーションのデータモデルを修正することができる
ActiveRecordを使うと、データモデルを作成したり操作したりするための多数のメソッドが使えるようになる
ActiveRecordのバリデーションを使うと、モデルに対して制限を追加することができる
よくあるバリデーションには、存在性・長さ・フォーマットなどがある
正規表現は謎めいて見えるが非常に強力である
データベースにインデックスを追加することで検索効率が向上する。また、データベースレベルでの位置異性を保証するためにも使われる。
has_secure_passwordメソッドを使うことで、モデルに対してセキュアなパスワードを追加することができる
専門用語
認証 (authentication)
相手が誰か確認する
社員証をチェックするみたいなもの
認可 (authorization)
通していい相手か確認する
社員証見て入れるかどうか判断するようなもの
マイグレーション
migration
コンフリクト
conflict
ハッシュ
hash 刻む
この記事が気に入ったらサポートをしてみませんか?