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をしています。
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テストを書いてみます。
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の仕様の勘違いを修正することにします。
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を変更します。
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を作ります。
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
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時間です。