Ruby on Railsチュートリアル第6版の第10章のまとめです。
個人的に気になったところ、難しかったところ、わからなかったところを中心にまとめていきます。基本的に自分の理解を助けることと、復習しやすくすることを目的とした記事ですが、今Ruby on Railsチュートリアルをやってる人に役立つ情報があるかもしれません。
僕の学習の順番は、1周目はRailsチュートリアル第4版(Rails5.1)を使い、2周目で第6版を使うという感じになってます。これは1周目が終わったところで第6版のWebテキスト版がリリースされたからです。
ちなみに、2周目は解説動画を購入してやってます。オススメです。
第10章 ユーザーの更新・表示・削除

第10章では第8章で実装したアクションに加えて、4つのアクションを実装してRESTアクションを完成させます。
10.1 ユーザーを更新する
まずはユーザーの情報を更新するためのeditアクションから作っていきます。
いつものようにブランチを作って進みましょう。
10.1.1 編集フォーム
Usersコントローラにはeditアクションがないので追加します。そのコードは説明がなくてもわかると思います。
次にビューを作ります。editビューのコードはnewビューのコードとほとんど同じなのに、生成されるHTMLのformタグの中身が違ってます。
editビューではPATCHリクエスト的なものを送ることができるようになってます。どうやって区別してるかというと、new_record?メソッドだそうです。その名の通り、新しいレコードだとtrueを返します。
Railsチュートリアルでは変数に入れずに試してありますが、変数に入れるとどうなるのか実験してみました。
>> user = User.new
(省略)
>> user.new_record?
=> true
>> user2 = User.first
(省略)
>> user2.new_record?
=> false
変数に入れても同じように動きましたね。内部でどう動いてるのかわかりませんが、おそらくデータベースに問い合わせをしてるんじゃないかなと思います。
10.1.2 編集の失敗
編集フォームができたので、次はデータを更新するupdateアクションです。ここも問題なく理解できると思います。
ただ、引数で使われてるuser_paramsって何だっけ?という疑問が出ました。すっかり忘れてましたが、第7章でやったストロングパラメータです。updateアクションの下を見れば書いてあります。
10.1.3 編集失敗時のテスト
編集に失敗したときのテストを書きます。ここも問題ないと思います。
10.1.4 TDDで編集を成功させる
次に編集が成功するようにしますが、ここではテスト駆動開発でやっていきます。ここもサラッといけると思います。
ちなみに、allow_nilは「Railsドキュメント」によると、nilをスキップするオプションで、デフォルトではfalseになってるそうです。
10.2 認可
認証と認可は名前が似てるので混乱してしまいますが、別物だそうです。テストじゃないのですぐに暗記する必要はないと思いますが、使いながら覚えていけたらいいなと思います。
ここまでの実装だと誰でも編集ができてしまうので、ユーザー情報をそのユーザーだけが編集できるようにします。
10.2.1 ユーザーにログインを要求する
編集ページなどにはログインしたユーザーだけがアクセスできるようにします。そのために、before_actionメソッドを使います。
before_actionメソッドは特定のメソッドが何らかの処理の直前に実行されるようにするものだそうです。「特定のメソッド」はシンボルで記述します。
ここで作った特定のメソッドに出てくる「unless」がどうもわかりにくいんですよね。何度か出会ってるはずなんですが。ということで、今回は調べてみました。
unless は if と反対で、条件式が偽の時に then 以下の式を評価します。unless 式にelsif を指定することはできません。
unless – 制御構造 – Ruby 2.7.0 リファレンスマニュアル
unlessは条件式が偽、つまりfalseだった場合に処理を実行するということですね。しかも、elsifは使えないと。ちょっとスッキリしました。
10.2.2 正しいユーザーを要求する
次に、他のユーザーがユーザー情報を編集できないようにします。
テストを書いてから、実装します。ここもそんなに難しくないと思います。
リファクタリングのところで出てくる、
user && user == current_user
というコードですが、第8章で出てきたぼっち演算子的にできないかなと思って、いろいろ試してみました。そしたら、
user&.== current_user
でテストがGREENになりました。GREENになったのはいいんですが、この書き方は一体何なんでしょうか?調べてみましたが、わからず。とりあえずテキスト通りに直して、コメントで残しておきました。
10.2.3 フレンドリーフォワーディング
ログインしてない状態で編集ページにアクセスすると、ログインページにリダイレクトされます。そこでログインした後に編集ページに飛ぶようにします。
ここでもテスト駆動開発です。ここも特に問題ないと思います。
10.3 すべてのユーザーを表示する
ユーザー一覧を表示するためにindexアクションを追加します。
10.3.1 ユーザーの一覧ページ
indexビューのところまでは問題ないと思います。そのあとのGravatar関係のところが気になります。
gravatar_forヘルパーにオプション引数を追加するとなってますが、これは第7章の演習で出てきてます。さらに、その演習ではキーワード引数に書き換えてあります。
とりあえず、どちらも同じように動きますが、違いはあるようです。
teratailに「Rails オプション引数とキーワード引数の違い、優先順位」という質問があり、回答されてます。が、いまいちわかりません。
調べてみましたが、結局わからないまま。Ruby 3.0でキーワード引数の挙動が変わる予定で、2.7からハッシュをキーワード引数に自動変換する挙動が非推奨となるみたいな情報は見つけました。それもよくわからず。

