Rails Tutorial 拡張機能の返信機能を作ってみた(その5)：

Rails Tutorialの第14章にある、返信機能を作る件の続きです。
前回まででユーザーをユニークにするmodelの作成とテストまでできました。次にユーザーの更新・変更に機能を追加します。
###userのviewの変更箇所の確認
unique_nameをどこにどうやって追加するか考えます。
tutorialの第7章を読み返します。
7.1　ユーザーを表示する　　表示する項目にunique_nameを追加
7.2　ユーザー登録フォーム　入力する項目にunique_nameを追加　
7.3  ユーザー登録失敗　エラーメッセージに追加、テストに追加
7.4 ユーザー登録成功　成功時のテストに追加

第10章ユーザーの更新・表示・削除　を読み返します。
10.1 ユーザーを更新する　入力する項目にunique_nameを追加　
10.3 すべてのユーザーを表示する　　表示する項目にunique_nameを追加

これで網羅していると考えます。
###userのviewの変更
7.1　ユーザーのviewを変更します。
画面上では、これがunique_nameだと分かるように先頭に@を表示することにします。

```ruby:views/users/show.html.erb
      <h1>
        <%= gravatar_for @user %>
        <%= @user.name%>
        @<%= @user.unique_name%>
      </h1>
```

7.2　ユーザー登録フォームに追加します。
どのファイルか探し、new.html.erbを見ると、render 'form'とありました。formに追加すべきと分かりました。

```ruby:views/users/_form.html.erb
    <%= f.label :name %>
    <%= f.text_field :name, class: 'form-control' %>
    
    <%= f.label :email %>
    <%= f.email_field :email, class: 'form-control'  %>

    <%= f.label :unique_name %>
    <%= f.text_field :unique_name, class: 'form-control'  %>
    
    <%= f.label :password %>
    <%= f.password_field :password, class: 'form-control' %>
```

続いて、contollerを見ます。

7.3.2 Strong Parametersを見て、追加するところが分かりました。

```ruby:controllers/users_controller.rb
  private
    def user_params
        params.require(:user).permit(:name, :email, :password, 
                                     :password_confirmation, :unique_name)
    end
```  

7.3.3 エラーメッセージ を読み返します。
図7.18のエラーメッセージについて、unique_nameでもエラーを出力すべきと分かります。
Unique name can't be blank
Unique name has already been taken

###ユーザー登録のテスト

リスト 7.23: 無効なユーザー登録に対するテストと同様に、テストを追加します。

```
ubuntu:~/environment/sample_app (reply-micropost) $ rails test test/integration/users_signup_test.rb 
 FAIL["test_valid_signup_information_with_account_activation", UsersSignupTest, 1.2921500019999712]
 test_valid_signup_information_with_account_activation#UsersSignupTest (1.29s)
        "User.count" didn't change by 1.
        Expected: 36
          Actual: 35
        test/integration/users_signup_test.rb:29:in `block in <class:UsersSignupTest>'
```

追加する前に、試しにテストを実行してみたところ、REDになりました。
正常に登録するテストtest_valid_signup_information_with_account_activationにunique_nameがなかったからユーザー追加がエラーになったのではと考えました。unique_nameを追加します。

```ruby:test/integration/users_signup_test.rb　GREEN
    test "valid signup information with account activation" do
      get signup_path
      assert_difference 'User.count', 1 do
        post users_path, params: { user: {name:  "Example User",
                                          email: "user@example.com",
                                          password:              "password",
                                          password_confirmaiton: "password",
                                          unique_name: "Example1"} }
      end
