見出し画像

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 刻む


この記事が気に入ったらサポートをしてみませんか?