Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って始めたブログ

【Ruby on Railsチュートリアル(第4版)】第8章 基本的なログイン機構(演習と解答)

はじめに

このブログ記事は、私(Kodak)自身のRailsの勉強記録として書いています。
Ruby on Railsチュートリアル』の演習と解答をもくもくと書いているだけの記事なので、興味の無い方は軽くスルーしてあげてください。他にも、まだ『Railsチュートリアル』の演習に挑戦していない方、これから『Railsチュートリアル』をやるぞ!という方は(演習の解答の)ネタバレになりますので、スルーしてください。

Ruby on Rails チュートリアルとは?

Ruby業界でRailsを使い始めるなら、まず最初に初めるRails入門サイトです。
電子書籍版は有料ですが、Webサイトにあるオンライン版は無料なので、誰でも読む(Railsにチャレンジする)事ができます。
railstutorial.jp
全部で14章あり、かなりのボリュームですが、これを読む事でRailsの基礎を学ぶ事ができ、ちょっとしたWebアプリケーションを作れるレベルにはなれる?とのこと。
各章毎に演習問題が複数あり、これを解いていく事でRailsへの理解を深めていく事ができる様になっています。

環境について

Ruby 2.5.0-dev
Rails 5.1.4
・バージョン管理ツール:GitHub(https://github.com/Kodak4400)

演習と解答

8.1.1 Sessionsコントローラ<演習>
1. GET login_pathとPOST login_pathとの違いを説明できますか? 少し考えてみましょう。
【解答】GET login_pathは、データを取得したい時に使用する。 POST login_pathはデータを送信したい時に使用する。

2. ターミナルのパイプ機能を使ってrails routesの実行結果とgrepコマンドを繋ぐことで、Usersリソースに関するルーティングだけを表示させることができます。同様にして、Sessionsリソースに関する結果だけを表示させてみましょう。現在、いくつのSessionsリソースがあるでしょうか? ヒント: パイプやgrepの使い方が分からない場合は Learn Enough Command Line to Be Dangerousの Section on Grep (英語) を参考にしてみてください。
【解答】以下の通り。

$ rails routes | grep sessions
sessions_new GET    /sessions/new(.:format)   sessions#new
       login GET    /login(.:format)          sessions#new
             POST   /login(.:format)          sessions#create
      logout DELETE /logout(.:format)         sessions#destroy

8.1.2 ログインフォーム<演習>
1. リスト 8.4で定義したフォームで送信すると、Sessionsコントローラのcreateアクションに到達します。Railsはこれをどうやって実現しているでしょうか? 考えてみてください。ヒント:表 8.1とリスト 8.5の1行目に注目してください。
【解答】"Sign up now!"のSubmitボタンの遷移先がsignup_pathになっているため。signup_pathは、Post users#createのコントローラアクションと紐付いている。

8.1.3 ユーザーの検索と認証<演習>
1. Railsコンソールを使って、表 8.2のそれぞれの式が合っているか確かめてみましょう. まずはuser = nilの場合を、次にuser = User.firstとした場合を確かめてみてください。ヒント: 必ず論理値オブジェクトとなるように、4.2.3で紹介した!!のテクニックを使ってみましょう。例: !!(user && user.authenticate(’foobar’))
【解答】以下の通り。

$ rails console --sandbox
Loading development environment in sandbox (Rails 5.1.4)
Any modifications you make will be rolled back on exit
# 存在しない(何でもよい)の確認
irb(main):001:0> user = nil
=> nil
irb(main):002:0> !!(user && user.authenticate('foobar'))
=> false
# 有効なユーザー(誤ったパスワード)の確認
irb(main):003:0> user = User.first
  User Load (0.4ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2018-08-12 15:23:47", updated_at: "2018-08-12 15:23:47", password_digest: "$2a$10$j8YTAFtYA/b8uo0q0gqNX.9Zox3VCDNwhYkjMd6ET87...">
irb(main):004:0> !!(user && user.authenticate('aaaaa'))
=> false
# 有効なユーザー(正しいパスワード)の確認
irb(main):005:0> !!(user && user.authenticate('foobar'))
=> true
irb(main):006:0>

8.1.4 フラッシュメッセージを表示する<演習>
1. 8.1.4の処理の流れが正しく動いているかどうか、ブラウザで確認してみてください。特に、flashがうまく機能しているかどうか、フラッシュメッセージの表示後に違うページに移動することを忘れないでください。
【解答】実機操作のため割愛。

8.2.1 log_inメソッド<演習>
1. 有効なユーザーで実際にログインし、ブラウザからcookiesの情報を調べてみてください。このとき、sessionの値はどうなっているでしょうか? ヒント: ブラウザでcookiesを調べる方法が分からない? 今こそググってみるときです! (コラム 1.1)
【解答】以下の通り。
f:id:special-moucom:20180819161644p:plain

2. 先ほどの演習課題と同様に、Expiresの値について調べてみてください。
【解答】上記画像参照。ブラウザセッション終了時

8.2.2 現在のユーザー<演習>
1. Railsコンソールを使って、User.find_by(id: ...)で対応するユーザーが検索に引っかからなかったとき、nilを返すことを確認してみましょう。
【解答】以下の通り。

$ rails console --sandbox
Loading development environment in sandbox (Rails 5.1.4)
Any modifications you make will be rolled back on exit
## 検索に引っかかる場合
irb(main):001:0> user = User.find_by(email: "example@railstutorial.org")
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "example@railstutorial.org"], ["LIMIT", 1]]
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2018-08-12 15:23:47", updated_at: "2018-08-12 15:23:47", password_digest: "$2a$10$j8YTAFtYA/b8uo0q0gqNX.9Zox3VCDNwhYkjMd6ET87...">
## 検索に引っかからない場合
irb(main):002:0> user = User.find_by(email: "test@test")
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "test@test"], ["LIMIT", 1]]
=> nil

