Ruby on Railsチュートリアル第6版の第14章のまとめです。
個人的に気になったところ、難しかったところ、わからなかったところを中心にまとめていきます。基本的に自分の理解を助けることと、復習しやすくすることを目的とした記事ですが、今Ruby on Railsチュートリアルをやってる人に役立つ情報があるかもしれません。
僕の学習の順番は、1周目はRailsチュートリアル第4版(Rails5.1)を使い、2周目で第6版を使うという感じになってます。これは1周目が終わったところで第6版のWebテキスト版がリリースされたからです。
ちなみに、2周目は解説動画を購入してやってます。オススメです。
第14章 ユーザーをフォローする
最終章の第14章は、最も難しい章だと思います。何が難しいかというと、フォローに関するモデルの理解です。
1周目ではほとんど理解できず、とりあえず終わらせた感じでした。2周目に入るときに解説動画を購入した理由は、お試し動画で第14章を見てわかりやすかったからです。
どうしてもわからないという場合は、解説動画を見ることを強くオススメします。効率がものすごく上がります。

14.1 Relationshipsモデル
まずはフォローに関するデータモデルの構成です。第14章はここが理解できないと、何をやってるかわからなくなると思います。
しっかりと理解しながら、確実に進んでいきます。
では、いつものように、ブランチを作ります。
14.1.1 データモデルの問題(および解決策)
1周目はここで大混乱でした。
最初の方で「英語の都合」の問題が説明されてますが、それを日本語で読むという、なかなかハードな感じになってます。おそらくそこは本質的なところではないと思います。
重要なことは、「シンプルなデータモデルを作る」ということだと思います。
followerとか、followingとか、followedとか、いろいろ出てきますが、シンプルで無駄のないデータモデルを作るために、抽象化を行う必要があるということを説明してると、僕は理解してます。
最終的には「関係性」というシンプルなデータモデルを使うことになります。これは、「誰が誰をフォローしてるか」というデータになります。
つまり、「フォローした人」と「フォローされた人」がセットになったデータモデルです。
この2つのデータはユーザーのIDで管理されるため、「ID1番がID2番をフォローした」というようなデータになればいいということですね。それで、そのIDを使って、ユーザー情報を引き出すというわけです。
なので、フォローした人のデータとして「follower_id」、フォローされた人のデータとして「followed_id」というカラム名を設定します。このIDはユーザーのIDです。
「ID1番がID2番をフォローした」ということであれば、「follower_id = 1」、「followed_id = 2」ということです。
検索の高速化のためのインデックスを付け、フォロー・フォロワーのセットに一意性を持たせるために複合キーインデックスでユニークにします。
14.1.2 User/Relationshipの関連付け
関連付けと言えばhas_manyとbelongs_toが第2章と第13章で出てきました。この2つがどのように機能するかは第2章でまとめてあります。
これまではRailsのデフォルトの挙動を使ってたようで、
has_many :microposts
みたいに書けばすべてが完了してました。
でも、今回は違います。上のコードは、Micropostモデルがあることを前提としてます。つまり、引数のシンボルを単数形にしたモデルを探しに行くという感じなんだと思います。
それを踏まえて今回のコードを見てみると、ActiveRelationshipモデルを探しに行ってしまうことがわかります。
has_many :active_relationships
今回作ったモデルはRelationshipモデルなので、それと関連付けたいわけです。ということは、Railsのデフォルトの挙動から外れて、どのモデルを探しに行ってもらうかを指定する必要があるということです。
そのために出てくるのが、「class_name」です。こうすることで、指定したモデルを探しに行ってくれるようになるそうです。
次にidを使った関連付けです。デフォルトの挙動では、belongs_toの引数を使って関連付けが行われてるようです。
class Micropost < ApplicationRecord
belongs_to :user
であれば、micropostsテーブルにあるuser_idというカラム(user_id属性)と、usersテーブルにあるidというカラム(id属性)が自動的に関連付けられます。
これは、自動的にmicropostsテーブルのuser_id属性が外部キー(foriegn key)として設定されるということです。
ということは、今回のコードだと外部キーを設定しなければfollower_idが外部キーになってしまうということです。
belongs_to :follower
それは存在しないので、設定する必要があります。
ということで、
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id"
こんな感じのコードになるわけです。
解説動画によると、has_manyとbelongs_toはメソッドを生成するメソッドということなので、「follower_idを外部キーにしたRelationshipモデルを使って、active_relationships関係のメソッドを生成します!」みたいな感じなのかもしれません。
で、belongs_to側もこのままではFollowerモデルを探しに行ってしまうので、
belongs_to :follower, class_name: "User"
こうやってクラス名としてUserを指定して、Userモデルと関連付けるようにします。日本語訳は、「Userモデルを使って、follower関係のメソッドを生成します!」ということでしょうか。
言葉で説明すると、なかなかややこしい感じになってしまいますね。
まとめると、「関連付けるモデル名と生成したいメソッド名が違ったら、has_many側でclass_nameを使ってモデル名を指定して、外部キーも違ったらforiegn_keyで外部キーを指定する」ということだと思います。
これで、active_relationshipsメソッドやfollowerメソッド、followedメソッドなどが使えるようになります。
(Userクラスのオブジェクトに対してfollower/followedメソッドを呼び出そうとしてNoMethodErrorが出て悩んだのは内緒です)
14.1.3 Relationshipのバリデーション
Relationshipモデルのfollower_idとfollowed_idに対してバリデーションを設定します。
TDDでやりますが、何度もやってきたことなので特に問題ないと思います。
14.1.4 フォローしているユーザー
has_many throughを使ってfollowingメソッドなどの便利メソッドを実装します。
has_many throughに関しては解説動画を見るとわかりやすいと思いますが、今のところの理解に基づいて別の説明を試みようと思います。
has_many :following, through: :active_relationships, source: :followed
Userモデルにあるこのコードについてです。
has_manyメソッドは「メソッドを作るメソッド」なので、followingに関するメソッドが生成されることになります。そのときに、throughとsourceを使うことでデフォルトの挙動から外れることができます。
言い換えると、デフォルトではその挙動が指定してあって、それを上書きできるということだと思います。
解説動画ではthroughとsourceという英単語に意味を無視した方がわかりやすいと説明されてますが、僕はあえてその意味に基づいた説明をしてみます。
throughは「〇〇を通って」というような意味です。sourceは「情報源」などの意味がありますね。
これをそのまま使うと、「active_relationshipsを通って」、「followedが情報源」となります。
Railsチュートリアルにある図14.7が関連付けの模式図が重要になります。その図を見ればわかるように、active_relationshipsを通って、usersテーブルの情報を取り出してます。
active_relationshipsのインスタンスに対してfollowedメソッドを使うと、followed_idに対応したユーザーの情報をusersテーブルから取り出すことができます。つまり、「followedメソッドを使って情報源にアクセスする」ということですね。
active_relationshipsは「経由地」になってることがわかります。
その経由地でユーザーと一致するfollower_idのデータを全て取り出し、followedメソッドを使ってそれぞれのデータにあるfollowed_idの情報源であるユーザーデータを取り出すことになります。
図にするとこんな感じです。

