rails tutorial 第8章


はじめに

独学でrails tutorialを進めていく過程を投稿していきます。

進めていく上でわからなかった単語、詰まったエラーなどに触れています。

個人の学習のアウトプットなので間違いなどあればご指摘ください。

初めての投稿なので読みにくいところも多々あるかと思いますがご容赦ください。

第8章 基本的なログイン機構

8.1.2 ログインフォーム

ログインフォームを作成時のscopeの働きについてわからなかったので調べました。

form_with(url: login_path, scope: :session, local: true)

参考
https://qiita.com/akilax/items/f36b13f377f7e442bc73

あまり深く考え過ぎずにパラメーターを渡す際に必要なname値のプレフィックスと考えるのが良さそうです。

ざっくりと自分なりにまとめると、Active Recodeを継承しているオブジェクトのフォームでは

<%= form_with(model: @user, local: true) do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name, class: 'form-control' %>
.
.
<% end %>

とし、入力された値(生成されたinputタグのname値) へのアクセスは

params[:user][:name]

となり、入力結果をuserハッシュに保存していました。

今回のsessionフォームにおいても同様で、パラメーターに入力結果の値を渡すために、scopeで指定したハッシュに入力結果を保存しているということだと思います。

8.1.4 フラッシュメッセージを表示する

ログイン失敗時にエラーメッセージを表示します。

app/controllers/sessions_controller.rb
#リスト 8.8: ログイン失敗時の処理を扱う(誤りあり)
class SessionsController < ApplicationController

  def new
  end

  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      # ユーザーログイン後にユーザー情報のページにリダイレクトする
    else
      flash[:danger] = 'Invalid email/password combination' # 本当は正しくない
      render 'new'
    end
  end

  def destroy
  end
end

実は上のコードのままでは、リクエストのフラッシュメッセージが一度表示されると消えずに残ってしまいます。リスト 7.27でリダイレクトを使ったときとは異なり、表示したテンプレートをrenderメソッドで強制的に再レンダリングしてもリクエストと見なされないため、リクエストのメッセージが消えません。例えばわざと無効な情報を入力して送信してエラーメッセージを表示してから、Homeページをクリックして移動すると、そこでもフラッシュメッセージが表示されたままになっています。(rails tutorial 8章より引用)


ここで、なぜリクエストと見なされなければメッセージが消えないのか、また、エラーメッセージ表示後にhomeページをクリックして移動する際にGETリクエストをしているから消えてもよいのでは?という疑問が生まれました。

この疑問を解消するため、まずは改めてrenderとredirect_toの違いについて調べてみた。

参考
https://qiita.com/january108/items/54143581ab1f03deefa1
この記事を読んでもやはりhomeページをクリックする時にリクエストしてるよな、、、という疑問が残りました。
その後も色々調べてようやく回答にたどり着きました。どうやらflashについての理解が足りなかったようです。

参考
https://pikawaka.com/rails/flash

つまりflashメッセージが表示されてから、リクエストを受け、アクションが実行されてからフラッシュメッセージが消去されるようです。

だからrenderメソッドでviewが表示された後、homeリクエストをしてもメッセージが残っているようでした。

試しに
1.無効な情報を入力して送信
2.エラーメッセージを確認
3.homeページに移動
4.homeページでもエラーメッセージ消えていないことを確認
5.再度homeページをクリック
とするとエラーメッセージは消えていました。

凄いすっきりしました!!

8.2 ログイン

Railsのセッション用ヘルパーはビューにも自動的に読み込まれます。Railsの全コントローラの親クラスであるApplicationコントローラにこのモジュールを読み込ませれば、どのコントローラでも使えるようになります 。(rails tutorial 8章より引用)

注意
viewには自動で読み込まれますが、コントローラーなどで、ヘルパーに設定したメソッドなどを利用する場合はincludeで読み込む必要があります。

8.2.2 現在のユーザー

