おぴよの気まぐれ日記

おぴよの気まぐれ日記

岡山やプログラミング、ファッションのこと、子育てや人生、生き方についての備忘録。

Ruby on Rails チュートリアルで30歳までに人生を変える(第8章)

こんにちは。opiyoです。

今回は、第8章をやっていきます。

第8章は画面からユーザーを登録する方法です。

railsチュートリアル8章の学んだこと

  • httpはステートレスなプロトコル。つまり状態がない
  • リクエストのひとつひとつは、その前のリクエスト情報を知らない
  • リクエストが終わると何もかも忘れて次回最初からやり直す健忘症的なプロトコル
  • ブラウザのあるページから別のページに移動したときに、ユーザーのIDを保持しておく手段がHTTPプロトコル内「には」まったくあり
  • セッション (Session) と呼ばれる半永続的な接続をコンピュータ間 (ユーザーのパソコンのWebブラウザRailsサーバーなど) に別途設定
  • セッションを実装する方法として最も一般的なのは、cookies
  • sessionメソッドで作成した一時cookiesは自動的に暗号化され
  • user.authenticate(password)は、引数に渡された文字列 (パスワード) をハッシュ化した値と、データベース内にあるpassword_digestカラムの値を比較する
  • 「!!」(「バンバン (bang bang)」と読みます) という演算子を使うと、そのオブジェクトを2回否定することになるので、どんなオブジェクトも強制的に論理値に変換できる
> 0
=> 0
irb(main):017:0> !0
=> false
irb(main):019:0* !!0
=> true

> user && user.authenticate("hakuhaku")
=> #<User id: 1, name: "haku", email: "haku@co.jp", created_at: "2017-08-17 02:41:54", updated_at: "2017-08-17 02:41:54", password_digest: "$2a$10$zcrCZj1hnh3cp.zZDmYD6OV/73uzwUzbWTqyceJZWEz...">
irb(main):023:0> !!(user && user.authenticate("hakuhaku"))
=> true
  • flashのメッセージとは異なり、flash.nowのメッセージはその後リクエストが発生したときに消滅します
  • sessionメソッドで作成した一時cookiesは自動的に暗号化される
  • Railsのapplication.jsファイルを通して、Bootstrapに同梱されているJavaScriptライブラリを読み込むよう、アセットパイプラインに指示する
  • Rails 5.1 からjQueryもデフォルトで読み込まれなくなったので、jQueryも追加する必要があります

railsチュートリアル8章の演習解説

8.1.1 演習 Sessionsコントローラ

8.1.1.1

<問題>GET login_pathとPOST login_pathとの違いを説明できますか? 少し考えてみましょう。

<回答>

       login GET    /login(.:format)          sessions#new
             POST   /login(.:format)          sessions#create
  • GET login_path:'/login'にアクセスした時 → sessionコントローラー、newアクション
  • POST login_path:'/login'で、入力した値を送信した時 → sessionコントローラー、createアクション

8.1.1.2

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

<回答>

$ rails routes | grep users
      signup GET    /signup(.:format)         users#new
       users GET    /users(.:format)          users#index
             POST   /users(.:format)          users#create
    new_user GET    /users/new(.:format)      users#new
   edit_user GET    /users/:id/edit(.:format) users#edit
        user GET    /users/:id(.:format)      users#show
             PATCH  /users/:id(.:format)      users#update
             PUT    /users/:id(.:format)      users#update
             DELETE /users/:id(.:format)      users#destroy
$ 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 演習 ログインフォーム

8.1.2.1

<問題>リスト 8.4で定義したフォームで送信すると、Sessionsコントローラのcreateアクションに到達します。Railsはこれをどうやって実現しているでしょうか? 考えてみてください。ヒント:表 8.1とリスト 8.5の1行目に注目してください。

<回答>

       login GET    /login(.:format)          sessions#new
             POST   /login(.:format)          sessions#create

form_for(:session, url: login_path)のurlに指定されたプレフィックスを見て判断している。 httpメソッドが、GET or POSTかはmethodオプションで変えれるはずだけどデフォルトはpostになる。 よってSessionsコントローラのcreateアクションに到達する!

8.1.3 演習 ユーザーの検索と認証

8.1.3.1

<問題>Railsコンソールを使って、表 8.2のそれぞれの式が合っているか確かめてみましょう. まずはuser = nilの場合を、次にuser = User.firstとした場合を確かめてみてください。ヒント: 必ず論理値オブジェクトとなるように、4.2.3で紹介した!!のテクニックを使ってみましょう。例: !!(user && user.authenticate(’foobar’))

<回答>

■User:存在しない && Password:合致しない → false

> user = nil
=> nil
irb(main):002:0> user && user.authenticate("hoge")
=> nil

■User:存在する && Password:合致しない → false