今回のコードでは、「following」という名前のメソッドをhas_manyメソッドで作ってます。なので、ここまで説明してきた動きをfollowingメソッドで実現できることになります。
14.1.5 フォロワー
今まで実装してきたのとは逆で、今度はfollowerを実装します。active_relationships関係がわかってれば、そこまで難しくないと思います。
サクッといくだろうと思ってたら、演習で引っかかりました。最初のユーザーが何人かにフォローされてるようにRailsコンソールでデータを作ろうとしたら、
ActiveRecord::RecordNotUnique (SQLite3::ConstraintException: UNIQUE constraint failed: index 'index_relationships_on_folloer_id_and_followed_id')
というエラーが出ました。何やらfolloer_idがインデックスで設定されてるようです。マイグレーションファイルを見直してみたら、複合キーインデックスのところでfolloer_idになってました。
マイグレーションをやり直しました。スペルミスには要注意ですね。
14.2 [Follow]のWebインターフェイス
フォローに関するWebインターフェイスを実装します。変化が見えるようになるので、わかりやすくなりそうです。
14.2.1 フォローのサンプルデータ
サンプルデータを作成します。
他のサンプルデータと同様にseeds.rbに記述しますが、これまでの知識でコードは読めると思います。
14.2.2 統計と[Follow]フォーム
フォロー/フォロワーの統計情報(人数)を表示するようにします。
新しく出てくるのがmemberメソッドです。ルーティングの入れ子構造で、URLも入れ子な感じで、idに続いて「/hogehoge」のようになります。名前付きルートも作られます。
resourcesに「do-end」を使って追加しますが、これはブロックだそうです。
たぶん、Railsチュートリアルの説明より、Railsガイドの説明の方がわかりやすいと思います。

