35歳だけどRailsチュートリアルやってみた。[第4版 13章 13.2 マイクロポストを表示する まとめ&解答例]

#はじめに
最近、プロジェクト管理業務が業務の大半を占めており、
プログラムを書く機会がなかなかありません。

このままだとプログラムがまったく書けない人になってしまう危機感（迫り来る35歳定年説）と、
新しいことに挑戦したいという思いから、
[Ruby on Rails チュートリアル実例を使ってRailsを学ぼう 第4版](https://railstutorial.jp/)を学習中です。
業務で使うのはもっぱらJavaなのですが、Rails楽しいですね。


これまでEvernoteに記録していましたが、ソースコードの貼付けに限界を感じたため、
Qiitaで自分が学習した結果をアウトプットしていきます。

個人の解答例なので、誤りがあればご指摘ください。

##動作環境
- cloud9
- ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
- Rails 5.0.0.1

##13.2.1 マイクロポストの描画
###本章での学び
####【事前準備】サンプルデータのリセット

```irb
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コントローラの生成

```irb
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パーシャルの作成

```irb
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のヘルパーメソッド](http://edgeapi.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html#method-i-time_ago_in_words)
「3分前に投稿」というような文字列を返すらしい。

```/sample_app/app/views/microposts/_micropost.html.erb
<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変数を定義する。

```/sample_app/app/controllers/users_controller.rb
  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】ユーザープロフィール画面にマイクロポストを表示する

```/sample_app/app/views/users/show.html.erb
<% 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>
```

###演習１
*7.3.3で軽く説明したように、今回ヘルパーメソッドとして使ったtime_ago_in_wordsメソッドは、Railsコンソールのhelperオブジェクトから呼び出すことができます。このhelperオブジェクトのtime_ago_in_wordsメソッドを使って、3.weeks.agoや6.months.agoを実行してみましょう。*

```irb
>> helper.time_ago_in_words(3.weeks.ago)
=> "21 days"
>> 
?> helper.time_ago_in_words(6.month.ago)
=> "6 months"
```

###演習２
*helper.time_ago_in_words(1.year.ago)と実行すると、どういった結果が返ってくるでしょうか?*

```irb
?> helper.time_ago_in_words(1.year.ago)
=> "about 1 year"
```

###演習３
*micropostsオブジェクトのクラスは何でしょうか? ヒント: リスト 13.23内のコードににあるように、まずはpaginateメソッド (引数はpage: nil) でオブジェクトを取得し、その後classメソッドを呼び出してみましょう。*

```irb
?> 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](https://ja.wikipedia.org/wiki/Lorem_ipsum)

```
```

####【事前準備】DBのリセット

```irb
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にサンプルデータを登録する

```irb
yokoyan:~/workspace/sample_app (user-microposts) $ rails db:seed
yokoyan:~/workspace/sample_app (user-microposts) $ 
```

####【動作確認】マイクロポストの表示確認
登録が終わったら、Railsサーバを再起動して、ブラウザで確認。

![image](https://qiita-image-store.s3.amazonaws.com/0/98130/70ab1547-e0e6-e1cd-190a-ce64941bc98d.png)

####【scss】マイクロポストのCSSを追加

```/sample_app/app/assets/stylesheets/custom.scss
/* 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件のマイクロポストが表示され、ページネーションも動くことを確認。

![image](https://qiita-image-store.s3.amazonaws.com/0/98130/66720c47-45db-e3da-8910-ff5e4471ab03.png)


###演習１
*(1..10).to_a.take(6)というコードの実行結果を推測できますか? 推測した値が合っているかどうか、実際にコンソールを使って確認してみましょう。*

1から10までのうち先頭６件を表示。

```irb
?> (1..10).to_a.take(6)
=> [1, 2, 3, 4, 5, 6]
```

###演習２
*先ほどの演習にあったto_aメソッドの部分は本当に必要でしょうか? 確かめてみてください。*

演習１と同じ結果となる。

```irb
?> (1..10).take(6)
=> [1, 2, 3, 4, 5, 6]
>> 
```

###演習３
*Fakerはlorem ipsum以外にも、非常に多種多様の事例に対応しています。Fakerのドキュメント (英語) を眺めながら画面に出力する方法を学び、実際に大学名や電話番号、Hipster IpsumやChuck Norris facts (参考: チャック・ノリスの真実) を画面に出力してみましょう。(訳注: もちろん日本語にも対応していて、例えば沖縄らしい用語を出力するfaker-okinawaもあります。ぜひ遊んでみてください。)*

Fakerで大学名を表示する。

```irb
?> Faker::University.name
=> "Volkman Institute"
>> 
```

Fakerで電話番号を表示する。

```irb
?> Faker::PhoneNumber.phone_number
=> "1-782-129-1939"
>> 
```

Hipster（ジャズ？）のテキストを表示する。

```irb
?> Faker::Hipster.sentence
=> "Street forage tousled asymmetrical."
```

チャック・ノリスの真実（ジョーク）を表示する。
量子暗号はチャック・ノリスでは機能しませんwww

```irb
?> 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をコメントアウトする。

```/sample_app/Gemfile
# gem 'faker',  '1.6.6'
gem 'faker-okinawa'
```

バンドルする。

```irb
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をインストール。

```irb
yokoyan:~/workspace/sample_app (user-microposts) $ gem install faker-okinawa
Successfully installed faker-okinawa-0.1.0
1 gem installed
```

#####【動作確認】faker-okinawaを試してみる。
泡盛の名前を取得。

```irb
?> Faker::Okinawa::Awamori.name
=> "かねやま"
```

####設定を戻す
動作確認が完了したら、fakerを元に戻す。

Gemfileを戻す。

```/sample_app/Gemfile
gem 'faker',  '1.6.6'
#gem 'faker-okinawa'
```

bundleする。

```irb
yokoyan:~/workspace/sample_app (user-microposts) $ bundle
```

fakerの再インストール。

```irb
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 プロフィール画面のマイクロポストをテストする
###本章での学び
####【事前準備】統合テストの作成
プロフィール画面用の統合テストを生成する。

```irb
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が出た時の対処法](http://qiita.com/yokoyan/items/2311a40aea1fbdd7d300)

```/sample_app/test/fixtures/microposts.yml
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を使って１ページ目を取得する。取得した結果を繰り返す
    - マイクロポストのcontent属性が、レスポンスのHTML内に存在するかチェック

```/sample_app/test/integration/users_profile_test.rb
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になることを確認。

```irb
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
```

###演習１
*リスト 13.28にある２つの’h1’のテストが正しいか確かめるため、該当するアプリケーション側のコードをコメントアウトしてみましょう。テストが green から redに変わることを確認してみてください。*

viewの該当箇所のタグをコメントアウトする。

```/sample_app/app/views/users/show.html.erb
      <!-- <h1> -->
        <%= gravatar_for @user %>
        <%= @user.name %>
      </h1>
```

テストがredになることを確認。
（確認後はコメントアウトを戻すこと）

```irb
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
```

###演習２
*リスト 13.28にあるテストを変更して、will_paginateが１度のみ表示されていることをテストしてみましょう。ヒント: 表 5.2を参考にしてください。*

will_paginateに該当する、`div class=pagination`が1度のみ表示されていることをテストする。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/98130/a3703241-d149-0ad5-2aa6-460aae26ace9.png)

```/sample_app/test/integration/users_profile_test.rb
  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になることを確認。

```irb
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対応) が出そうなので、8月中に14章まで完了できるようにがんばります！