```

テストが成功したので、serverを立ち上げて画面からユーザーを登録してみることにします。

ます、テストで入れたユーザーを削除するために、データベースにデータを入れ直します。

```
ubuntu:~/environment/sample_app (reply-micropost) $ rails db:migrate:reset
ubuntu:~/environment/sample_app (reply-micropost) $ rails db:seed
```

また重複エラーになりました。
デバッグとして、unique_nameをputsで出力してみたところ、Jeramyが二回出力されました。Fakerはfirst_nameだけではユニークにならないと考え、first_nameの後ろに数字を加えることにします。

```ruby:db/seeds.rb
99.times do |n|
  name  = Faker::Name.name
  email = "example-#{n+1}@railstutorial.org"
  password = "password"
  unique_name = "#{Faker::Name.first_name}#{n+1}"
```

上手く行きました。consoleでデータをいくつか見てみます。

```
>> User.last
  User Load (0.2ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 100, name: "Dorian Greenfelder", email: "example-99@railstutorial.org", created_at: "2020-10-24 02:06:54", updated_at: "2020-10-24 02:06:54", password_digest: "$2a$10$syodSOoCtHmdP/vi20.LgOou95cxRnHino0IhRRcxQN...", remember_digest: nil, admin: false, activation_digest: "$2a$10$LAs1aWDvwZIS0UPWhTR47.9BIGQPQ4eY16qFJT148dY...", activated: true, activated_at: "2020-10-24 02:06:54", reset_digest: nil, reset_sent_at: nil, unique_name: "Alejandrin99">
```

serverを起動して、画面からユーザーを登録してみたところ、無事登録できました。
![input_form.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639093/bffa4973-922b-5435-6f6f-2d6e0f6b675d.png)
![show.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639093/2db680d5-4b97-5b79-2b6b-26c23d332108.png)

###ユーザ更新のテスト

tutorialの10.1 「ユーザーを更新する」を読みます。
試しに画面からユーザーを更新してみたところ、エラーはなく更新ができました。

テストは登録のときと同様にREDになりそうなので、試しにやってみます。

```ruby
ubuntu:~/environment/sample_app (reply-micropost) $ rails test test/integration/users_edit_test.rb 
 FAIL["test_successful_edit", UsersEditTest, 1.2000509099998453]
 test_successful_edit#UsersEditTest (1.20s)
        Expected true to be nil or false
        test/integration/users_edit_test.rb:33:in `block in <class:UsersEditTest>'
```

原因を考え、fixtureのユーザーにunique_nameを追加していないことに気が付きました。

```yml:test/fixtures/users.yml
michael:
  name: Michael Example
  email: michael@example.com
  password_digest: <%= User.digest('password') %>
  admin: true
  activated: true
  activated_at: <%= Time.zone.now %>  
  unique_name: Michael1

archer:
  name: Sterling Archer
  email: duchess@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>  
  unique_name: Archer1
```

正常な変更のテストにunique_nameの変更も追加します。

```ruby:test/integration/users_edit_test.rb
  test "successful edit" do
    log_in_as(@user)
    get edit_user_path(@user)
    assert_template 'users/edit'
    name = "Foo Bar"
    email = "foo@bar.com"
    unique_name = "Foo1"
    patch user_path(@user), params: { user: { name: name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "",
                                              unique_name: unique_name} }
 ```

失敗のテストにもunique_nameの変更を追加します。

```ruby:test/integration/users_edit_test.rb　RED
  test "unsuccessful edit" do
    log_in_as(@user)
    #log_in(@user)
    get edit_user_path(@user)
    assert_template 'users/edit'
    patch user_path(@user), params: { user: {name: "",
                                            email: "foo@invalid",
                                            password:              "foo",
                                            password_confirmation: "bar",
                                            unique_name: "" } }
    assert_template 'users/edit'
    assert_select "div.alert", "The form contains 4 errors."
  end
```

```ruby
test_unsuccessful_edit#UsersEditTest (0.94s)
        <The form contains 4 errors.> expected but was
        <The form contains 5 errors.>..
        Expected 0 to be >= 1.
        test/integration/users_edit_test.rb:20:in `block in <class:UsersEditTest>'
```

unique_nameのエラーが1つ増えるはすなので、想定通りREDになりました。エラーの数を4から5に増やしたところ、GREENになりました。

```ruby:test/integration/users_edit_test.rb　GREEN
  test "unsuccessful edit" do

    assert_select "div.alert", "The form contains 5 errors."
  end
```
###ユーザ一覧のテスト
10.3 「すべてのユーザーを表示する」を読みます。
unique_nameをnameのとなりに表示するようにします。

```ruby:app/views/users/_user.html.erb
<li>
  <%= gravatar_for user, size: 50 %>
  <%= link_to user.name, user %>
  <%= "@" + user.unique_name %>
```

indexのテストを実行し、GREENでした。
test/integration/users_index_test.rb

###全てのテスト
全てのテストを実行してみます。

```ruby
FAIL["test_profile_display", UsersProfileTest, 0.9564320010000529]
 test_profile_display#UsersProfileTest (0.96s)
        <Michael Example> expected but was
        <Michael Example
                @Michael1>..
        Expected 0 to be >= 1.
```

profileのtestでエラーになっています。h1の文字列を「nameと完全一致」でなく
「nameを含む」に変更します。ネットで検索し、assertには正規表現が使えることが分かりました。
https://zariganitosh.hatenablog.jp/entry/20080405/1207455670

```ruby
assert_select "title", /Welcome/ # titleタグの文字列に、「Welcome」が含まれていれば成功。
```

記事を参考にして、変更します。

```ruby:test/integration/users_profile_test.rb　GREEN
 test "profile display" do
    get user_path(@user)
    assert_template 'users/show'
    assert_select 'title', full_title(@user.name)
    assert_select 'h1', /#{@user.name}/ 
    assert_select 'h1', /#{@user.unique_name}/ 
```

replyにnameを使っていたところを、unique_nameを使うように変更します。

```ruby:controllers/microposts_controller.rb
  def create
    @micropost = current_user.microposts.build(micropost_params)
      # replyかチェックする
      if reply_id =  @micropost.content.scan(/@\w+/)[0]
        reply_id.slice!(0)
        @micropost.in_reply_to = User.find_by(unique_name: reply_id).id
      end
```

テストします。

```ruby
test/controllers/microposts_controller_test.rbubuntu:~/environment/sample_app (reply-micropost) $ rails test
ERROR["test_reply_to_user_", ReplyTest, 2.186517049000031]
 test_reply_to_user_#ReplyTest (2.19s)
NoMethodError:         NoMethodError: undefined method `id' for nil:NilClass
            app/controllers/microposts_controller.rb:10:in `create'
            test/integration/reply_test.rb:14:in `block in <class:ReplyTest>'
```

testはreplyにnameを使うままでまだ変更していないことによるエラーになるのかと思ったのですが、違うエラーが出ました。
メッセージではUser.find_by(unique_name: reply_id)がnilだからと出ています。find_byがNot Foundだからではと考えます。putsでデバッグします。

```ruby:controllers/microposts_controller.rb
  def create
    @micropost = current_user.microposts.build(micropost_params)
      # replyかチェックする
      if reply_id =  @micropost.content.scan(/@\w+/)[0]
        reply_id.slice!(0)
        puts (User.find_by(unique_name: reply_id) == nil) 
```
putsの出力がtrueだったので、Not Foundの場合は何もしないようにcontrollerを変更します。

```ruby:controllers/microposts_controller.rb  
def create
    @micropost = current_user.microposts.build(micropost_params)
      # replyかチェックする
      if reply_id =  @micropost.content.scan(/@\w+/)[0]
        reply_id.slice!(0)
        if reply_user = User.find_by(unique_name: reply_id)  
          @micropost.in_reply_to = reply_user.id
        end
      end
```
    
テストをすると、先のエラーは解消し、別のエラーになりました。

```ruby
ubuntu:~/environment/sample_app (reply-micropost) $ rails test
 FAIL["test_reply_to_user_", ReplyTest, 1.5751866470000095]
 test_reply_to_user_#ReplyTest (1.58s)
        Expected /@Bob\ reply\ test\ content/ to match "<!DOCTYPE html>　...
 <h3>Micropost Feed</h3>\n            \n        </div>\n    </div>\n\n 
```

replyのpostがfeedに表示されていないというエラーです。

テストでpostしているreplyをnameからunique_nameに変更していないことに思い当たりました。変更したところGREENになりました。

```ruby:test/integration/reply_test.rb　GREEN
 test "reply to user " do
    log_in_as(@user)
    content = "@#{@other.unique_name} reply test content"
```
###画面からテスト
serverを立ち上げ、画面からテストしてみます。
![unique_error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639093/16536e74-2768-1450-fcf0-175f3e9a9cc0.png)
図．ユニークなユーザーの重複のテスト


![index.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639093/330c0c78-34d4-dd0f-ab78-f03f8e682c9c.png)
図．ユーザーの一覧表示　@でunique_userが追加されている

replyを入力してみます。
送信者:@Example 
受信者：@testb

![reply_test1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639093/ad3f87bf-0c1c-0369-2108-10ce63c7d469.png)
図．replyを入力

送信者のHome画面にreplyのpostが表示されています。
![reply_test2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639093/b1813785-8188-7344-070a-112927c53320.png)
図．replyの送信者に表示されている

replyの受信者でログインし直して、home画面を表示します。replyのpostが表示されています。
![reply_test3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639093/7c55e7cc-bad9-1e65-b605-0651ce4347a1.png)
図．replyの受信者のHome画面にreplyが表示

受信者のshow画面には、replyのpostは表示されません。
![reply_test4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/639093/f98d6d75-0d15-2349-84ac-14dcf99d0a3c.png)
図．replyの受信者のshow画面にreplyは非表示
###show画面の仕様確認
受信者のshow画面にはreplyは表示されません。なぜか考え、feedメソッドを使っていないのか確認します。

```ruby: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 == true
  end
```
feedメソッドは使っていませんでした。
この仕様が要件に合っているか考えます。showはログインしていない人も使えるという点です。

要件を決めたときを振り返ると、
>・第3者が受信者のフィード画面を表示すると、replyが表示されない

と要件を決めていました。なので、問題ないことが分かりました。

次に、followしている人に見えるべきか、要件を確認します。
要件を決めたときを振り返ると、

>replyの送信者のフォロワーは見える。
>replyの受信者のフォロワーは見えない。

でした。followのテストの中で確認しているように思えるので確認します。

```ruby:test/integration/following_test.rb
 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
```

feedを使っているので確認できていました。

実際に画面で確認します。
送信者のフォロワーとして@Exampleのフォロアーを探します。
seedsを見ると、users[3..40]がフォローしています。
そのうちの1名でログインします。
name:Gladys Kuhlman　
email:example-3@railstutorial.org
replyが表示されていました。


replyの受信者のフォロワーの画面を確認します。
@testbのフォロアーが誰もいませんでした。
送信者をフォローしていないのは、user[2]です。
name:Horacio Kautzer 
email:example-1@railstutorial.org
でログインします。@testbを画面からフォローします。
Home画面に戻り、replyは表示されていないので、動作が正しいことが確認できました。

seedsに古い仕様の@の書き方が残っていることに気がつき修正しました。
見た目の問題だけなので、テストに影響はないですが後で分からなくなると嫌なので直しました。

```ruby:/sample_app/db/seeds.rb
sender   = users.first
receiver = users.second
reply_content = "@#{receiver.unique_name} reply test"
sender.microposts.create!(content: reply_content,
                          in_reply_to: receiver.id)
```

これで返信機能は完成です。

##所要時間
10/23から10/26までの5.5時間です。
返信機能全体では、9/27-10/26　作業した日は29日間で計22時間です。