このトピックではわかりにくいところがあり、初めてQiitaで質問を利用させていただきました。

内容を深堀したとき、なぜfindメソッドではなくfind_byメソッドを利用すべきだったのか、わかりませんでしたので質問をしました。

質問内容
https://qiita.com/shun_study_p/questions/da3de50fe7826dc151ed
回答をしていただいた方、ありがとうございます。


@current_user ||= User.find_by(id: session[:user_id])

Userオブジェクトそのものの論理値は常にtrueになることです。そのおかげで、@current_userに何も代入されていないときだけfind_by呼び出しが実行され、無駄なデータベースへの読み出しが行われなくなります。(rails tutorial 8章より引用)

ここの文についても自分なりに補足

@current_user ||= User.find_by(id: session[:user_id])

この一文は@current_userがnilの時はfind_byメソッドを実行しUserオブジェクトを作成するというもの。(@current_userにUserオブジェクトが代入される)
そうなった後は@current_userはUserオブジェクトとなり、またUserオブジェクトはtrueを返すので、以降は左辺がtrueとなり、無駄なfind_byメソッドは実行されず(@current_userがnilではないから)無駄なデータの呼び出しも行われなくなるということ。

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

<%= link_to "Profile", current_user %>

復習も兼ねてこちらの一文に置いて何が行われているかというと

<%= link_to "Profile", "/users/#{current_user.id}" %>
<%= link_to "Profile", user_path(current_user.id) %>
<%= link_to "Profile", user_path(current_user) %>
<%= link_to "Profile", current_user %>

と省略されて行っています。

ここでまた一つの疑問が、
link_toの引数でモデルオブジェクトを渡すのはわかります。

でも今回渡してるのってメソッドじゃないの、、、?

こちらに関しては以下の記事を参考にしました。

参考記事1(7章でも参考にしました)
https://qiita.com/Kawanji01/items/96fff507ed2f75403ecb

参考記事2
https://teratail.com/questions/198096
どうやら戻り値にモデルのインスタンスを返すならそのメソッドはモデルオブジェクトとみなせるようですね。

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

def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
end

このdigestメソッドは、今後様々な場面で活用します。例えば9.1.1でもdigestを再利用するので、このdigestメソッドはUserモデル(user.rb)に置いておきましょう。この計算はユーザーごとに行う必要はないので、fixtureファイルなどでわざわざユーザーオブジェクトにアクセスする必然性はありません(つまり、インスタンスメソッドで定義する必要はありません)。(rails tutorial 8章より引用)


インスタンスメソッドとクラスメソッドの違いをあまり理解出来ていなかったのか、上の文の意味がよくわからない、、、

上の本文は

1.インスタンスメソッドで定義するとわざわざユーザーオブジェクトにアクセスする必要がある。
2.今回の"渡された文字列のハッシュを返すというdigestメソッド"はユーザーごとのインスタンスで行う必要はない。(クラスオブジェクトから直接実行すればよいもの)

と言っている。なんとなくだがこの2つが言いたいことはわかるような、、、

まだふんわりとした理解ですが、記事を参考に自分でまとめてみました。

参考
https://qiita.com/tbpgr/items/56eb65c0ea5882abbb07

つまり
クラスメソッドは○○クラス自身に関する情報の変更や参照の役割をもっている。

なので今回のようなパスワードのハッシュ化、記事の例にあるように男女といった性別の属性、これらはクラスメソッドであらかじめ定義しておくことができる。

インスタンスメソッドは、個別のインスタンスに関する情報の変更や参照の役割りを持っている。

なので特定のデータのパスワードや名前と言った情報の参照をしたいなどといったときはインスタンスメソッドを使う。(ユーザーオブジェクトにアクセスする必要があることもイメージしやすい)

こんなところだろうか、、、難しい、、

終わりに

今回の章も難しく、7割程度の理解で進んでしまった部分もあるので、また復習が必要だと思いました。