Ruby on Rails チュートリアルで30歳までに人生を変える(第13章)
こんにちは。opiyoです。
今回は、第13章をやっていきます。
第13章はユーザーが短いメッセージを投稿できる「マイクロポスト」機能を追加します。
やっとログイン関係の処理を抜けて、機能拡張ですね!
ではでは、早速行ってみましょう。
railsチュートリアル13章の演習解説
13.1.1 演習 基本的なモデル
13.1.1.1
<問題>RailsコンソールでMicropost.newを実行し、インスタンスを変数micropostに代入してください。その後、user_idに最初のユーザーのidを、contentに "Lorem ipsum" をそれぞれ代入してみてください。この時点では、 micropostオブジェクトのマジックカラム (created_atとupdated_at) には何が入っているでしょうか?
<回答>
> micropost = Micropost.new => #<Micropost:0x007fbbfc35b870 id: nil, content: nil, user_id: nil, created_at: nil, updated_at: nil, picture: nil> [3] pry(main)> micropost.user_id = User.first.id User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => 1 [4] pry(main)> micropost.content = "Lorem ipsum" => "Lorem ipsum" [5] pry(main)> micropost => #<Micropost:0x007fbbfc35b870 id: nil, content: "Lorem ipsum", user_id: 1, created_at: nil, updated_at: nil, picture: nil>
created_atとupdated_atには何も入ってない
13.1.1.2
<問題>先ほど作ったオブジェクトを使って、micropost.userを実行してみましょう。どのような結果が返ってくるでしょうか? また、micropost.user.nameを実行した場合の結果はどうなるでしょうか?
<回答>
> micropost.user User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] => #<User:0x007fbc0396b358 id: 1, name: "Example User", email: "example@railstutorial.org", created_at: Wed, 30 Aug 2017 01:05:28 UTC +00:00, updated_at: Wed, 30 Aug 2017 01:05:28 UTC +00:00, password_digest: "$2a$10$zjV8yVOjS/lZaq6t2I0Ae.xOrbyTAE/G18QeHKZw/72vRIFOOaSl6", remember_digest: nil, admin: true, activation_digest: "$2a$10$WVoTuhmujpxjL.agLrTGeeZy/56retMcUz2fYMe.8YWfBPf.8ZnRq", activated: true, activated_at: Wed, 30 Aug 2017 01:05:28 UTC +00:00, reset_digest: nil, reset_sent_at: nil> [7] pry(main)> micropost.user.name => "Example User"
13.1.1.3
<問題>先ほど作ったmicropostオブジェクトをデータベースに保存してみましょう。この時点でもう一度マジックカラムの内容を調べてみましょう。今度はどのような値が入っているでしょうか?
<回答>
> micropost.save (0.1ms) begin transaction SQL (0.4ms) INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "Lorem ipsum"], ["user_id", 1], ["created_at", 2017-08-30 01:19:30 UTC], ["updated_at", 2017-08-30 01:19:30 UTC]] (2.5ms) commit transaction => true [11] pry(main)> micropost => #<Micropost:0x007fbbfc35b870 id: 301, content: "Lorem ipsum", user_id: 1, created_at: Wed, 30 Aug 2017 01:19:30 UTC +00:00, updated_at: Wed, 30 Aug 2017 01:19:30 UTC +00:00, picture: nil>
マジックカラムであるcreated_atとupdated_atに値が入った!!
13.1.2 演習 Micropostのバリデーション
13.1.2.1
<問題>Railsコンソールを開き、user_idとcontentが空になっているmicropostオブジェクトを作ってみてください。このオブジェクトに対してvalid?を実行すると、失敗することを確認してみましょう。また、生成されたエラーメッセージにはどんな内容が書かれているでしょうか?
<回答>
> micropost = Micropost.new => #<Micropost:0x007fbbfcdc41b8 id: nil, content: nil, user_id: nil, created_at: nil, updated_at: nil, picture: nil> [2] pry(main)> micropost.valid? => false > micropost.errors.full_messages => ["User must exist", "User can't be blank", "Content can't be blank"]
13.1.2.2
<問題>コンソールを開き、今度はuser_idが空でcontentが141文字以上のmicropostオブジェクトを作ってみてください。このオブジェクトに対してvalid?を実行すると、失敗することを確認してみましょう。また、生成されたエラーメッセージにはどんな内容が書かれているでしょうか?
<回答>
> micropost.content = "a" * 141 => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" > micropost.content.size => 141 > micropost.valid? => false [16] pry(main)> micropost.errors.full_messages => ["User must exist", "User can't be blank", "Content is too long (maximum is 140 characters)"]
13.1.3 演習 User/Micropostの関連付け
13.1.3.1
<問題>データベースにいる最初のユーザーを変数userに代入してください。そのuserオブジェクトを使ってmicropost = user.microposts.create(content: "Lorem ipsum")を実行すると、どのような結果が得られるでしょうか?
<回答>
> user = User.first User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User:0x007fbc06359de0 id: 1, name: "Example User", email: "example@railstutorial.org", created_at: Wed, 30 Aug 2017 01:05:28 UTC +00:00, updated_at: Wed, 30 Aug 2017 01:05:28 UTC +00:00, password_digest: "$2a$10$zjV8yVOjS/lZaq6t2I0Ae.xOrbyTAE/G18QeHKZw/72vRIFOOaSl6", remember_digest: nil, admin: true, activation_digest: "$2a$10$WVoTuhmujpxjL.agLrTGeeZy/56retMcUz2fYMe.8YWfBPf.8ZnRq", activated: true, activated_at: Wed, 30 Aug 2017 01:05:28 UTC +00:00, reset_digest: nil, reset_sent_at: nil> [30] pry(main)> micropost = user.microposts.create(content: "Lorem ipsum") (0.1ms) begin transaction SQL (0.5ms) INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "Lorem ipsum"], ["user_id", 1], ["created_at", 2017-08-30 01:40:35 UTC], ["updated_at", 2017-08-30 01:40:35 UTC]] (2.5ms) commit transaction => #<Micropost:0x007fbc06409380 id: 302, content: "Lorem ipsum", user_id: 1, created_at: Wed, 30 Aug 2017 01:40:35 UTC +00:00, updated_at: Wed, 30 Aug 2017 01:40:35 UTC +00:00, picture: nil>
13.1.3.2
<問題>先ほどの演習課題で、データベース上に新しいマイクロポストが追加されたはずです。user.microposts.find(micropost.id)を実行して、本当に追加されたのかを確かめてみましょう。また、先ほど実行したmicropost.idの部分をmicropostに変更すると、結果はどうなるでしょうか?
<回答>
> user.microposts.find(micropost.id) Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? AND "microposts"."id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? [["user_id", 1], ["id", 302], ["LIMIT", 1]] => #<Micropost:0x007fbc05d60240 id: 302, content: "Lorem ipsum", user_id: 1, created_at: Wed, 30 Aug 2017 01:40:35 UTC +00:00, updated_at: Wed, 30 Aug 2017 01:40:35 UTC +00:00, picture: nil> [43] pry(main)> user.microposts.find(micropost) DEPRECATION WARNING: You are passing an instance of ActiveRecord::Base to `find`. Please pass the id of the object by calling `.id`. (called from <main> at (pry):18) Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? AND "microposts"."id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? [["user_id", 1], ["id", 302], ["LIMIT", 1]] => #<Micropost:0x007fbc051de5c0 id: 302, content: "Lorem ipsum", user_id: 1, created_at: Wed, 30 Aug 2017 01:40:35 UTC +00:00, updated_at: Wed, 30 Aug 2017 01:40:35 UTC +00:00, picture: nil>
13.1.3.3
<問題>user == micropost.userを実行した結果はどうなるでしょうか? また、user.microposts.first == micropost を実行した結果はどうなるでしょうか? それぞれ確認してみてください。
<回答>
> user == micropost.user => true [47] pry(main)> user.microposts.first == micropost => true
13.1.4 演習 マイクロポストを改良する
13.1.4.1
<問題>Micropost.first.created_atの実行結果と、Micropost.last.created_atの実行結果を比べてみましょう。
<回答>
> Micropost.first.created_at.to_s Micropost Load (0.4ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" DESC LIMIT ? [["LIMIT", 1]] => "2017-08-30 01:40:35 UTC" [14] pry(main)> Micropost.last.created_at.to_s Micropost Load (0.4ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" ASC LIMIT ? [["LIMIT", 1]] => "2017-08-30 01:05:42 UTC"
あー僕これ勘違いしてた。first
で取得した時はid
順で取ってくると思ってた。
default_scope
設定してるんだから、そりゃー降順で取ってくるわな。
きちんと発行されたSQL見るのは大事ですね。
13.1.4.2
<問題>Micropost.firstを実行したときに発行されるSQL文はどうなっているでしょうか? 同様にして、Micropost.lastの場合はどうなっているでしょうか? ヒント: それぞれをコンソール上で実行したときに表示される文字列が、SQL文になります。
<回答>
> Micropost.first Micropost Load (0.4ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" DESC LIMIT ? [["LIMIT", 1]] [19] pry(main)> Micropost.last Micropost Load (0.4ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" ASC LIMIT ? [["LIMIT", 1]]
firstは、DESC。lastは、ASCで発行されていることが分かる。
13.1.4.3
<問題>データベース上の最初のユーザーを変数userに代入してください。そのuserオブジェクトが最初に投稿したマイクロポストのidはいくつでしょうか? 次に、destroyメソッドを使ってそのuserオブジェクトを削除してみてください。削除すると、そのuserに紐付いていたマイクロポストも削除されていることをMicropost.findで確認してみましょう。
<回答>
# 最初に投稿したマイクロポスト > user.microposts.last Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" ASC LIMIT ? [["user_id", 1], ["LIMIT", 1]] => #<Micropost:0x007fbc00dda3b0 id: 1, content: "Cum consequatur enim quibusdam aliquid corrupti reprehenderit et.", user_id: 1, created_at: Wed, 30 Aug 2017 01:05:42 UTC +00:00, updated_at: Wed, 30 Aug 2017 01:05:42 UTC +00:00, picture: nil> # ユーザーをdestroy > user.destroy (0.1ms) begin transaction Micropost Load (0.6ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC [["user_id", 1]] SQL (0.3ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 302]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 301]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 295]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 289]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 283]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 277]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 271]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 265]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 259]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 253]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 247]] SQL (0.2ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 241]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 235]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 229]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 223]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 217]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 211]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 205]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 199]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 193]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 187]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 181]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 175]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 169]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 163]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 157]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 151]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 145]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 139]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 133]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 127]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 121]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 115]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 109]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 103]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 97]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 91]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 85]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 79]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 73]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 67]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 61]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 55]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 49]] SQL (0.0ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 43]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 37]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 31]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 25]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 19]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 13]] SQL (0.2ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 7]] SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 1]] SQL (0.2ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 1]] (3.1ms) commit transaction => #<User:0x007fbc04851110 id: 1, name: "Example User", email: "example@railstutorial.org", created_at: Wed, 30 Aug 2017 01:05:28 UTC +00:00, updated_at: Wed, 30 Aug 2017 01:05:28 UTC +00:00, password_digest: "$2a$10$zjV8yVOjS/lZaq6t2I0Ae.xOrbyTAE/G18QeHKZw/72vRIFOOaSl6", remember_digest: nil, admin: true, activation_digest: "$2a$10$WVoTuhmujpxjL.agLrTGeeZy/56retMcUz2fYMe.8YWfBPf.8ZnRq", activated: true, activated_at: Wed, 30 Aug 2017 01:05:28 UTC +00:00, reset_digest: nil, reset_sent_at: nil> > Micropost.find(1) Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? [["id", 1], ["LIMIT", 1]] ActiveRecord::RecordNotFound: Couldn't find Micropost with 'id'=1 > user.find(1) NoMethodError: undefined method `find' for #<User:0x007fbc04851110>
13.2.1 演習 マイクロポストの描画
13.2.1.1
<問題>7.3.3で軽く説明したように、今回ヘルパーメソッドとして使ったtime_ago_in_wordsメソッドは、Railsコンソールのhelperオブジェクトから呼び出すことができます。このhelperオブジェクトのtime_ago_in_wordsメソッドを使って、3.weeks.agoや6.months.agoを実行してみましょう。
<回答>
> helper.time_ago_in_words(3.weeks.ago) => "21 days" [48] pry(main)> helper.time_ago_in_words(6.months.ago) => "6 months"
13.2.1.2
<問題>helper.time_ago_in_words(1.year.ago)と実行すると、どういった結果が返ってくるでしょうか?
<回答>
> helper.time_ago_in_words(1.year.ago) => "about 1 year"
13.2.1.3
<問題>micropostsオブジェクトのクラスは何でしょうか? ヒント: リスト 13.23内のコードににあるように、まずはpaginateメソッド (引数はpage: nil) でオブジェクトを取得し、その後classメソッドを呼び出してみましょう。
<回答>
> user = User.first > microposts = user.microposts.paginate(page: 1) > microposts.class => Micropost::ActiveRecord_AssociationRelation
13.2.2 演習 マイクロポストのサンプル
13.2.2.1
<問題>(1..10).to_a.take(6)というコードの実行結果を推測できますか? 推測した値が合っているかどうか、実際にコンソールを使って確認してみましょう。
<回答>
> (1..10).to_a.take(6) => [1, 2, 3, 4, 5, 6]
13.2.2.2
<問題>先ほどの演習にあったto_aメソッドの部分は本当に必要でしょうか? 確かめてみてください。
<回答>
> (1..10).take(6) => [1, 2, 3, 4, 5, 6] > (1..10).class => Range [72] pry(main)> (1..10).to_a.class => Array
13.2.2.3
<問題>Fakerはlorem ipsum以外にも、非常に多種多様の事例に対応しています。Fakerのドキュメント (英語) を眺めながら画面に出力する方法を学び、実際に大学名や電話番号、Hipster IpsumやChuck Norris facts (参考: チャック・ノリスの真実) を画面に出力してみましょう。(訳注: もちろん日本語にも対応していて、例えば沖縄らしい用語を出力するfaker-okinawaもあります。ぜひ遊んでみてください。)
<回答>
> Faker::Cat.name => "Shadow" > Faker::SlackEmoji.people => ":stuck_out_tongue_winking_eye:" > Faker::Music.key => "Fb"
13.2.3 演習 プロフィール画面のマイクロポストをテストする
13.2.3.1
<問題>リスト 13.28にある2つの’h1’のテストが正しいか確かめるため、該当するアプリケーション側のコードをコメントアウトしてみましょう。テストが green から redに変わることを確認してみてください。
<回答>
# app/views/users/show.html.erb 5 <!-- <h1> 6 <%= gravatar_for @user %> 7 <%= @user.name %> 8 </h1> -->
13.2.3.2
<問題>リスト 13.28にあるテストを変更して、will_paginateが1度のみ表示されていることをテストしてみましょう。ヒント: 表 5.2を参考にしてください。
<回答>
test "profile display" do get user_path(@user) assert_template 'users/show' assert_select 'title', full_title(@user.name) assert_select 'h1', text: @user.name assert_select 'h1>img.gravatar' assert_match @user.microposts.count.to_s, response.body assert_select 'div.pagination', count: 1 # ここを修正。 @user.microposts.paginate(page: 1).each do |micropost| assert_match micropost.content, response.body end end
13.3.1 演習 マイクロポストのアクセス制御
13.3.1.1
<問題>なぜUsersコントローラ内にあるlogged_in_userフィルターを残したままにするとマズイのでしょうか? 考えてみてください。
<回答> コードが重複してしまうため
13.3.2 演習 マイクロポストを作成する
13.3.2.1
<問題>Homeページをリファクタリングして、if-else文の分岐のそれぞれに対してパーシャルを作ってみましょう。
<回答>
# app/views/static_pages/home.html.erb <% if logged_in? %> <%= render 'user_logged_in' %> <% else %> <%= render 'user_logged_in' %> <% end %>
# app/views/static_pages/_user_logged_in.html.erb <div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= render 'shared/user_info' %> </section> <section class="micropost_form"> <%= render 'shared/micropost_form' %> </section> </aside> <div class="col-md-8"> <h3>Micropost Feed</h3> <%= render 'shared/feed' %> </div> </div>
# app/views/static_pages/_user_not_logged_in.html.erb <% provide(:title, "Home") %> <div class="center jumbotron"> <h1>Welcome to the Sample App</h1> <h2> This is the home page for the <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </h2> <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %> </div> <%= link_to image_tag("rails.png", alt: "Rails logo"), "https://rubyonrails.org/" %>
13.3.3 演習 フィードの原型
13.3.3.1
<問題>新しく実装したマイクロポストの投稿フォームを使って、実際にマイクロポストを投稿してみましょう。Railsサーバーのログ内にあるINSERT文では、どういった内容をデータベースに送っているでしょうか? 確認してみてください。
<回答>
INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "hakuの休日"], ["user_id", 101], ["created_at", 2017-08-30 05:01:10 UTC], ["updated_at", 2017-08-30 05:01:10 UTC]]
13.3.3.2
<問題>コンソールを開き、user変数にデータベース上の最初のユーザーを代入してみましょう。その後、Micropost.where("user_id = ?", user.id)とuser.microposts、そしてuser.feedをそれぞれ実行してみて、実行結果がすべて同じであることを確認してみてください。ヒント: ==で比較すると結果が同じかどうか簡単に判別できます。
<回答>
> user.feed Micropost Load (0.4ms) SELECT "microposts".* FROM "microposts" WHERE (user_id=2) ORDER BY "microposts"."created_at" DESC > Micropost.where("user_id = ?", user.id) Micropost Load (0.4ms) SELECT "microposts".* FROM "microposts" WHERE (user_id = 2) ORDER BY "microposts"."created_at" DESC > user.feed == user.microposts Micropost Load (0.4ms) SELECT "microposts".* FROM "microposts" WHERE (user_id=2) ORDER BY "microposts"."created_at" DESC => true [57] pry(main)> Micropost.where("user_id = ?", user.id) == user.microposts Micropost Load (0.4ms) SELECT "microposts".* FROM "microposts" WHERE (user_id = 2) ORDER BY "microposts"."created_at" DESC => true # なぜか`false`になる。発行されているSQLも一緒なのに。なぜだ? > Micropost.where("user_id = ?", user.id) == user.feed => false
13.3.4 演習 マイクロポストを削除する
13.3.4.1
<問題>マイクロポストを作成し、その後、作成したマイクロポストを削除してみましょう。次に、Railsサーバーのログを見てみて、DELETE文の内容を確認してみてください。
<回答>
DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 303]]
13.3.4.2
<問題>redirect_to request.referrer || root_urlの行をredirect_back(fallback_location: root_url)と置き換えてもうまく動くことを、ブラウザを使って確認してみましょう (このメソッドはRails 5から新たに導入されました)。
<回答> エラーなく削除できることを確認した。
13.3.5 演習 フィード画面のマイクロポストをテストする
13.3.5.1
<問題>リスト 13.55で示した4つのコメント (「無効な送信」など) のそれぞれに対して、テストが正しく動いているか確認してみましょう。具体的には、対応するアプリケーション側のコードをコメントアウトし、テストが redになることを確認し、元に戻すと greenになることを確認してみましょう。
<回答>
くそー動かない。画面でも再現する。
ERROR["test_micropost_interface", MicropostsInterfaceTest, 3.208587193999847] test_micropost_interface#MicropostsInterfaceTest (3.21s) ActionView::Template::Error: ActionView::Template::Error: Missing partial microposts/_user_logged_in, application/_user_logged_in with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :coffee, :jbuilder]}. Searched in: * "/Users/taku/rails/railstutorial/railstutorial_13/app/views" app/views/static_pages/home.html.erb:2:in `_app_views_static_pages_home_html_erb___3536680938325879454_70206177007920' app/controllers/microposts_controller.rb:12:in `create' test/integration/microposts_interface_test.rb:16:in `block (2 levels) in <class:MicropostsInterfaceTest>' test/integration/microposts_interface_test.rb:15:in `block in <class:MicropostsInterfaceTest>'
解決した!
# app/views/static_pages/home.html.erb <% if logged_in? %> <%= render 'static_pages/user_logged_in' %> # `user_logged_in`だけ書いてたのが原因。同じディレクトリでもフォルダ名から書く! <% else %> <%= render 'static_pages/user_not_logged_in' %> # `user_logged_in`だけ書いてたのが原因。同じディレクトリでもフォルダ名から書く! <% end %>
13.3.5.2
<問題>サイドバーにあるマイクロポストの合計投稿数をテストしてみましょう。このとき、単数形 (micropost) と複数形 (microposts) が正しく表示されているかどうかもテストしてください。ヒント: リスト 13.57を参考にしてみてください。
<回答>
# test/integration/microposts_interface_test.rb require 'test_helper' class MicropostInterfaceTest < ActionDispatch::IntegrationTest def setup @user = users(:michael) end . . . test "micropost sidebar count" do log_in_as(@user) get root_path assert_match "#{@user.microposts.count} microposts", response.body # ここを修正 # まだマイクロポストを投稿していないユーザー other_user = users(:malory) log_in_as(other_user) get root_path assert_match "0 microposts", response.body other_user.microposts.create!(content: "A micropost") get root_path assert_match "1 micropost", response.body # ここを修正 end end
13.4.1 演習 基本的な画像アップロード
13.4.1.1
<問題>画像付きのマイクロポストを投稿してみましょう。もしかして、大きすぎる画像が表示されてしまいましたか? (心配しないでください、次の13.4.3でこの問題を直します)。
<回答> 問題なくアップロードできる!
13.4.1.2
<問題>リスト 13.63に示すテンプレートを参考に、13.4で実装した画像アップローダーをテストしてください。テストの準備として、まずはサンプル画像をfixtureディレクトリに追加してください (コマンド例: cp app/assets/images/rails.png test/fixtures/)。リスト 13.63で追加したテストでは、Homeページにあるファイルアップロードと、投稿に成功した時に画像が表示されているかどうかをチェックしています。なお、テスト内にあるfixture_file_uploadというメソッドは、fixtureで定義されたファイルをアップロードする特別なメソッドです18。ヒント: picture属性が有効かどうかを確かめるときは、11.3.3で紹介したassignsメソッドを使ってください。このメソッドを使うと、投稿に成功した後にcreateアクション内のマイクロポストにアクセスするようになります。
<回答>
# test/integration/microposts_interface_test.rb require 'test_helper' class MicropostsInterfaceTest < ActionDispatch::IntegrationTest def setup @user = users(:michael) end test "micropost interface" do log_in_as(@user) get root_path assert_match "#{@user.microposts.count} microposts", response.body assert_select "div.pagination" assert_select 'input[type=file]' # 無効な送信 assert_no_difference 'Micropost.count' do post microposts_path, params: {micropost: {content: ""}} end assert_select 'div#error_explanation' # 有効な送信 content = "This micropost really ties the room together" picture = fixture_file_upload('test/fixtures/rails.png', 'image/png') assert_difference 'Micropost.count', 1 do post microposts_path, params: {micropost: {content: content, picture: picture}} end assert assigns(:micropost).picture? assert_redirected_to root_url follow_redirect! assert_match content, response.body assert_match "#{@user.microposts.count} micropost", response.body # 投稿を削除する assert_select 'a', text: 'delete' first_micropost = @user.microposts.paginate(page: 1).first assert_difference 'Micropost.count', -1 do delete micropost_path(first_micropost) end # 違うユーザーのプロフィールにアクセス(削除リンクが無いこと) get user_path(users(:archer)) assert_select 'a', text: 'delete', count: 0 end end
エラーになるので、改めて確認する。
13.4.2 演習 画像の検証
13.4.2.1
<問題>5MB以上の画像ファイルを送信しようとした場合、どうなりますか?
<回答> メッセージウィンドウが表示される(Maximum file size is 5MB. Please choose a smaller file.)
13.4.2.1
<問題>無効な拡張子のファイルを送信しようとした場合、どうなりますか?
<回答>
Picture translation missing: en.errors.messages.extension_whitelist_error
13.4.3 演習 画像のリサイズ
13.4.3.1
<問題>解像度の高い画像をアップロードし、リサイズされているかどうか確認してみましょう。画像が長方形だった場合、リサイズはうまく行われているでしょうか?
<回答> 問題なし
13.4.3.2
<問題>既にリスト 13.63のテストを追加していた場合、この時点でテストスイートを走らせるとエラーメッセージが表示されるようになるはずです。このエラーを取り除いてみましょう。ヒント: リスト 13.68にある設定ファイルを修正し、テスト時はCarrierWaveに画像のリサイズをさせないようにしてみましょう。
<回答>
- エラーにならない
- config/initializers/skip_image_resizing.rb
が存在しない