2. 先ほどと同様に、今度は:user_idキーを持つsessionハッシュを作成してみましょう。リスト 8.17に記したステップに従って、||=演算子がうまく動くことも確認してみましょう。
【解答】以下の通り。

irb(main):005:0> session = {}
=> {}
irb(main):006:0> session[:user_id] = nil
=> nil
irb(main):007:0> @current_user ||= User.find_by(id: session[:user_id])
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT ?  [["LIMIT", 1]]
=> nil
irb(main):008:0> session[:user_id]= User.first.id
  User Load (0.1ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> 1
irb(main):009:0> @current_user ||= User.find_by(id: session[:user_id])
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2018-08-12 15:23:47", updated_at: "2018-08-12 15:23:47", password_digest: "$2a$10$j8YTAFtYA/b8uo0q0gqNX.9Zox3VCDNwhYkjMd6ET87...">
irb(main):010:0> @current_user ||= User.find_by(id: session[:user_id])
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2018-08-12 15:23:47", updated_at: "2018-08-12 15:23:47", password_digest: "$2a$10$j8YTAFtYA/b8uo0q0gqNX.9Zox3VCDNwhYkjMd6ET87...">
irb(main):011:0>

8.2.3 レイアウトリンクを変更する<演習>
1. ブラウザのcookieインスペクタ機能を使って (8.2.1.1)、セッション用のcookieを削除してみてください。ヘッダー部分にあるリンクは非ログイン状態のものになっているでしょうか? 確認してみましょう。
【解答】実機操作のため割愛。

2. もう一度ログインしてみて、ヘッダーのレイアウトが変わったことを確認してみましょう。その後、ブラウザを再起動させ、再び非ログイン状態に戻ったことも確認してみてください。注意: もしブラウザの [閉じたときの状態に戻す] 機能をオンにしていると、セッション情報も復元される可能性があります。もしその機能をオンにしている場合、忘れずにオフにしておきましょう (コラム 1.1)。
【解答】実機操作のため割愛。

8.2.4 レイアウトの変更をテストする<演習>
1. 試しにSessionヘルパーのlogged_in?メソッドから!を削除してみて、リスト 8.23が redになることを確認してみましょう。
【解答】以下の通り。

### app/helpers/sessions_helper.rb
module SessionsHelper

  # ユーザーがログインしていればtrue、その他ならfalseを返す
  def logged_in?
    current_user.nil?
  end
end
$ rails test test/integration/users_login_test.rb
Failure:
Expected exactly 0 elements matching "a[href="/login"]", found 1..
Expected: 0
  Actual: 1

2. 先ほど削除した部分 (!) を元に戻して、テストが greenに戻ることを確認してみましょう。
【解答】以下の通り。

### app/helpers/sessions_helper.rb 
module SessionsHelper

  # ユーザーがログインしていればtrue、その他ならfalseを返す
  def logged_in?
    !current_user.nil?
  end
end
$ rails test test/integration/users_login_test.rb
Finished in 0.537099s, 3.7237 runs/s, 18.6185 assertions/s.
2 runs, 10 assertions, 0 failures, 0 errors, 0 skips
$

8.2.5 ユーザー登録時にログイン<演習>
1. リスト 8.25のlog_inの行をコメントアウトすると、テストスイートは red になるでしょうか? それとも green になるでしょうか? 確認してみましょう。
【解答】以下の通り。ユーザー作成後にログインしていない状態となるため、テストではREDになる。

### app/controllers/users_controller.rb
class UsersController < ApplicationController

  def create
    @user = User.new(user_params)
    if @user.save
      # log_in @user
      flash[:success] = "Welcome to the Sample App!"
      redirect_to @user
    else
      render 'new'
    end
  end

end
$ rails test
Failure:
Expected false to be truthy.

2. 現在使っているテキストエディタの機能を使って、リスト 8.25をまとめてコメントアウトできないか調べてみましょう。また、コメントアウトの前後でテストスイートを実行し、コメントアウトすると red に、コメントアウトを元に戻すと green になることを確認してみましょう。ヒント: コメントアウト後にファイルを保存することを忘れないようにしましょう。また、テキストエディタコメントアウト機能については Test Editor Tutorial の Commenting Out (英語) などを参照してみてください。
【解答】実機操作のため割愛。(参考)私が使用してるテキストエディタVimですが、コメントアウトは、Ctl-v → G → I → "#"を入力 → esc で可能。

8.3.4 失敗時のテスト<演習>
1. ブラウザから [Log out] リンクをクリックし、どんな変化が起こるか確認してみましょう。また、リスト 8.31で定義した3つのステップを実行してみて、うまく動いているかどうか確認してみましょう。
【解答】実機操作のため割愛。

2. cookiesの内容を調べてみて、ログアウト後にはsessionが正常に削除されていることを確認してみましょう。
【解答】実機操作のため割愛。