tnkzw.sake

HomeSeriesTagsAbout

Rails Tutorialで学べることメモ-第14章-

Series
Railsチュートリアル

Rails Tutorial を改めてしっかりやり直してのメモ第 14 章編

https://railstutorial.jp/chapters/following_users?version=7.0https://railstutorial.jp/chapters/following_users?version=7.0

便利なコマンド・メソッド

has_many through

このコードでは引数のシンボルの単数系に対する外部キーを、throughオプションで指定された関連を経由して探索する。
すなわち

has_many :followeds, through: :relationships

の場合はrelationshipsという関連(別でhas_manyなどで指定)を通じてfollowed_idを探索する。

しかし、デフォルトの名前を変えたい場合もある。その場合はsourceオプションに実態のキー名を書く。

member, collection

resource に対するルーティングにおいて、デフォルトで追加される各種アクション以外に設定したいアクションを定義する。

memberはリソース id を伴ったルーティングを追加することができる。

resources :users do
  member do
    get :followers # /users/1/followersのようなルーティングを追加する
  end
end

collectionはリソース id を伴わない部分集合や集合に対する操作などに利用できる。

resources :users do
  collection do
    get :cats # /users/catsのようなルーティングを追加する
  end
end

知識

モデリング

フォロー、フォロワーを考える際に、作成されるものをしっかり考えると「ユーザー間の関係」であると言える。

フォロワーのように対象そのものをモデリングすると、User とは別に email などを持つレコードが生成される。
しかし、関係が作られると見立てると既存の User の情報を紐づけるリソースを作るという発想になる。

空虚な真(vacuous truth)

ある文に含まれる条件が常に真になると、何も言明していないのと同然の無意味な文になる状況を表す論理学の用語

例えば JavaScript のeveryや Python のallは配列に対して何かしらの条件を検査し、その全てが条件を満たしていればtrueを返す。

const array = [2, 4, 6, 8, 10];
const isEven = array.every((e) => e % 2 === 0); // true

しかし、これらのメソッドは配列が空の場合もtrueを返す。

const array = [];
const isEven = array.every((e) => e % 2 === 0); // true

つまり、「配列内の全ての要素が条件funcをみたす」という命題の真偽を評価しているが、そもそも「配列内の全ての要素が(存在して、それらが)」という前件が偽ならばその後の真理値を推論せずとも、命題全体として(空虚に)真であるとみなされる。

ざっくり言えば、If(もし)P, then(ならば) Q という仮定 P だったら Q が成り立つの?を考えているので、仮定(P)が成り立たない時点で帰結(Q)が何であっても、この話は真としましょうというルールになってるんだと受け入れておく。

Hotwire

Rails の生みの親、DHH によって Rails 向けに開発された仕組みだが、Ruby 以外にも多くの言語で動作する。

https://zenn.dev/shita1112/books/cat-hotwire-turbo/viewer/abstracthttps://zenn.dev/shita1112/books/cat-hotwire-turbo/viewer/abstract

サブセレクト

has_many :followingのように関連付けを行うと、Rails ではfollowing_idsのようなメソッドが自動生成される。
これは実態としては、関連付けたテーブルのレコードを取得し、idを取り出して配列にする処理であり、SELECT 文が実行された結果を Rails 上に展開している。

つまり、following_idsを使ってwhereを含む SQL を発行すると 2 回問い合わせをすることになる。

DB 側の機能で最適化させた方が効率が良い場合はこのような処理を SQL で記述し(サブセレクトとして扱い)、1 回のクエリで実行できるようにする。

following_ids = "SELECT followed_id FROM relationships
                 WHERE  follower_id = :user_id"

N+1 クエリ問題

上記のサブクエリを用いて、microposts を次のように取得する場合を考える。

Micropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", user_id: id)

ここで、microposts N 件分は 1 クエリで取得できるが、各ポストに対応するユーザー情報をポストごとに取得するとポスト数分、つまり追加で N 回のクエリが発行される。

このような無駄なクエリ発行が発生する問題をN+1問題と呼ぶことが多い。
実際にはポストごとに画像の問い合わせをする場合もあるので、2N+1 回分のクエリが発行される可能性がある。

この無駄なクエリを削減するためにeager loadingと呼ばれる手法を用いる。1 回のクエリでフィード表示に必要な情報を全部取ってこようという算段であり、そもそも Microposts に紐づく情報なので Join すれば取って来れる SQL の仕組みを知っていれば理解できる。

まとめ

SQL 自体の知識や Hotwire の実践など Rails だけに留まらない知識を利用しながら完走に導き、今後の発展課題まで紹介される内容だった。

Rails を API モードで使う開発をここ数年ずっとやってきて、Rails Way ってよく言うけど DRY とか CoC(Convention over Configuration)みたいな特徴のコト?みんな何を指してるの?という疑問がずっとあったので Rails チュートリアルをしっかり走りなおしてみた。

結果、Rails Way がなにか説明できるようになったかといえば微妙な気もするが、異常なほどの規約の強さが Rails の敷いた道で、それに乗った際の記述の少なさがその恩恵だというぼんやり思っていたところがみんなの頭にある Rails Way という概念なんだろうなと結論したい。

そしてよく議論されるように、現場に出るとすぐに Rails Way に素朴に乗るだけでは解決が難しい要件に直面する。
Rails がレールを敷いてくれたからといって、やっぱり簡単に解決できることは少ないし、簡単に解決できるように使いこなせる自身の力を伸ばす努力が必要になる。

ともかく Rails チュートリアル改めてやって良かったし、実際に認証や認可モデルを伴ったものをデプロイするので、動くものを作る!というスタート地点までのアウトラインとするのにすごくちょうどいい教材だと感じた。Rails を使わない予定の人も、まずはこれで勉強してもいいんじゃなかろうか。