> user = User.first
  User Load (0.2ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 1, name: "haku", email: "haku@co.jp", created_at: "2017-08-17 02:41:54", updated_at: "2017-08-17 02:41:54", password_digest: "$2a$10$zcrCZj1hnh3cp.zZDmYD6OV/73uzwUzbWTqyceJZWEz...">
> !!(user && user.authenticate("hoge"))
=> false

■User:存在する && Password:合致する → true

> !!(user && user.authenticate("hakuhaku"))
=> true

8.1.5 演習 フラッシュのテスト

8.1.5.1

<問題>8.1.4の処理の流れが正しく動いているかどうか、ブラウザで確認してみてください。特に、flashがうまく機能しているかどうか、フラッシュメッセージの表示後に違うページに移動することを忘れないでください。

<回答>

  1. "/login"を開く
  2. EmailとPasswordに適当な値を入力しログインボタンを押下する
  3. flashメッセージが表示されることを確認
  4. "/"を開いた時にflashメッセージが表示されないことを確認

8.2.1 log_inメソッド

8.2.1.1

<問題>有効なユーザーで実際にログインし、ブラウザからcookiesの情報を調べてみてください。このとき、sessionの値はどうなっているでしょうか? ヒント: ブラウザでcookiesを調べる方法が分からない? 今こそググってみるときです! (コラム 1.1)

<回答> Chromeの場合はディベロッパーツールのApplicationタブから確認できます。(右クリックして「検証」すれば出てくる!) f:id:opiyotan:20170817122354p:plain

8.2.1.2

<問題>先ほどの演習課題と同様に、Expiresの値について調べてみてください。

<回答> 有効期限の意味。 上の画像で「Session」ってなってるやつは、ブラウザの再起動で無くなる。 年月日があるものは、それが有効期限になる。

8.2.2 演習 現在のユーザー

8.2.2.1

<問題>Railsコンソールを使って、User.find_by(id: ...)で対応するユーザーが検索に引っかからなかったとき、nilを返すことを確認してみましょう。

<回答>

> User.find_by(id: 99)
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 99], ["LIMIT", 1]]
=> nil

<問題>先ほどと同様に、今度は:user_idキーを持つsessionハッシュを作成してみましょう。リスト 8.17に記したステップにしたがって、||=演算子がうまく動くことも確認してみましょう。

<回答>

> session = {}
=> {}
irb(main):002:0> session[:user_id] = nil
=> nil
irb(main):003:0> @current_user ||= User.find_by(id: session[:user_id])
  User Load (2.9ms)  SELECT  "users".* FROM "users" WHERE "users"."id" IS NULL LIMIT ?  [["LIMIT", 1]]
=> nil
irb(main):004: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):005:0> @current_user ||= User.find_by(id: session[:user_id])
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "haku", email: "haku@co.jp", created_at: "2017-08-17 02:41:54", updated_at: "2017-08-17 02:41:54", password_digest: "$2a$10$zcrCZj1hnh3cp.zZDmYD6OV/73uzwUzbWTqyceJZWEz...">

8.2.3 演習 レイアウトリンクを変更する

8.2.3.1

<問題>ブラウザのcookieインスペクタ機能を使って (8.2.1.1)、セッション用のcookieを削除してみてください。ヘッダー部分にあるリンクは非ログイン状態のものになっているでしょうか? 確認してみましょう。

<回答> 非ログイン状態になる。

8.2.3.2

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

<回答> 非ログイン状態になる。

8.2.4 演習 レイアウトの変更をテストする

8.2.4.1

<問題>試しにSessionヘルパーのlogged_in?メソッドから!を削除してみて、リスト 8.23が redになることを確認してみましょう。

<回答>

$ rails test
FAIL["test_login_with_valid_information_followed_by_logout", UsersLoginTest, 0.940030867999667]
 test_login_with_valid_information_followed_by_logout#UsersLoginTest (0.94s)
        Expected exactly 0 elements matching "a[href="/login"]", found 1..
        Expected: 0
          Actual: 1
        test/integration/users_login_test.rb:27:in `block in <class:UsersLoginTest>'

8.2.4.2

<問題>先ほど削除した部分 (!) を元に戻して、テストが greenに戻ることを確認してみましょう。

<回答>

$ rails test
Running via Spring preloader in process 4789
Started with run options --seed 35483

  21/21: [==========================================================================================================================================================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.37352s
21 tests, 54 assertions, 0 failures, 0 errors, 0 skips

8.2.5 演習 レイアウトの変更をテストする

8.2.5.1

<問題>リスト 8.25のlog_inの行をコメントアウトすると、テストスイートは red になるでしょうか? それとも green になるでしょうか? 確認してみましょう。

<回答> テストスイートは redになる。

 FAIL["test_valid_signup_information", UsersSignupTest, 2.6715680599991174]
 test_valid_signup_information#UsersSignupTest (2.67s)
        Expected false to be truthy.
        test/integration/users_signup_test.rb:15:in `block in <class:UsersSignupTest>'

8.2.5.2

<問題>現在使っているテキストエディタの機能を使って、リスト 8.25をまとめてコメントアウトできないか調べてみましょう。また、コメントアウトの前後でテストスイートを実行し、コメントアウトすると red に、コメントアウトを元に戻すと green になることを確認してみましょう。ヒント: コメントアウト後にファイルを保存することを忘れないようにしましょう。また、テキストエディタコメントアウト機能については Test Editor Tutorial の Commenting Out (英語) などを参照してみてください。

<回答> コメントアウトは、コメントアウトしたい行を全て選択して「Command + /」(エディタ:Atom

8.3 演習 ログアウト

8.3.1

<問題>ブラウザから [Log out] リンクをクリックし、どんな変化が起こるか確認してみましょう。また、リスト 8.31で定義した3つのステップを実行してみて、うまく動いているかどうか確認してみましょう。

<回答> - トップページが表示される - dropdownメニューがLog inになる - ProfileSettingsのメニューが非表示になっている

8.3.2

<問題>cookiesの内容を調べてみて、ログアウト後にはsessionが正常に削除されていることを確認してみましょう。

<回答>

   18:   def destroy
   19:     log_out if logged_in?
   20:     byebug
=> 21:     redirect_to root_url
   22:   end

(byebug) session[:user_id]
nil