Rails Tutorial 拡張機能の返信機能を作ってみた(その3)：仕様の勘違いを修正

Rails Tutorialの第14章にある、返信機能を作る件の続きです。
###postされたらmodelのin_reply_toにIDを入れる機能

@replyが書いてあるmicropostがpostされたら、modelのin_reply_toにIDを入れる機能を
作ります。

変更するのはpostを受け取って、modelをsaveするところです。どこなのか探します。

文字列の操作をするメソッドをネットで検索します。
https://www.sejuku.net/blog/11843
start_with?（指定の文字列で始まっているか調べる）
http://rubytips86.hatenablog.com/entry/2014/03/28/132953
を読みます。

###仕様を勘違いしていることが判明
機能を深堀りしている途中で、contentが@replyで始まるのだと思っていたのが実は勘違いであることが分かりました。
「@reply exampleuser　hogehoge...」 
だと思っていたのですが、正しくは
「@exampleuser hogehoge...」
でした。

そうだとして、nameにブランクが含まれていると問題があると分かります。
例えば、nameが「Ola Lakin I 」は「@Ola Lakin I 」とpostしてもnameは「@Ola」しか探せないことに思い当たりました

対応方法としては、ユニークなidを追加する方法が考えられます。実はテキストで同じことを言っているのですが、この時点でやっとピンときました。

@以下がユニークになる方法はテキストでも具体的な方法が書いてあることから、後で考えることにします。
当面は@nameがブランクを含まずユニークになっている前提で進めることにします。この前提で機能を作った後、ユニークにする変更を加えることにします。

###文字列から@で始まる単語を取り出す
ネットで調べた方法をコンソールで試します。

```
>> s = "@Example hoge hoge aa"
=> "@Example hoge hoge aa"
>> b = s.scan(/@\w+/)
=> ["@Example"]
```

文字列に@xxが含まれないときどうなるかを調べます。

```
>> s2 = "Example hoge hoge aa"
=> "Example hoge hoge aa"
>> b2 = s2.scan(/@\w+/)                                                                                                                            
=> []
>> b2[0]
=> nil
```

nilになりました。

次に先頭の@を削除する方法を試します。

```
>> b = s.scan(/@\w+/)
=> ["@Example"]
>> b[0].slice!(0)
=> "@"
>> b[0]
=> "Example"
```

これで@xxxの単語を取り出せました。

###controllerの変更

画面は変更する必要がないと考え、controllerのどこを変更するかテキストを調べます。
controllers/microposts_controller.rbでcreateをしています。



```ruby:controllers/microposts_controller.rb
  def create
    @micropost = current_user.microposts.build(micropost_params)
      # replyかチェックする
      if reply_id =  @micropost.content.scan(/@\w+/)
        @micropost.in_reply_to = reply_id[0].slice![0]
      end
    if @micropost.save
      flash[:succsess] = "Micropost created!"
      redirect_to root_url
    else
      @feed_items = []
      render 'static_pages/home'
    end
  end
```

###変更点のテストをどこで書くか
controllerを変更するので、先にtestを書きます。
テスト項目は、replyのmicropostをcreateすると、受信者の件数が1増えることです。
テストをどこで書くかで、integration testとのすみわけを考えます。

controllerのテストの例について、tutorialを読み返します。
第13章、第14章のmicropostでは、modelのテストの次はintegrationテストをしていました。
13.3.1でcontrollerのテストの内容は、ログインしているかどうかのテストでした。

実際にtestのソースを見てみます。controllersフォルダの下です。いずれもログインしているかどうかや正しいページを表示しているかのテストでした。処理した結果を検証するのはintegrationテストで良いと分かりました。

integrationテストを書いてみます。

```ruby:test/integration/reply_test.rb　　RED
  test "reply to user " do
    log_in_as(@user)
    content = "@#{@other.name} reply test content"
    post microposts_path, params: { micropost: {content: content }}
    log_in_as(@other)
    get root_path
    assert_not @other.following?(@user)
    assert_match content, response.body
  end
```
###fixtureのデータでエラー
テストを実行したところ、fixtureのデータでエラーが起きました。

```
ERROR["test_reply_to_user_", ReplyTest, 0.3831760289999693]
 test_reply_to_user_#ReplyTest (0.38s)
NoMethodError:         NoMethodError: undefined method `id' for nil:NilClass
            test/fixtures/microposts.yml:54:in `get_binding'
```

not foundのようです。
ネットで調べると、fixtureを呼び出す順番に依るためで、外部制約があると
面倒なことになるようです。

fixtureでテストデータを作るのではなく、modelのtest内でpostするようにしてみます。

ついでに前にfixtureを作ったときの@replyの仕様の勘違いを修正することにします。

```ruby:test/models/user_test.rb
  test "feed should have the reply posts" do
    michael = users(:michael)
    malory = users(:malory)
    assert_not malory.following?(michael)
    reply_post = michael.microposts.create!(content: "@malory reply test", 
                                            in_reply_to: malory.id)
    assert michael.feed.include?(reply_post)
    assert malory.feed.include?(reply_post)
  end
```

これでテストは通るようになりました。

###controllerを変更
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)
        @micropost.in_reply_to = User.find_by(name: reply_id)
      end
    if @micropost.save
      flash[:succsess] = "Micropost created!"
      redirect_to root_url
    else
      @feed_items = []
      render 'static_pages/home'
    end
  end
```

###テストデータを修正
テストデータを直します。姓と名の間にスペースがないuserを作ります。

```ruby:test/fixtures/users.yml
Bob:
  name: Bob
  email: bob@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>
```

###間違いの修正
テストがRedのままなので、putsでデバッグしたところ、間違いが分かり修正しました。idが必要でした。

修正前        @micropost.in_reply_to = User.find_by(name: reply_id)
修正後        @micropost.in_reply_to = User.find_by(name: reply_id).id

```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(name: reply_id).id
      end
    if @micropost.save
      flash[:succsess] = "Micropost created!"
      redirect_to root_url
    else
      @feed_items = []
      render 'static_pages/home'
    end
  end
```

これでテストがGREENになりました。

画面でテストしてみます。
スペースがないユーザーを作り、動くことを確認できました。

##所要時間
10/11から10/17までの2.5時間です。