次は、フォロワーの統計情報のパーシャルにある
@user ||= current_user
というコードです。このコード自体は第8章で出てきた書き方で、まとめたので特に問題ないんですが、何をやってるかで少し混乱しました。
そもそも、ここで@userに入ってることが期待されるものは何か、current_userはどう定義されてるか、ということが曖昧になってました。
current_userはログインしてるユーザーのはずで、なぜ@userに代入されるのかもよくわからず。
まずは@userから。
プロフィールページのデバッグ情報を確認すると、Usersコントローラのshowアクションが呼び出されてることがわかります。
@user = User.find_by(id: params[:id])
Usersコントローラのshowアクションにはこのようなコードがあります。これはそのユーザーページのユーザーidを使ってデータベースに問い合わせて、ユーザー情報を@userに代入してるということですね。
@userに入ってるのはそのプロフィールページのユーザーです。なので、プロフィールページに関しては、そのプロフィールページのユーザーの情報が出てくることになります。
ではなぜcurrent_userを代入するコードもあるのかという疑問です。理由がわかったら、なぜわからなかったのか不思議なくらい簡単でした。
統計情報のパーシャルはHomeページでも表示されます。そのページではStaticPagesコントローラのhomeアクションが呼び出されてます。ということは、@userがnilということですね。
それでは困るし、Homeページはログインしてるユーザーのページが表示されるようになってるので、current_userを@userに代入してるわけです。
current_userメソッドを探し当てるのに苦労しましたが、Sessionsヘルパーにありました。記憶の通り、ログインしてるユーザーですね。
でも、current_userはメソッドで、データ自体は@current_userに入れるようになってます。ということは、メソッドを代入?
よくわからないので、debuggerを使って、current_userに入ってるものを確認してみました。そうすると、ログインしてるユーザーの情報が出てきました。
これは暗黙の戻り値関係でしょうか。
よくわかりませんが、とにかくログインしてるユーザーの情報を@userに入れることができてることはわかりました。
14.2.3 [Following]と[Followers]ページ
フォロー関係のページを作成します。一緒にテストも書いていきます。
テストのところで、
assert_not @user.following.empty?
これは無意味なテストではないということが書かれてます。
その後のコードの、
@user.following.each do |user|
assert_select "a[href=?]", user_path(user)
end
これを確かめるためとか。でも意味がよくわかりませんでした。
可能性としては、2番目のテストコードは@user.followingが空だったら通ってしまうということかなと思って、確かめてみました。
具体的にはテスト用のrelationships.ymlの空の状態にしてテストを走らせました。
1番目のテストコードをコメントアウトするとGREENになってしまいます。ということで、予想は当たってたようです。
14.2.4 [Follow]ボタン(基本編)
Follow/Unfollowボタンの実装です。
before_actionで使われてるlogged_in_userはApplicationコントローラで定義されてます。見つけるのに苦労しました。
それ以外はサラッといけるかと思いましたが、Followボタンでエラー。
user = User.find(params[:followed_id])
createアクションのここのところで引っかかったみたいです。「ActiveRecord::RecordNotFound in RelationshipsController#create」で、「Couldn’t find User without an ID」と言われました。
「IDがないユーザーを見つけられない」的な感じですね。ということは、「params[:followed_id]」のところが何かおかしいと判断。パラメータを見てみたら「followed」となってました。
もしかしたらFollowボタンのコードが間違ってるかもしれないと思って確認すると、
<%= hidden_field_tag :followed, @user.id %>
となってました。followed_idに書き換えて無事に動きました。
14.2.5 [Follow]ボタン(Ajax編)
Ajaxを使ってFollow/Unfollowしたときにページの変更箇所だけリロードさせるように変更します。
jQueryやJavaScriptがわからないと詳しいことを理解できないので、RailsでAjaxを実装する方法があるということを覚えておいて、必要になったら調べる感じでとりあえずはいいかなと思ってます。
ただ、Ajaxも使ってWebアプリケーションを作れたら楽しそうだなと思います。
14.2.6 フォローをテストする
フォローに関するテストを書きます。
内容的には問題ないと思いますが、演習がわかりませんでした。調べてみたら、わからないという記事も出てきたので、難しいところなのでしょうか。
レベルアップしてから理解できればいいなと思いつつ、先に進みました。
14.3 ステータスフィード
第13章で実装したフィードの完全版の実装です。
14.3.1 動機と計画
どのようになればいいかは明確なので、テストから書きます。テストも読めると思います。
14.3.2 フィードを初めて実装する
要件に合うようにフィードを実装します。
SQLの話がメインなので、SQLが全くわからないと何を言ってるのかわからないかもしれません。
ProgateのSQLコースをやってれば何となくわかると思いますが、忘れてるところもあって完全には理解できないまま進みました。
14.3.3 サブセレクト
SQL文をさらに最適化します。
ここもSQLの知識が前提になってます。SQLの勉強をここで入れるかは人それぞれかなと思います。
僕は今ある知識でぼんやりとした理解をして、Railsをもっと使えるようになったり、必要に迫られたりしたときに勉強しようと考えてます。
これでRailsチュートリアルで作るsample_appは完成です。テキストにはコミットなどをやるように書いてありますが、その下にある演習を先にやった方が楽だと思います。
Herokuへのデプロイまでやってから演習に気づき、再度デプロイするという無駄なことをやってしまいました。
ただ、演習3については、書いてある通りにコードを書き換えると、同じ投稿がたくさん表示されるという不思議な現象が起こりました。それも1番目(idが1)のユーザーだけ。
よくわからないので、元のコードに戻して、とりあえず放置することにしました。あとで修正するかもしれません。
そんなこんなで、Herokuへのデプロイは計3回になってしまいました。でも、2周目はある程度の理解を保った状態で完走することができました。
まとめ
2周目をやってみて、一番難しいのはやっぱり第14章だと思いました。データモデルが最大の難関ですね。
それと同時に、Ajaxの実装はワクワクさせられます。Railsチュートリアルで作ったアプリケーションはスマホ対応のレスポンシブデザインになってないですし、その辺も作りこめるようになったら楽しそうです。
Railsチュートリアルの完走がどの程度のレベルなのかはわかりませんが、おそらくWeb系エンジニアのスタートラインに立つ前の準備運動くらいなんじゃないかなと感じてます。
テキスト通りにやっていけばいいのはここまでで、ここからは正解のない世界に足を踏み出してくことになると思います。
2周目を始める前は3周目も必要かなと思ってましたが、今のところ3周目の必要性は感じてません。かといって、オリジナルアプリケーションを作るのもハードルが高い気もしてます。
本当は早くポートフォリオ作りに入った方がいいんでしょうけど、まずは第13章で気づいたバグの修正やRailsチュートリアルのアプリケーションの機能拡張をやろうと思ってます。
テキストに書いてあるものをすべてやるかはわかりませんが、1つ以上は実装して、やりながら次を考える予定です。
Railsチュートリアルの学習効率を上げたいなら、解説動画の購入をオススメします。

