Ruby on Railsチュートリアルの演習問題と解答をまとめる。
第14章 ユーザーをフォローする - Railsチュートリアル
アウトプットすることで、より自分の理解を深めることを目的としています。
自分なりに調べて考えた回答のため、記載内容に誤りがある場合はコメントいただけると幸いです。
14.1.1 データモデルの問題(および解決策)
演習1
図 14.7のid=1のユーザーに対してuser.following.map(&:id)を実行すると、結果はどのようになるでしょうか? 想像してみてください。ヒント: 4.3.2で紹介したmap(&:method_name)のパターンを思い出してください。例えばuser.following.map(&:id)の場合、idの配列を返します。
解答
id=1にフォローされているユーザー(id: 2, 7, 10,)のidをそれぞれ1つずつ返す
演習2
図 14.7を参考にして、id=2のユーザーに対してuser.followingを実行すると、結果はどのようになるでしょうか? また、同じユーザーに対してuser.following.map(&:id)を実行すると、結果はどのようになるでしょうか? 想像してみてください。
解答
id=2にフォローされているユーザー(id: 1)を返す
14.1.2 User/Relationshipの関連付け
演習1
コンソールを開き、表 14.1のcreateメソッドを使ってActiveRelationshipを作ってみましょう。データベース上に2人以上のユーザーを用意し、最初のユーザーが2人目のユーザーをフォローしている状態を作ってみてください。
解答
>> user = User.first
(省略)
>> user2 = User.second
(省略)
>> user.active_relationships.create(followed_id: user2.id)
(0.1ms) begin transaction
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
SQL (2.5ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 2], ["created_at", "2019-11-04 10:27:41.371417"], ["updated_at", "2019-11-04 10:27:41.371417"]]
(1.6ms) commit transaction
=> #<Relationship id: 1, follower_id: 1, followed_id: 2, created_at: "2019-11-04 10:27:41", updated_at: "2019-11-04 10:27:41">
演習2
先ほどの演習を終えたら、active_relationship.followedの値とactive_relationship.followerの値を確認し、それぞれの値が正しいことを確認してみましょう。
解答
=> #<Relationship id: 1, follower_id: 1, followed_id: 2, created_at: "2019-11-04 10:27:41", updated_at: "2019-11-04 10:27:41">
14.1.3 Relationshipのバリデーション
演習1
リスト 14.5のバリデーションをコメントアウトしても、テストが成功したままになっていることを確認してみましょう。(以前のRailsのバージョンでは、このバリデーションが必須でしたが、Rails 5から必須ではなくなりました。今回はフォロー機能の実装を優先しますが、この手のバリデーションが省略されている可能性があることを頭の片隅で覚えておくと良いでしょう。)
解答
動作確認のみなので省略
14.1.4 フォローしているユーザー
演習1
コンソールを開き、リスト 14.9のコードを順々に実行してみましょう。
解答
>> michael = User.third
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ? [["LIMIT
", 1], ["OFFSET", 2]]
=> #<User id: 3, name: "Ryley Abbott DVM", email: "example-2@railstutorial.org", created_at: "2019-11-03
00:46:18", updated_at: "2019-11-03 00:46:18", password_digest: "$2a$10$LsvPDhcVk3jlVoEyO4PSmOyH/n6mXynvrwC1MpSxgYs...", remember_digest: nil, admin: false, activation_digest: "$2a$10$KC4QN336HKYIOXoQH3fJ2.T1Gy8C6GJvA9du.cmc85m...", activated: true, activated_at: "2019-11-03 00:46:18", reset_digest: nil, reset_
sent_at: nil>
>> archer = User.fourth
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? OFFSET ? [["LIMIT", 1], ["OFFSET", 3]]
=> #<User id: 4, name: "Grace Bernier DDS", email: "example-3@railstutorial.org", created_at: "2019-11-03 00:46:18", updated_at: "2019-11-03 00:46:18", password_digest: "$2a$10$kw8OUcJpxH6.qYhDOqCiYuCR/jHjNCp8w/e2gcCn/Ad...", remember_digest: nil, admin: false, activation_digest: "$2a$10$/CmJZJhdONUJYJCkobvs8eynbxyZiDGEUCpMJkTbyze...", activated: true, activated_at: "2019-11-03 00:46:18", reset_digest: nil, reset_sent_at: nil>
>> michael.following?(archer)
User Exists (0.3ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 3], ["id", 4], ["LIMIT", 1]]
=> false
>> michael.follow(archer)
(0.1ms) begin transaction
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
SQL (1.3ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 3], ["followed_id", 4], ["created_at", "2019-11-04 11:07:29.366144"], ["updated_at", "2019-11-04 11:07:29.366144"]]
(2.4ms) commit transaction
User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? LIMIT ? [["follower_id", 3], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 4, name: "Grace Bernier DDS", email: "example-3@railstutorial.org", created_at: "2019-11-03 00:46:18", updated_at: "2019-11-03 00:46:18", password_digest: "$2a$10$kw8OUcJpxH6.qYhDOqCiYuCR/jHjNCp8w/e2gcCn/Ad...", remember_digest: nil, admin: false, activation_digest: "$2a$10$/CmJZJhdONUJYJCkobvs8eynbxyZiDGEUCpMJkTbyze...", activated: true, activated_at: "2019-11-03 00:46:18", reset_digest: nil, reset_sent_at: nil>]>
>> michael.following?(archer)
User Exists (0.4ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 3], ["id", 4], ["LIMIT", 1]]
=> true
>> michael.unfollow(archer)
Relationship Load (0.3ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ? [["follower_id", 3], ["followed_id", 4], ["LIMIT", 1]]
(0.1ms) begin transaction
SQL (0.6ms) DELETE FROM "relationships" WHERE "relationships"."id" = ? [["id", 2]]
(1.3ms) commit transaction
=> #<Relationship id: 2, follower_id: 3, followed_id: 4, created_at: "2019-11-04 11:07:29", updated_at: "2019-11-04 11:07:29">
>> michael.following?(archer)
User Exists (0.4ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 3], ["id", 4], ["LIMIT", 1]]
=> false
>>
演習2
先ほどの演習の各コマンド実行時の結果を見返してみて、実際にはどんなSQLが出力されたのか確認してみましょう。
解答
演習1参照
14.1.5 フォロワー
演習1
コンソールを開き、何人かのユーザーが最初のユーザーをフォローしている状況を作ってみてください。最初のユーザーをuserとすると、user.followers.map(&:id)の値はどのようになっているでしょうか?
解答
>> user = User.first
>> user2 = User.second
>> user3 = User.third
>> user4 = User.fourth
>> user2.follow(user)
>> user3.follow(user)
>> user4.follow(user)
>> user.followers.map(&:id)
User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 1]]
=> [2, 3, 4]
演習2
上の演習が終わったら、user.followers.countの実行結果が、先ほどフォローさせたユーザー数と一致していることを確認してみましょう。
解答
>> user.followers.count
(0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 1]]
=> 3
user_idが2. 3. 4の3人なので一致している
演習3
user.followers.countを実行した結果、出力されるSQL文はどのような内容になっているでしょうか? また、user.followers.to_a.countの実行結果と違っている箇所はありますか? ヒント: もしuserに100万人のフォロワーがいた場合、どのような違いがあるでしょうか? 考えてみてください。
解答
>> user.followers.count
(0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 1]]
=> 3
>> user.followers.to_a.count
=> 3
実行結果にsql文が出力されない。
100万人のフォロワーがいたとする
user.followers.countの場合、DB内でfollowersの合計を算出し返す。
user.followers.to_a.countの場合、DBから100万回データを取り出し配列を生成しその数の合計を返す。
下記実行結果をcountしているため100万人もいたら時間がかかる。
たった3人でこの量、DBの負担がすごそう。。
>> user.followers.to_a
=> [#<User id: 2, name: "Mr. Jade Renner", email: "example-1@railstutorial.org", created_at: "2019-11-03 00:46:17", updated_at: "2019-11-03 00:46:17", password_digest: "$2a$10$MBzfZ.ZcjOUs9GYGkTZBH.kY6U25hrORxv8DUsZ1C6o...", remember_digest: nil, admin: false, activation_digest: "$2a$10$7X19s7oIJNvcz.QJWJkvXOFDD9abC0alx/hdNtvzGCD...", activated: true, activated_at: "2019-11-03 00:46:17", reset_digest: nil, reset_sent_at: nil>, #<User id: 3, name: "Ryley Abbott DVM", email: "example-2@railstutorial.org", created_at: "2019-11-03 00:46:18", updated_at: "2019-11-03 00:46:18", password_digest: "$2a$10$LsvPDhcVk3jlVoEyO4PSmOyH/n6mXynvrwC1MpSxgYs...", remember_digest: nil, admin: false, activation_digest: "$2a$10$KC4QN336HKYIOXoQH3fJ2.T1Gy8C6GJvA9du.cmc85m...", activated: true, activated_at: "2019-11-03 00:46:18", reset_digest: nil, reset_sent_at: nil>, #<User id: 4, name: "Grace Bernier DDS", email: "example-3@railstutorial.org", created_at: "2019-11-03 00:46:18", updated_at: "2019-11-03 00:46:18", password_digest: "$2a$10$kw8OUcJpxH6.qYhDOqCiYuCR/jHjNCp8w/e2gcCn/Ad...", remember_digest: nil, admin: false, activation_digest: "$2a$10$/CmJZJhdONUJYJCkobvs8eynbxyZiDGEUCpMJkTbyze...", activated: true, activated_at: "2019-11-03 00:46:18", reset_digest: nil, reset_sent_at: nil>]
14.2.1 フォローのサンプルデータ
演習問題1
コンソールを開き、User.first.followers.countの結果がリスト 14.14で期待している結果と合致していることを確認してみましょう。
解答
>> User.first.followers.count
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
(0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 1]]
=> 38
演習問題2
先ほどの演習と同様に、User.first.following.countの結果も合致していることを確認してみましょう。
解答
>> User.first.following.count
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
(0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? [["follower_id", 1]]
=> 49
14.2.2 統計と [Follow] フォーム
演習問題1
ブラウザから /users/2 にアクセスし、フォローボタンが表示されていることを確認してみましょう。同様に、/users/5 では [Unfollow] ボタンが表示されているはずです。さて、/users/1 にアクセスすると、どのような結果が表示されるでしょうか?
解答
unfollowボタンが白くて見えない。
loginしているのでボタンがない
演習問題2
ブラウザからHomeページとプロフィールページを表示してみて、統計情報が正しく表示されているか確認してみましょう。
解答
統計情報は一致している
Homeページ
プロフィールページ
演習問題3
Homeページに表示されている統計情報に対してテストを書いてみましょう。ヒント: リスト 13.28で示したテストに追加してみてください。同様にして、プロフィールページにもテストを追加してみましょう。
解答
Homeページのテストは下記に追加
[test/integration/site_layout_test.rb]
require 'test_helper'
class SiteLayoutTest < ActionDispatch::IntegrationTest
(中略)
test "layout links when logged in user" do
log_in_as(@user)
get root_path
assert_select "a[href=?]", root_path, count: 2
assert_select "a[href=?]", help_path
assert_select "a[href=?]", about_path
assert_select "a[href=?]", contact_path
assert_select "a[href=?]", users_path
assert_select "a[href=?]", user_path(@user)
assert_select "a[href=?]", edit_user_path(@user)
assert_select "a[href=?]", logout_path
assert_match @user.active_relationships.count.to_s, response.body
assert_match @user.passive_relationships.count.to_s, response.body
end
end
プロフィールページは下記に追加
[test/integration/users_profile_test.rb]
require 'test_helper'
class UsersProfileTest < ActionDispatch::IntegrationTest
include ApplicationHelper
def setup
@user = users(:michael)
end
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
assert_match @user.active_relationships.count.to_s, response.body
assert_match @iser.passive_relationhips.count.to_s, response.body
end
end
14.2.3 [ Following ] と [ Followers ] ページ
演習問題1
ブラウザから /users/1/followers と /users/1/following を開き、それぞれが適切に表示されていることを確認してみましょう。サイドバーにある画像は、リンクとしてうまく機能しているでしょうか?
解答
following
followers
演習問題2
リスト 14.29のassert_selectに関連するコードをコメントアウトしてみて、テストが正しく red に変わることを確認してみましょう。
解答
コメントアウト2箇所必要です。
・link_to gravatar...の行のみだと、SyntaxErrorが出てしまうのでeach分ごとコメントアウトする。
・render @usersも忘れずにコメントアウト!
[app/views/users/show_follow.html.erb]
<% provide(:title, @title) %>
<div class="row">
<aside class="col-md-4">
<section class="uesr_info">
<%= gravatar_for @user %>
<h1><%= @user.name %></h1>
<span><%= link_to "view my profile", @user %></span>
<span><b>Microposts:</b> <%= @user.microposts.count %></span>
</section>
<section class="stats">
<%= render 'shared/stats' %>
<% if @users.any? %>
<div class="user_avatars">
<%# @users.each do |user| %>
<%#= link_to gravatar_for(user, size: 30), user %>
<%# end %>
</div>
<% end %>
</section>
</aside>
<div class="col-md-8">
<h3><%= @title %></h3>
<% if @users.any? %>
<ul class="users follow">
<%#= render @users %>
</ul>
<%= will_paginate %>
<% end %>
</div>
</div>
テストでassert_selectの行がred(Expected at least 1 element matching "a[href="/users/409608538"]", found 0..)になるのを確認。
最後直すのも忘れずに!
14.2.4 [ Follow ] ボタン(基本編)
演習問題1
ブラウザ上から /users/2 を開き、[Follow] と [Unfollow] を実行してみましょう。うまく機能しているでしょうか?
解答
実行してみましょう!
演習問題2
先ほどの演習を終えたら、Railsサーバーのログを見てみましょう。フォロー/フォロー解除が実行されると、それぞれどのテンプレートが描画されているでしょうか?
解答
フォロー
Rendering users/show.html.erb
Started POST "/relationships" for 127.0.0.1 at 2019-11-05 22:51:57 +0900
Processing by RelationshipsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"DtJHn4u00RaMzwLppiLdfSHm2mqE8zoN2p4JbZSbtYV1Ngurl+wXDvr8kb2b8wEsh9DY40oBBZRMCpL7gQ8nDQ==", "followed_id"=>"2", "commit"=>"Follow"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
(0.1ms) begin transaction
CACHE User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
SQL (1.6ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 2], ["created_at", "2019-11-05 13:51:57.239627"], ["updated_at", "2019-11-05 13:51:57.239627"]]
(1.2ms) commit transaction
Redirected to http://localhost:3000/users/2
Completed 302 Found in 15ms (ActiveRecord: 3.6ms)
Started GET "/users/2" for 127.0.0.1 at 2019-11-05 22:51:57 +0900
Processing by UsersController#show as HTML
Parameters: {"id"=>"2"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
Rendering users/show.html.erb within layouts/application
フォロー解除
同じく、Rendering users/show.html.erb
Started DELETE "/relationships/88" for 127.0.0.1 at 2019-11-05 22:52:02 +0900
Processing by RelationshipsController#destroy as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"n5ZZ1I9/igmIjRJ8losPb4MGgcBztJ5/IN6/2VFF1jfpq2Xr/h5uS39iTzz8o2kRlIHYlJvfQKlUDYpSifoOTw==", "commit"=>"Unfollow", "id"=>"88"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Relationship Load (0.4ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."id" = ? LIMIT ? [["id", 88], ["LIMIT", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
Relationship Load (0.4ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ? [["follower_id", 1], ["followed_id", 2], ["LIMIT", 1]]
(0.1ms) begin transaction
SQL (1.1ms) DELETE FROM "relationships" WHERE "relationships"."id" = ? [["id", 88]]
(1.6ms) commit transaction
Redirected to http://localhost:3000/users/2
Completed 302 Found in 16ms (ActiveRecord: 4.2ms)
Started GET "/users/2" for 127.0.0.1 at 2019-11-05 22:52:02 +0900
Processing by UsersController#show as HTML
Parameters: {"id"=>"2"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
Rendering users/show.html.erb within layouts/application
14.2.5 [ Follow ] ボタン( Ajax編 )
演習問題1
ブラウザから /users/2 にアクセスし、うまく動いているかどうか確認してみましょう。
解答
ブラウザにて動作確認
演習問題2
先ほどの演習で確認が終わったら、Railsサーバーのログを閲覧し、フォロー/フォロー解除を実行した直後のテンプレートがどうなっているか確認してみましょう。
解答
Started POST "/relationships" for 127.0.0.1 at 2019-11-06 22:37:57 +0900
Processing by RelationshipsController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"quG2QBvRnG9iE+GV/wn9f9GPcma2L5nT+X07bSNILbLRBfp0B4ladxQgcsHC2CEud7lw73jdpkpv6aD7Nty/Og==", "followed_id"=>"2", "commit"=>"Follow"}
(省略)
(0.2ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 2]]
Rendered relationships/create.js.erb (6.2ms)
Completed 200 OK in 25ms (Views: 7.6ms | ActiveRecord: 5.6ms)
Started DELETE "/relationships/90" for 127.0.0.1 at 2019-11-06 22:38:00 +0900
Processing by RelationshipsController#destroy as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"jrAkVxicFuNQQ96quS/UlJwg8zTi2mXGYYFtbJp/uHkgeNtoqU7k1jr0XF934QVDIulqxky9kEISQ8d4XCAXfQ==", "commit"=>"Unfollow", "id"=>"90"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1
(省略)
(0.1ms) begin transaction
SQL (1.0ms) DELETE FROM "relationships" WHERE "relationships"."id" = ? [["id", 90]]
(1.5ms) commit transaction
Rendering relationships/destroy.js.erb
Rendered users/_follow.html.erb (3.3ms)
(0.4ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 2]]
Rendered relationships/destroy.js.erb (10.7ms)
Completed 200 OK in 29ms (Views: 15.7ms | ActiveRecord: 4.3ms)
jsファイルが表示されている
14.2.6 フォローをテストする
演習問題1
リスト 14.36のrespond_toブロック内の各行を順にコメントアウトしていき、テストが正しくエラーを検知できるかどうか確認してみましょう。実際、どのテストケースが落ちたでしょうか?
解答
まず、現時点でテストがGreenになることを確認する
1行目をコメントアウトする
[app/controllers/relationships_controller.rb]
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
@user = User.find(params[:followed_id])
current_user.follow(@user)
respond_to do |format|
# format.html { redirect_to @user }
format.js
end
end
.
.
.
end
テスト -> Red
ERROR["test_should_follow_a_user_the_standard_way", FollowingTest, 1.0399349999934202]
test_should_follow_a_user_the_standard_way#FollowingTest (1.04s)
ActionController::UnknownFormat: ActionController::UnknownFormat: ActionController::UnknownFormat
app/controllers/relationships_controller.rb:7:in `create'
test/integration/following_test.rb:31:in `block (2 levels) in <class:FollowingTest>'
test/integration/following_test.rb:30:in `block in <class:FollowingTest>'
73/73: [===========================================================] 100% Time: 00:00:02, Time: 00:00:02
2行目をコメントアウトする
[app/controllers/relationships_controller.rb]
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
@user = User.find(params[:followed_id])
current_user.follow(@user)
respond_to do |format|
format.html { redirect_to @user }
# format.js
end
end
.
.
.
end
テスト -> Green
1行目の format.html { redirect_to @user }を消すとテストが落ちる
演習問題2
リスト 14.40のxhr: trueがある行のうち、片方のみを削除するとどういった結果になるでしょうか? このとき発生する問題の原因と、なぜ先ほどの演習で確認したテストがこの問題を検知できたのか考えてみてください。
解答
[test/integration/following_test.rb]
test "should follow a user with Ajax" do
assert_difference '@user.following.count', 1 do
# post relationships_path, xhr: true, params: { followed_id: @other.id}
end
end
エラー内容
FAIL["test_should_follow_a_user_with_Ajax", FollowingTest, 1.2087310000060825]
test_should_follow_a_user_with_Ajax#FollowingTest (1.21s)
"@user.following.count" didn't change by 1.
Expected: 2
Actual: 1
test/integration/following_test.rb:36:in `block in <class:FollowingTest>'
postがないのでもちろんテストはエラー。
なぜ先ほどの演習で確認したテストがこの問題を検知できたのか
3日間調べたが理解できなかったので飛ばします。
14.3.1 動機と計画
演習問題1
マイクロポストのidが正しく並んでいると仮定して (すなわち若いidの投稿ほど古くなる前提で)、図 14.22のデータセットでuser.feed.map(&:id)を実行すると、どのような結果が表示されるでしょうか? 考えてみてください。ヒント: 13.1.4で実装したdefault_scopeを思い出してください。
解答
default_scope -> { order(created_at: :desc) } と設定しているので、マイクロポストの投稿時間を基準に降順で表示される。
14.3.2 フィードを初めて実装する
演習問題1
リスト 14.44において、現在のユーザー自身の投稿を含めないようにするにはどうすれば良いでしょうか? また、そのような変更を加えると、リスト 14.42のどのテストが失敗するでしょうか?
解答
("user_id = ?", id)で自分の投稿を表示しているので削除する
def feed
Micropost.where("user_id IN (?)", following_ids)
end
テストがRedになる
paginationが見つかりませんも検出される(paginationはちゃんと表示されているのに)
% rails test
Running via Spring preloader in process 8714
Started with run options
FAIL["test_micropost_interface", MicropostsInterfaceTest, 1.0278030000044964]
test_micropost_interface#MicropostsInterfaceTest (1.03s)
Expected at least 1 element matching "div.pagination", found 0..
Expected 0 to be >= 1.
test/integration/microposts_interface_test.rb:12:in `block in <class:MicropostsInterfaceTest>'
FAIL["test_feed_should_have_the_right_posts", UserTest, 2.1715810000314377]
test_feed_should_have_the_right_posts#UserTest (2.17s)
Expected false to be truthy.
test/models/user_test.rb:110:in `block (2 levels) in <class:UserTest>'
test/models/user_test.rb:109:in `block in <class:UserTest>'
74/74: [===========================================================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.50530s
74 tests, 339 assertions, 2 failures, 0 errors, 0 skips
演習問題2
リスト 14.44において、フォローしているユーザーの投稿を含めないようにするにはどうすれば良いでしょうか? また、そのような変更を加えると、リスト 14.42のどのテストが失敗するでしょうか?
解答
演習1の逆バージョン
def feed
Micropost.where("user_id = ?", id)
end
テストがRedになる
paginationは自身の投稿に紐づいていることになっているので今回はエラーが出ない
% rails test
Running via Spring preloader in process 8827
Started with run options
FAIL["test_feed_should_have_the_right_posts", UserTest, 1.2993419999838807]
test_feed_should_have_the_right_posts#UserTest (1.30s)
Expected false to be truthy.
test/models/user_test.rb:106:in `block (2 levels) in <class:UserTest>'
test/models/user_test.rb:105:in `block in <class:UserTest>'
74/74: [===========================================================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.69392s
74 tests, 347 assertions, 1 failures, 0 errors, 0 skips
演習問題3
リスト 14.44において、フォローしていないユーザーの投稿を含めるためにはどうすれば良いでしょうか? また、そのような変更を加えると、リスト 14.42のどのテストが失敗するでしょうか? ヒント: 自分自身とフォローしているユーザー、そしてそれ以外という集合は、いったいどういった集合を表すのか考えてみてください。
解答
全ての投稿を表示すればいいので
def feed
Micropost.all
end
フォローしていないユーザーの投稿は表示しない。という部分でエラーが出る
% rails test
Running via Spring preloader in process 8864
Started with run options
FAIL["test_feed_should_have_the_right_posts", UserTest, 1.5234350000391714]
test_feed_should_have_the_right_posts#UserTest (1.52s)
Expected true to be nil or false
test/models/user_test.rb:114:in `block (2 levels) in <class:UserTest>'
test/models/user_test.rb:113:in `block in <class:UserTest>'
74/74: [===========================================================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.82366s
74 tests, 383 assertions, 1 failures, 0 errors, 0 skips
14.3.3 サブセレクト
演習問題1
Homeページで表示される1ページ目のフィードに対して、統合テストを書いてみましょう。リスト 14.49はそのテンプレートです。
解答
test "feed on Home page" do
get root_path
@user.feed.paginate(page: 1).each do |micropost|
assert_match CGI.escapeHTML(micropost.content), response.body
end
end
演習問題2
リスト 14.49のコードでは、期待されるHTMLをCGI.escapeHTMLメソッドでエスケープしています (このメソッドは11.2.3で扱ったCGI.escapeと同じ用途です)。このコードでは、なぜHTMLをエスケープさせる必要があったのでしょうか? 考えてみてください。ヒント: 試しにエスケープ処理を外して、得られるHTMLの内容を注意深く調べてください。マイクロポストの内容が何かおかしいはずです。また、ターミナルの検索機能 (Cmd-FもしくはCtrl-F) を使って「sorry」を探すと原因の究明に役立つはずです。
解答
おわり