はじめに
Ruby on Rails チュートリアル実例を使ってRailsを学ぼう 第4版の 13章 13.2 マイクロポストを表示するの演習まとめ&回答例です。
個人の解答例なので、誤りがあればご指摘ください。
動作環境
- cloud9
- ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
- Rails 5.0.0.1
13.2.1 マイクロポストの描画
本章での学び
【事前準備】サンプルデータのリセット
yokoyan:~/workspace/sample_app (user-microposts) $ rails db:migrate:reset
Dropped database 'db/development.sqlite3'
Dropped database 'db/test.sqlite3'
Created database 'db/development.sqlite3'
Created database 'db/test.sqlite3'
== 20170417215343 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0019s
== 20170417215343 CreateUsers: migrated (0.0021s) =============================
== 20170423125906 AddIndexToUsersEmail: migrating =============================
-- add_index(:users, :email, {:unique=>true})
-> 0.0011s
== 20170423125906 AddIndexToUsersEmail: migrated (0.0017s) ====================
== 20170424041610 AddPasswordDigestToUsers: migrating =========================
-- add_column(:users, :password_digest, :string)
-> 0.0008s
== 20170424041610 AddPasswordDigestToUsers: migrated (0.0008s) ================
== 20170514215307 AddRememberDigestToUsers: migrating =========================
-- add_column(:users, :remember_digest, :string)
-> 0.0006s
== 20170514215307 AddRememberDigestToUsers: migrated (0.0007s) ================
== 20170614215124 AddAdminToUsers: migrating ==================================
-- add_column(:users, :admin, :boolean, {:default=>false})
-> 0.0016s
== 20170614215124 AddAdminToUsers: migrated (0.0017s) =========================
== 20170618221238 AddActivationToUsers: migrating =============================
-- add_column(:users, :activation_digest, :string)
-> 0.0007s
-- add_column(:users, :activated, :boolean, {:dafault=>false})
-> 0.0004s
-- add_column(:users, :activated_at, :datetime)
-> 0.0005s
== 20170618221238 AddActivationToUsers: migrated (0.0023s) ====================
== 20170703042006 AddResetToUsers: migrating ==================================
-- add_column(:users, :reset_digest, :string)
-> 0.0007s
-- add_column(:users, :reset_sent_at, :datetime)
-> 0.0003s
== 20170703042006 AddResetToUsers: migrated (0.0016s) =========================
== 20170719214241 CreateMicroposts: migrating =================================
-- create_table(:microposts)
-> 0.0016s
-- add_index(:microposts, [:user_id, :created_at])
-> 0.0009s
== 20170719214241 CreateMicroposts: migrated (0.0032s) ========================
yokoyan:~/workspace/sample_app (user-microposts) $ rails db:seed
【事前準備】Micropostsコントローラの生成
yokoyan:~/workspace/sample_app (user-microposts) $ rails generate controller Microposts
Running via Spring preloader in process 2577
Expected string default value for '--jbuilder'; got true (boolean)
Expected string default value for '--helper'; got true (boolean)
Expected string default value for '--assets'; got true (boolean)
create app/controllers/microposts_controller.rb
invoke erb
create app/views/microposts
invoke test_unit
create test/controllers/microposts_controller_test.rb
invoke helper
create app/helpers/microposts_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/microposts.coffee
invoke scss
create app/assets/stylesheets/microposts.scss
【事前準備】micropostパーシャルの作成
yokoyan:~/workspace/sample_app (user-microposts) $ pwd /home/ubuntu/workspace/sample_app yokoyan:~/workspace/sample_app (user-microposts) $ touch app/views/microposts/_micropost.html.erb
【View】micropostパーシャルの実装
マイクロポストの内容を一覧表示するパーシャルを作成する。 将来的な拡張を見越して、マイクロポスト毎にidを割り振っている。 はじめて出てきた、time_ago_in_wordsは、ActionViewのヘルパーメソッド 「3分前に投稿」というような文字列を返すらしい。
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</li>
【controller】ページング処理の実装のための事前準備
Usersコントローラから、ユーザのページングを行うのであれば、 ユーザープロフィール画面(View)のwill_paginateの引数に@usersを明示的に渡す必要はない。
今回は、Usersコントローラから、マイクロポストのページング処理を行うため、 同画面(View)のwill_paginateの引数に@microposts変数をwill_paginateに明示的に渡す必要がある。 Usersコントローラにて、@microposts変数を定義する。
def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page])
redirect_to root_url and return unless @user.activated?
#debugger
end
【View】ユーザープロフィール画面にマイクロポストを表示する
<% provide(:title, @user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for @user %>
<%= @user.name %>
</h1>
</section>
</aside>
<div class="col-md-8">
<% if @user.microposts.any? %>
<h3>Microposts (<%= @user.microposts.count %>)</h3>
<ol class="microposts">
<%= render @microposts %>
</ol>
<%= will_paginate @microposts %>
<% end %>
</div>
</div>
演習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" >> ?> helper.time_ago_in_words(6.month.ago) => "6 months"
演習2
helper.time_ago_in_words(1.year.ago)と実行すると、どういった結果が返ってくるでしょうか?
?> helper.time_ago_in_words(1.year.ago) => "about 1 year"
演習3
micropostsオブジェクトのクラスは何でしょうか? ヒント: リスト 13.23内のコードににあるように、まずはpaginateメソッド (引数はpage: nil) でオブジェクトを取得し、その後classメソッドを呼び出してみましょう。
?> user = User.first User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2017-07-25 21:45:26", updated_at: "2017-07-25 21:45:26", password_digest: "$2a$10$G03Oh9yI0VMHnGF38MA3iOkoHiLtJ2qnuQ8whUZ8lEp...", remember_digest: nil, admin: true, activation_digest: "$2a$10$M9ShPLhv8mtQE6Yb4.BDMeTjvoWnGI1xfeEAvka1I7Z...", activated: true, activated_at: "2017-07-25 21:45:26", reset_digest: nil, reset_sent_at: nil> >> ?> microposts = user.microposts.paginate(page: nil) Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? OFFSET ? [["user_id", 1], ["LIMIT", 30], ["OFFSET", 0]] => #<ActiveRecord::AssociationRelation []> >> ?> microposts.class => Micropost::ActiveRecord_AssociationRelation
13.2.2 マイクロポストのサンプル
micropostsオブジェクトのクラスは何でしょうか? ヒント: リスト 13.23内のコードににあるように、まずはpaginateメソッド (引数はpage: nil) でオブジェクトを取得し、その後classメソッドを呼び出してみましょう。
本章での学び
【事前準備】サンプルデータにマイクロポストを追加する
サンプルユーザのうち、IDが小さい順に6人呼び出して、50件のマイクロポストを登録する。
ちなみに、lorem ipsumとは、意味のない典型的なダミーテキストのこと。 wiki:lorem ipsum
【事前準備】DBのリセット
yokoyan:~/workspace/sample_app (user-microposts) $ rails db:migrate:reset
Dropped database 'db/development.sqlite3'
Dropped database 'db/test.sqlite3'
Created database 'db/development.sqlite3'
Created database 'db/test.sqlite3'
== 20170417215343 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0014s
== 20170417215343 CreateUsers: migrated (0.0015s) =============================
== 20170423125906 AddIndexToUsersEmail: migrating =============================
-- add_index(:users, :email, {:unique=>true})
-> 0.0010s
== 20170423125906 AddIndexToUsersEmail: migrated (0.0011s) ====================
== 20170424041610 AddPasswordDigestToUsers: migrating =========================
-- add_column(:users, :password_digest, :string)
-> 0.0008s
== 20170424041610 AddPasswordDigestToUsers: migrated (0.0013s) ================
== 20170514215307 AddRememberDigestToUsers: migrating =========================
-- add_column(:users, :remember_digest, :string)
-> 0.0007s
== 20170514215307 AddRememberDigestToUsers: migrated (0.0008s) ================
== 20170614215124 AddAdminToUsers: migrating ==================================
-- add_column(:users, :admin, :boolean, {:default=>false})
-> 0.0013s
== 20170614215124 AddAdminToUsers: migrated (0.0014s) =========================
== 20170618221238 AddActivationToUsers: migrating =============================
-- add_column(:users, :activation_digest, :string)
-> 0.0011s
-- add_column(:users, :activated, :boolean, {:dafault=>false})
-> 0.0006s
-- add_column(:users, :activated_at, :datetime)
-> 0.0005s
== 20170618221238 AddActivationToUsers: migrated (0.0024s) ====================
== 20170703042006 AddResetToUsers: migrating ==================================
-- add_column(:users, :reset_digest, :string)
-> 0.0010s
-- add_column(:users, :reset_sent_at, :datetime)
-> 0.0006s
== 20170703042006 AddResetToUsers: migrated (0.0023s) =========================
== 20170719214241 CreateMicroposts: migrating =================================
-- create_table(:microposts)
-> 0.0019s
-- add_index(:microposts, [:user_id, :created_at])
-> 0.0010s
== 20170719214241 CreateMicroposts: migrated (0.0035s) ========================
【事前準備】DBにサンプルデータを登録する
yokoyan:~/workspace/sample_app (user-microposts) $ rails db:seed yokoyan:~/workspace/sample_app (user-microposts) $
【動作確認】マイクロポストの表示確認
登録が終わったら、Railsサーバを再起動して、ブラウザで確認。

【scss】マイクロポストのCSSを追加
/* microposts */
.microposts {
list-style: none;
padding: 0;
li {
padding: 10px 0;
border-top: 1px solid #e8e8e8;
}
.user {
margin-top: 5em;
padding-top: 0;
}
.content {
display: block;
margin-left: 60px;
img {
display: block;
padding: 5px 0;
}
}
.timestamp {
color: $gray-light;
display: block;
margin-left: 60px;
}
.gravatar {
float: left;
margin-right: 10px;
margin-top: 5px;
}
}
aside {
textarea {
height: 100px;
margin-bottom: 5px;
}
}
span.picture {
margin-top: 10px;
input {
border: 0;
}
}
【動作確認】マイクロポストの表示確認2
ブラウザで確認。50件のマイクロポストが表示され、ページネーションも動くことを確認。

演習1
(1..10).to_a.take(6)というコードの実行結果を推測できますか? 推測した値が合っているかどうか、実際にコンソールを使って確認してみましょう。
1から10までのうち先頭6件を表示。
?> (1..10).to_a.take(6) => [1, 2, 3, 4, 5, 6]
演習2
先ほどの演習にあったto_aメソッドの部分は本当に必要でしょうか? 確かめてみてください。
演習1と同じ結果となる。
?> (1..10).take(6) => [1, 2, 3, 4, 5, 6] >>
演習3
Fakerはlorem ipsum以外にも、非常に多種多様の事例に対応しています。Fakerのドキュメント (英語) を眺めながら画面に出力する方法を学び、実際に大学名や電話番号、Hipster IpsumやChuck Norris facts (参考: チャック・ノリスの真実) を画面に出力してみましょう。(訳注: もちろん日本語にも対応していて、例えば沖縄らしい用語を出力するfaker-okinawaもあります。ぜひ遊んでみてください。)
Fakerで大学名を表示する。
?> Faker::University.name => "Volkman Institute" >>
Fakerで電話番号を表示する。
?> Faker::PhoneNumber.phone_number => "1-782-129-1939" >>
Hipster(ジャズ?)のテキストを表示する。
?> Faker::Hipster.sentence => "Street forage tousled asymmetrical."
チャック・ノリスの真実(ジョーク)を表示する。 量子暗号はチャック・ノリスでは機能しませんwww
?> Faker::ChuckNorris.fact => "Quantum cryptography does not work on Chuck Norris. When something is being observed by Chuck it stays in the same state until he's finished. " >>
faker-okinawaを試す。
【事前準備】faker-okinawaのインストール
gemfileを修正する。 ※fakerと同時に入れるとエラーになったため、一旦fakerをコメントアウトする。
# gem 'faker', '1.6.6' gem 'faker-okinawa'
バンドルする。
yokoyan:~/workspace/sample_app (user-microposts) $ bundle The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/......... Fetching version metadata from https://rubygems.org/.. Fetching dependency metadata from https://rubygems.org/. Resolving dependencies... ・・・略・・・ Installing faker-okinawa 0.1.0 Bundle complete! 26 Gemfile dependencies, 84 gems now installed. Gems in the group production were not installed. Use `bundle show [gemname]` to see where a bundled gem is installed.
gemをインストール。
yokoyan:~/workspace/sample_app (user-microposts) $ gem install faker-okinawa Successfully installed faker-okinawa-0.1.0 1 gem installed
【動作確認】faker-okinawaを試してみる。
泡盛の名前を取得。
?> Faker::Okinawa::Awamori.name => "かねやま"
設定を戻す
動作確認が完了したら、fakerを元に戻す。
Gemfileを戻す。
gem 'faker', '1.6.6' #gem 'faker-okinawa'
bundleする。
yokoyan:~/workspace/sample_app (user-microposts) $ bundle
fakerの再インストール。
yokoyan:~/workspace/sample_app (user-microposts) $ gem install faker Fetching: faker-1.8.4.gem (100%) Successfully installed faker-1.8.4 1 gem installed
13.2.3 プロフィール画面のマイクロポストをテストする
本章での学び
【事前準備】統合テストの作成
プロフィール画面用の統合テストを生成する。
yokoyan:~/workspace/sample_app (user-microposts) $ rails generate integration_test users_profile
Running via Spring preloader in process 1595
Expected string default value for '--jbuilder'; got true (boolean)
invoke test_unit
create test/integration/users_profile_test.rb
【事前準備】テストデータの生成
マイクロポスト用のfixtureファイルを修正する。
各マイクロポストデータに、user: michaelを追記すると、michaelユーザの投稿として関連付けされる。また、マイクロポストのページャーのテストを行うために、30回マイクロポストを自動生成する。
なお、ymlの各属性の後ろに:をつけないと、エラーが発生するので注意。 詳細は以下参照。
Railsチュートリアル中にcould not find expected ‘:’ while scanning a simple keyが出た時の対処法
orange: content: "I just ate an orange!" created_at: <%= 10.minutes.ago %> user: michael tau_manifesto: content: "Check out the @tauday site by @mhartl: http://tauday.com" created_at: <%= 3.years.ago %> user: michael cat_video: content: "Sad cats are sad: http://youtu.be/PKffm2uI4dk" created_at: <%= 2.hours.ago %> user: michael most_recent: content: "Writing a short test" created_at: <%= Time.zone.now %> user: michael <% 30.times do |n| %> micropost_<%= n %>: content: <%= Faker::Lorem.sentence(5) %> created_at: <%= 42.days.ago %> user: michael <% end %>
【test】統合テストの実装
以下の仕様で実装する。
- GETリクエスト送信(ユーザープロフィールページ)
- ユーザープロフィールページ(users/show)のテンプレートが表示されること
- HTMLのtitle属性にユーザーの名前が表示されること
- HTMLのh1タグにユーザーの名前が表示されること
- HTMLのh1タグの下に、imgタグ(gravatorクラス付き)が表示されること
- ユーザーのマイクロポスト数をカウントして文字列に変換する。レスポンスのHTML内に存在するかチェック
- HTMLにdivタグ(paginationクラス付き)が表示されること
- ユーザーのマイクロポストから、paginateを使って1ページ目を取得する。取得した結果を繰り返す
- マイクロポストのcontent属性が、レスポンスのHTML内に存在するかチェック
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.gravator'
assert_match @user.microposts.count.to_s, response.body
assert_select 'div.pagination'
@user.microposts.paginate(page: 1) do |micropost|
assert_match micropost.content, response.body
end
end
end
【test】テスト実行
テスト結果がgreenになることを確認。
yokoyan:~/workspace/sample_app (user-microposts) $ rails test Running via Spring preloader in process 1230 Started with run options --seed 10439 54/54: [================================================================================================================================================================================] 100% Time: 00:00:04, Time: 00:00:04 Finished in 4.15487s 54 tests, 238 assertions, 0 failures, 0 errors, 0 skips
演習1
リスト 13.28にある2つの’h1’のテストが正しいか確かめるため、該当するアプリケーション側のコードをコメントアウトしてみましょう。テストが green から redに変わることを確認してみてください。
viewの該当箇所のタグをコメントアウトする。
<!-- <h1> -->
<%= gravatar_for @user %>
<%= @user.name %>
</h1>
テストがredになることを確認。 (確認後はコメントアウトを戻すこと)
yokoyan:~/workspace/sample_app (user-microposts) $ rails test
Running via Spring preloader in process 1915
Started with run options --seed 50720
FAIL["test_profile_display", UsersProfileTest, 1.6931300410069525]
test_profile_display#UsersProfileTest (1.69s)
Expected at least 1 element matching "h1", found 0..
Expected 0 to be >= 1.
test/integration/users_profile_test.rb:14:in `block in <class:UsersProfileTest>'
54/54: [================================================================================================================================================================================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.94467s
54 tests, 234 assertions, 1 failures, 0 errors, 0 skips
演習2
リスト 13.28にあるテストを変更して、will_paginateが1度のみ表示されていることをテストしてみましょう。ヒント: 表 5.2を参考にしてください。
will_paginateに該当する、div class=paginationが1度のみ表示されていることをテストする。

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.gravator'
assert_match @user.microposts.count.to_s, response.body
assert_select 'div.pagination'
@user.microposts.paginate(page: 1) do |micropost|
assert_match micropost.content, response.body
end
assert_select 'div.pagination', count:1 #追加
end
テストがgreenになることを確認。
yokoyan:~/workspace/sample_app (user-microposts) $ rails test Running via Spring preloader in process 2471 Started with run options --seed 42382 54/54: [================================================================================================================================================================================] 100% Time: 00:00:03, Time: 00:00:03 Finished in 3.10291s 54 tests, 239 assertions, 0 failures, 0 errors, 0 skips
おわりに
マイクロポストのテストデータを作成し、テストコードの実装が完了しました。 最新版 (Rails 5.1対応) が出てしまったので、9月中に14章まで完了できるようにがんばります!