railsチュートリアル第十四章 ［Follow］ボタン Ajax編

###［Follow］ボタン Ajax編
####Ajaxを使ったフォローフォーム
app/views/users/_follow.html.erb

```html
<%= form_with(model: current_user.active_relationships.build, remote: true) do |f| %>  <div><%= hidden_field_tag :followed_id, @user.id %></div>
  <%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>
```

###Ajaxを使ったフォロー解除フォーム
app/views/users/_unfollow.html.erb

```html
<%= form_with(model: current_user.active_relationships.find_by(followed_id: @user.id),
             html: { method: :delete }, remote: true) do |f| %>
  <%= f.submit "Unfollow", class: "btn btn-default" %>
<% end %>
```

####RelationshipsコントローラでAjaxリクエストに対応する
app/controllers/relationships_controller.rb

```rb
class RelationshipsController < ApplicationController
  before_action :logged_in_user
  # ログインされていることが前提
  def create
    user = User.find(params[:followed_id])
    current_user.follow(user)
    respond_to do |format|
      format.html { redirect_to @user }
       # リクエストされるフォーマットがHTML形式の場合
       # @userにリダイレクト
      format.js
    end
  end

  def destroy
    user = Relationship.find(params[:id]).followed
    current_user.unfollow(user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end
end
```

####JavaScriptが無効になっていたときのための設定
config/application.rb

```rb
require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module SampleApp
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.
    
    # 認証トークンをremoteフォームに埋め込む
    config.action_view.embed_authenticity_token_in_remote_forms = true
  end
end
```

####JavaScriptと埋め込みRubyを使ってフォローの関係性を作成する
app/views/relationships/create.js.erb

```js
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>");
$("#followers").html('<%= @user.followers.count %>');
```

####Ruby JavaScript（RJS）を使ってフォローの関係性を削除する
app/views/relationships/destroy.js.erb

```js
$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>");
$("#followers").html('<%= @user.followers.count %>');
```

###演習
1.
ブラウザから /users/2 にアクセスし、うまく動いているかどうか確認してみましょう。

確認

2.
先ほどの演習で確認が終わったら、Railsサーバーのログを閲覧し、フォロー/フォロー解除を実行した直後のテンプレートがどうなっているか確認してみましょう。

わからなかった。

###フォローをテストする
####［Follow］/［Unfollow］ボタンをテストする 
test/integration/following_test.rb

```rb
require 'test_helper'

class FollowingTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
    # テストユーザー
    @other = users(:archer)
    log_in_as(@user)
    # ログインさせる
  end

  test "following page" do
    # followページに正確にフォッロワーが表示しているかどうかのテスト
    get following_user_path(@user)
    # ユーザーのフォロー数を表示させることを要求
    assert_not @user.following.empty?
    # ユーザーのフォローが０でないか確認？
    assert_match @user.following.count.to_s, response.body
    # フォロー数がユーザーページにあるかどうか確認？
    @user.following.each do |user|
    # ユーザーのフォローしているユーザー数を一つづつ取り出す
      assert_select "a[href=?]", user_path(user)
      # ひとつずつ取り出してaタグにユーザーが書かれているか？
    end
  end

  test "followers page" do
    get followers_user_path(@user)
    assert_not @user.followers.empty?
    assert_match @user.followers.count.to_s, response.body
    @user.followers.each do |user|
      assert_select "a[href=?]", user_path(user)
    end
  end
  
  test "should follow a user the standard way" do
    assert_difference '@user.following.count', 1 do
    # フォローがひとつだけ増えたか確認
      post relationships_path, params: { followed_id: @other.id }
    end
  end

  test "should follow a user with Ajax" do
  # Ajax用のテスト
    assert_difference '@user.following.count', 1 do
      post relationships_path, xhr: true, params: { followed_id: @other.id }
      # xhr: true Ajaxでリクエストを発行するように変わります。
    end
  end

  test "should unfollow a user the standard way" do
    @user.follow(@other)
    relationship = @user.active_relationships.find_by(followed_id: @other.id)
    # フォローさせる
    assert_difference '@user.following.count', -1 do
    # フォローが一つだけ減ったか確認
      delete relationship_path(relationship)
    end
  end

  test "should unfollow a user with Ajax" do
    @user.follow(@other)
    relationship = @user.active_relationships.find_by(followed_id: @other.id)
    assert_difference '@user.following.count', -1 do
      delete relationship_path(relationship), xhr: true
    end
  end
end
```

###演習
1.
リスト 14.36のrespond_toブロック内の各行を順にコメントアウトしていき、テストが正しくエラーを検知できるかどうか確認してみましょう。実際、どのテストケースが落ちたでしょうか?

```
ERROR["test_should_follow_a_user_the_standard_way", #<Minitest::Reporters::Suite:0x00005639201cf5e8 @name="FollowingTest">, 6.431756742000005]
 test_should_follow_a_user_the_standard_way#FollowingTest (6.43s)
ActionController::UnknownFormat:         ActionController::UnknownFormat: RelationshipsController#create is missing a template for this request format and variant.
        
        request.formats: ["text/html"]
        request.variant: []
            test/integration/following_test.rb:40:in `block (2 levels) in <class:FollowingTest>'
            test/integration/following_test.rb:38:in `block in <class:FollowingTest>'


ERROR["test_should_unfollow_a_user_the_standard_way", #<Minitest::Reporters::Suite:0x000056392023fa78 @name="FollowingTest">, 6.489258220000011]
 test_should_unfollow_a_user_the_standard_way#FollowingTest (6.49s)
ActionController::UnknownFormat:         ActionController::UnknownFormat: RelationshipsController#destroy is missing a template for this request format and variant.
        
        request.formats: ["text/html"]
        request.variant: []
            test/integration/following_test.rb:58:in `block (2 levels) in <class:FollowingTest>'
            test/integration/following_test.rb:56:in `block in <class:FollowingTest>'

  73/73: [===========================] 100% Time: 00:00:07, Time: 00:00:07

Finished in 7.62512s
73 tests, 338 assertions, 0 failures, 2 errors, 0 skips
```

2.
リスト 14.40のxhr: trueがある行のうち、片方のみを削除するとどういった結果になるでしょうか? このとき発生する問題の原因と、なぜ先ほどの演習で確認したテストがこの問題を検知できたのか考えてみてください。

わからない。