仕方ないので、オプション引数に書き直して先に進みます。
10.3.2 サンプルのユーザー
Fakerというgemを使ってサンプルユーザーを量産します。ここも特に問題ないと思います。今回もbundle updateは必要ないはずです。
bundle installとbundle updateの違いは第3章のまとめに書いてあります。
10.3.3 ページネーション
ユーザー一覧ページに全ユーザーが表示されると大変なので、ページネーションを追加します。
ここはwill_paginateというgemの使い方の説明という感じですね。
10.3.4 ユーザー一覧のテスト
ページネーションを追加したので、そのテストを書きます。ここも問題ないと思います。
10.3.5 パーシャルのリファクタリング
indexビューのユーザー一覧部分をパーシャル化します。
indexビューに、
<%= render @users %>
と書くだけで、Railsが@usersをUserオブジェクトのリストと推測するらしいんですが、その条件を調べてみました。
第10章をやってたときにはわかりませんでしたが、第13章でも同じようなのが出てきて、そこに書かれてたことと組み合わせて試したところ謎が解けました。
以前に書いてた内容はこんな感じです。
まず、@usersを@uに書き換えてみると、テストでエラー。
複数形である必要があると想定して、@dogsに書き換えてみても変わらず。パーシャルのファイル名も同じにする必要があるかもしれないと考えて、「_user.html.erb」を「_dog.html.erb」に書き換えてもダメ。
別の名前に置き換えてうまくいくことが再現できなかったので、1つ考えられるのは、コントローラ名に合わせる必要があるということです。
調べて出てきたページを見ると、tweetsコントローラに@tweetsという感じのがいくつかあったので、その可能性が高そうです。真相は不明ですが。
問題となってたのはwill_paginateというgemでした。
Railsチュートリアル第6版第13章から引用します。
実は、上のコードは引数なしで動作していました。これは
Ruby on Railsチュートリアル第6版 第13章will_paginate
が、Usersコントローラのコンテキストにおいて、@users
インスタンス変数が存在していることを前提としているためです。
上のコードというのは第10章のコードのことを指してます。
<%= will_paginate %>
これのことです。この場合、@usersという引数が省略されてると考えればよさそうです。つまり、will_paginateはどのインスタンス変数を使うかの設定が必要で、何もない場合(デフォルトの場合)はコントローラ名を複数形にしたインスタンス変数を引数に取るということだと思います。
その証拠に、テストのログをよく見てみると、
ActionView::Template::Error: ActionView::Template::Error: The @users variable appears to be empty. Did you forget to pass the collection object for will_paginate?
と書かれてました。「@users変数が空っぽですよ」と言われてますね。
なので、ここで使うインスタンス変数を@usersから@uに変更して、will_paginateの引数に@uを渡すと、テストがGREENになりました。実際のページでも問題なく動くことが確認できました。
さらにwill_paginateをコメントアウトしてみたところ、テストはREDですが(それがあることをテストしてるため)、ページは表示されました。
結論としては、
<%= render @users %>
に関してはどんなインスタンス変数名でも大丈夫ということがわかりました。ただ、will_paginateを使う場合にインスタンス変数名をコントローラ名の複数形以外にするなら、引数にインスタンス変数を渡す必要があります。
10.4 ユーザーを削除する
最後に権限を持つユーザーだけがユーザーを削除できるようにします。これでRESTfulな実装が完成します。
10.4.1 管理ユーザー
管理ユーザーを作るために、usersテーブルにadmin属性を追加します。admin属性はboolean(ブーリアンと読むらしい)型にします。
boolean型が当たり前のように出てきますが、これの説明は今までなかったような気がします。見落としてますかね。
調べてみたところ、boolean型はtrueかfalseという2つの値をとるデータ型だそうです。
これを追加することで使えるtoggle!メソッドですが、admin属性の状態を反転させてデータベースを更新するという動きになってました。
あとは指示通りにやればOKです。
10.4.2 destroyアクション
destroyアクションを実装していきます。
指示通りにやっていって完成すると、deleteをクリックしたときに確認メッセージが表示されるようになります。これは、
data: { confirm: "You sure?" }
という部分のようです。TechRachoの「[Rails 5][Rails 4] ‘link_to’ APIドキュメント完全翻訳」という記事によると、data:は、
カスタムのdata-属性を渡すのに使います。
[Rails 5][Rails 4] ‘link_to’ APIドキュメント完全翻訳 – TechRacho
と説明されてます。
confirmは「confirm: ‘質問文’」という形で、
このオプションを指定すると、ユーザーへの問い合わせメッセージ(この場合は「質問文」)をUnobtrusive JavaScriptドライバで表示できるようになります。ユーザーがこれを了承するとリンクは通常どおりに実行され、了承しない場合は何も実行されません。
[Rails 5][Rails 4] ‘link_to’ APIドキュメント完全翻訳 – TechRacho
と説明があります。
Railsチュートリアルにあるコードだとdeleteリンクは、
<a data-confirm="You sure?" rel="nofollow" data-method="delete" href="/users/2">delete</a>
となっています。
data属性(?)の部分を削除すると、
<a rel="nofollow" data-method="delete" href="/users/2">delete</a>
このように変わります。data-confirmがあるかないかの違いですね。
詳しいことはわかりませんが、引用した説明から推測すると、JavaScriptが関係してると思われます。
10.4.3 ユーザー削除のテスト
ユーザーが削除できるようになったので、そのテストを書きます。
テストコードを読んで何となくやってることはわかりましたが、自分でテストを書くとなるとまだまだ難しいだろうなと思います。
これで第10章は終わりなので、いつものようにコミット、マージ、プッシュ、デプロイします。
今回はHerokuにデプロイしたアプリケーションにサンプルデータを入力する手順が入ってます。
まとめ
第10章は第9章と比べればそこまで難しくなかったと思います。それはここまでの積み重ねがあったからですね。
おそらく理解度によって難しさは違ってくると思いますが、2周目だからか、できるだけ理解して進んできたからか、1周目の印象(だいぶ薄れていますが)より難しくなかった気がします。
キーワード引数とオプション引数の違いなど、理解できてないところもありますが、それはまた必要になったときに調べ直すことにします。その頃には知識も増えて、理解できるようになってると期待して。
ある程度のカタチになって達成感がすごいですが、先に進むことにします。
