noggy’s blog

自分用の備忘録です。。。

14.2 [Follow]のwebインターフェイス

14.2で、やや複雑なデータモデリングの技術を説明した。これまでに使われた様々な関連付けを理解するのに一番良い方法は。実際にwebインターフェースで使ってみること。
この節では、モックアップで示したようにフォロー/フォロー解除の基本的なインターフェイスを実装する。

また、フォローしているユーザーと、フォロワーにそれぞれ表示用のページを作成する。14.3で、ユーザーのステータスフィードを追加して、サンプルを完成させる。

14.2.1 フォローのサンプルデータ

前章のときと同じように、サンプルデータを自動生成するrails db:seedを使って、DBにサンプルデータを登録できるとやはり便利。先にサンプルデータを自動生成できるようにしておけば、Webページの見た目のデザインから先に取りかかることができ、バックエンド機能の実装を後に回すことができる。

次は、リレーションシップのサンプルデータを生成するためのコード。
ここでは、最初のユーザーにユーザー3からユーザー51をフォローさせ、それから逆にユーザー4からユーザー41に最初のユーザーをフォローさせる。

#ユーザー
User.create!(name: "Example User",
             email: "example@railstutorial.org",
             password: "foobar",
             password_confirmation: "foobar",
             admin: true,
             activated: true,
             activated_at: Time.zone.now)
             
99.times do |n|
  name = Faker::Name.name
  email = "example-#{n+1}@railstutorial.org"
  password ="password"
  User.create!(name: name,
               email: email, 
               password: password,
               password_confirmation: password,
               activated: true,
               activated_at: Time.zone.now)
end

#マイクロポスト
users = User.order(:created_at).take(6)
50.times do
  content = Faker::Lorem.sentence(5)
  users.each { |user| user.microposts.create!(content: content) }
end

#リレーションシップ
users = User.all
user = users.first
following = users[2..50]
followers = users[3..40]
following.each { |followed| user.follow(followed) }
followers.each { |follower| follower.follow(user) }


DBに反映

$ rails db:migrate:reset
$ rails db:seed

14.2.2 統計と[Follow]フォーム

これでサンプルユーザに、フォローしているユーザーとフォロワーができた。
プロフィールページとHomeページを更新して、これを反映する。

最初に、プロフィールページとHomeページに、フォローしているユーザーとフォロワーの統計情報を表示するためのパーシャルを作成する。
次に、フォロー用とフォロー解除用のフォームを作成する。それから、フォローしているユーザーの一覧(following)とフォロワーの一覧(followers)を表示する専用のページを作成する。

フォロー数の単位には「following」を使い、例えば「50 following」といった具合に表示する。
この統計情報には、現在のユーザーがフォローしている人数と、現在のフォロワーの人数が表示されている。
それぞれの表示はリンクになっており、専用の表示ページに移動できる。

5章では、これらのリンクはダミーテキスト'#'を使って無効にしていたが、今回は実装する。

実際のページ作成は14.2.3で行うが、ルーティングは今実装する。このコードでは、resourcesブロックの内側で:memberメソッドが使っている。これは初登場のメソッドだが、まずはどんな動作するのか推測してみる。

Rails.application.routes.draw do
  root 'static_pages#home'
  get     '/help',    to: 'static_pages#help'
  get     '/about',   to: 'static_pages#about'
  get     '/contact', to: 'static_pages#contact'
  get     '/signup',  to: 'users#new'
  post    '/signup',  to: 'users#create'
  get     '/login',   to: 'sessions#new'
  post    '/login',   to: 'sessions#create'
  delete  '/logout',  to: 'sessions#destroy'
  resources :users do #変更と追加                                                          
    member do
      get :following, :followers
    end
  end
  resources :account_activations, only: [:edit]                                 
  resources :password_resets,     only: [:new, :create, :edit, :update] 
  resources :microposts,          only: [:create, :destroy]  
end

この場合のURLは/users/1/followingや/users/1/followersのようになるのではないかと推測したかもしれない。
そして、上のコードはまさにそれを行っている。また、どちらもデータを表示するページなので、適切なHTTPメソッドはGETリクエストになる。したがって、getメソッドを使って適切なレスポンスを返すようにする。

ちなみに、memberメソッドを使うとユーザーidが含まれているURLを扱うようになるが、idを指定せずにすべてのメンバーを表示するには、次のようにcollectionメソッドを使う。

resources :users do
  collection do
    get :tigers
  end
end

このコードは/users/tiggersというURLに応答する(アプリにあるすべてのtigerのリストを表示)。
生成されるルーティングテーブルは次のとおり。

HTTPリクエス URL アクション 名前付きルート
GET /users/1/following following following_user_path(1)
GET /users/1/followers followers followers_user_path(1)

この表で示したフォロー用とフォロワー用の名前付きルートを、今後の実装で使っていく。
ルーティングを定義したので、統計情報のパーシャルを実装する準備が整った。このパーシャルでは、divタグの中に2つのリンクを含めるようにする。

<% @user ||= current_user %> #①
<div class="stats">
  <a href="<%= following_user_path(@user) %>">
    <strong id="following" class="stat">
      <%= @user.following.count %> #②
    </strong>
    following
  </a>
  <a href="<%= followers_user_path(@user) %>">
    <strong id="followers" class="stat">
      <%= @user.followers.count %> #③
    </strong>
    followers
  </a>
</div>

①:このパーシャルはプロフィールページとHomeページの両方に表示されるので、現在のユーザーを取得
@usernilでない場合(つまりプロフィールページ)は何もせず、
nilの場合には@usercurrent_userに代入する。
②③:フォローしているユーザーの人数を、関連付けを使って計算。これはフォロワーについても同様。これは、マイクロポストの投稿数を表示した方法と次と同じ

@user.microposts.count

なお、今回も以前と同様に、Railsは高速化のためにDB内で合計を計算している点に注意!

一部の要素で、次のようにCSS idを指定していることにも注目!

<strong id="following" class="stat">
:
</strong>

こうしておくと、14.2.5でAjaxを実装するときに便利。そこでは、一意のidを指定してページ要素にアクセスしている。

これで統計情報パーシャルができあがる。Homeページにこの統計情報を表示するには、次のようにすると簡単。

<% @user ||= current_user %>
<div class="stats">
  <a href="<%= following_user_path(@user) %>">
    <strong id="following" class="stat">
      <%= @user.following.count %>
    </strong>
    following
  </a>
  <a href="<%= followers_user_path(@user) %>">
    <strong id="followers" class="stat">
      <%= @user.followers.count %>
    </strong>
    followers
  </a>
</div>

統計情報にスタイルを与えるために、SCSSを追加する。

変更の結果、Homeページは以下のようにする。

/* sidebar */
 :
.gravatar {
  float: left;
  margin-right: 10px;
}

.gravatar_edit {
  margin-top: 15px;
}

.stats {
  overflow: auto;
  margin-top: 0;
  padding: 0;
  a {
    float: left;
    padding: 0 10px;
    border-left: 1px solid &gray-lighter;
    color: gray;
    &:first-child {
      padding-left: 0;
      border: 0;
    }
    $:hover {
      text-decoration: none;
      color: blue;
    }
  }
  strong {
    display: block;
  }
}

.user_avatars {
  overflow: auto;
  margin-top: 10px;
  .gravatar {
    margin: 1px 1px;
  }
  a {
    padding: 0;
  }
}

.users.follow {
  padding: 0;
}

/*  forms */
:

この後すぐ、プロフィールにも統計情報パーシャルを表示するが、今のうちに[Follow]/[Unfollow]ボタン用のパーシャルを作成する。

<% unless current_user?(@user) %>
  <div id="follow_form">
    <% if current_user.following?(@user) %>
      <%= render 'unfollow' %>
    <% else %>
      <%= render 'follow' %>
    <% end %>
  </div>
<% end %>

このコードは、followunfollowのパーシャルに作業を振っているだけ。
(urlのユーザーをログインユーザーがフォローしていればunfollow、フォローしていなければfollowをレンダリング)
パーシャルでは、Relationshipsリソース用の新しいルーティングが必要。これを、Micropostsリソースの例に従って作成する。

Rails.application.routes.draw do
  root 'static_pages#home'
  get     '/help',    to: 'static_pages#help'
  get     '/about',   to: 'static_pages#about'
  get     '/contact', to: 'static_pages#contact'
  get     '/signup',  to: 'users#new'
  post    '/signup',  to: 'users#create'
  get     '/login',   to: 'sessions#new'
  post    '/login',   to: 'sessions#create'
  delete  '/logout',  to: 'sessions#destroy'
  resources :users do
    member do
      get :following, :followers
    end
  end
  resources :users                                                              
  resources :account_activations, only: [:edit] 
  resources :password_resets,     only: [:new, :create, :edit, :update] 
  resources :microposts,          only: [:create, :destroy]    
  resources :relationships,       only: [:create, :destroy]  #追加 
end

フォロー/フォロー解除用のパーシャルも書く。

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

<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
    html: { method: :delete }) do |f| %>
    <%= f.submit "Unfollow", class: "btn" %>
<% end %>

これら2つのフォームでは、いずれもform_forを使ってRelationshipモデルオブジェクトを操作している。
これらの2つのフォームの主な違いは、フォローフォームは新しいリレーションシップを作成するのに対し、
フォロー解除フォームは既存のリレーションシップを見つけ出すという点。

すなわち、前者はPOSTリクエストをRelationshipsコントローラに送信してリレーションシップをcreate(作成)し、
後者はDELETEリクエストを送信してリレーションシップをdestroy(削除)するということ。

最終的に、このフォロー/フォロー解除フォームにはボタンしかないことが理解できる。
しかし、それでもフォローフォームではfollowed_idをコントローラに送信する必要がある。これを行うために、hidden_field_tagメソッドを使う。このメソッドは、次のフォーム用HTMLを生成する。

<input id="followed_id" name="followed_id" type="hidden" value="3" />

12.3で見たように、隠しフィールドのinputタグを使うことで、ブラウザ上に表示させずに適切な情報を含めることができる。
これでパーシャルとしてフォロー用フォームをプロフィールページに表示できるようになった。
プロフィールには、それぞれ[Follow]、[Unfollow]ボタンが表示される。

<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <h1>
        <%= gravatar_for @user %>
        <% @user.name %>
      </h1>
    </section>
    <section class="stats"> #追加
        <%= render 'shared/stats' %> #追加
    </section>
  </aside>
  <div class="col-md-8">
      <%= render 'follow_form' if logged_in? %> #追加
      <% if @user.microposts.any? %>
  :


これらのボタンの実装には2通りの方法がある。
1つは標準的な方法、もう1つはAjaxを使う方法。
でもその前に、フォローしているユーザーとフォロワーを表示するページをそれぞれ作成してHTMLインターフェイスを完成させる。

14.2.3 [Following]と[Followers]ページ

フォローしているユーザーを表示するページと、フォロワーを表示するページは、いずれもプロフィールページとユーザー一覧ページを合わせたような作りになるという点で似ている。

どちらにもフォローの統計情報などのユーザー情報を表示するサイドバーと、ユーザーのリストがある。
さらに、サイドバーには小さめのユーザープロフィール画像のリンクを格子状に並べて表示する。

ここでの最初の作業は、フォローしているユーザーのリンクとフォロワーのリンクを動くようにすること。
どちらのページでもユーザーのログインを要求するようにする。そこで前回のアクセス制御と同様に、まずはテストから書いていく。今回使うテストは以下の通り。

  :
  test "should redirect following when not logged in" do
    get following_user_path(@user)
    assert_redirected_to login_url
  end

  test "should redirect followers when not logged in" do
    get followers_user_path(@user)
    assert_redirected_to login_url
  end

上記コードではfollowing/followersの名前付きルートを使っている点に注意!この実装には1つだけトリッキーな部分がある。
それはUsersコントローラに2つの新しいアクションを追加する必要があるということ。

これはroutesで定義した2つのルーティングにもとづいており、これらはそれぞれfollowingおよびfollowersと呼ぶ必要がある。それぞれのアクションでは、タイトルを認定し、ユーザーを検索し、@user.followingまたは@user.followersからデータを取り出し、ページネーションを行なって、ページを出力する必要がある。作成したコードは↓

  before_action :logged_in_user, only: [index, :edit, :update, :destroy,
                                                            :following, :followers] #追加
  :
  def following
    @title = "Following"
    @user  = User.find(params[:id])
    @users = @user.following.paginate(page: params[:page])
    render 'show_follow'
  end

  def followers
    @title = "Followers"
    @user  = User.find(params[:id])
    @users = @user.followers.paginate(page: params[:page])
    render 'show_follow'
  end
  
  private
  :
end

これまで見てきたように、Railsは慣習に従って、アクションに対応するビューを暗黙的に呼び出す。
例えば、showアクションの最後でshow.html.erbを呼び出す、といった具合。

一方で、上記のいずれのアクションも、renderを明示的に呼び出し、show_followという同じビューを出力している。したがって、作成が必要なビューはこれ1つ。renderで呼び出しているビューが同じである理由は、このERBはどちらの場合でもほぼ同じであり、両方の場合をカバーできるため。

<% provide(:title, @title) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <%= gravatar_for @user %>
      <h1><%= @user.name %></h1>
      <span><%= link_to "view my profile", @user %></span>
      <span><b>Microposts:</b> <%= @user.microposts.count %></span>
    </section>
    <section class="stats">
      <%= render 'shared/stats' %>
      <% if @users.any? %>
        <div class="user_avatars">
          <% @users.each do |user| %>
            <%= link_to gravatar_for(user, size: 30), user %>
          <% end %>
        </div>
      <% end %>
    </section>
  </aside>
  <div class="col-md-8">
    <h3><%= @title %></h3>
    <% if @users.any? %>
      <ul class="users follow">
        <%= render @users %>
      </ul>
      <%= will_paginate %>
    <% end %>
  </div>
</div>

users_controller.rbにあるアクションは、2通りの方法で上のビューを呼び出している。
followingアクションでfollowingを通してshow_followビューを呼び出し、
followersアクションではfollowersを通してshow_followビューを呼び出す。
このとき、上のコードでは現在のユーザーを一切使っていない点に注目!したがって、他のユーザーのフォロワー一覧ページもうまく動く。

テストは成功。

次に、show_followの描画結果を確認するため、統合テストを書いていく。
ただし、今回の統合テストは基本的なテストだけに留めておき、網羅的なテストにはしていない。
これはHTML構造を網羅的にチェックするテストは壊れやすく、生産性を逆に落としかねないから。

したがって今回は、正しい数が表示されているかどうかと、正しいURLが表示されているかどうかの2つのテストを書く。
いつものように統合テストを生成するところから始める。

$ rails g integration_test following
      invoke  test_unit
      create    test/integration/following_test.rb

今度は、テストデータをいくつか揃える。リレーションシップ用のfixtureにデータを追加。

orange:
  content: "I just ate an orange!"
  created_at: <%= 10.minutes.ago %>
  user: michael

ユーザーとマイクロポストは関連付けできることを思い出す。上のコードではユーザー名を書いていたが、

user: michael

次のようにユーザーidでも関連付けできる

user_id: 1

この例を参考に、Relationship用のfixtureにテストデータを追加する。

one:
  follower: michael
  followed: lana

two:
  follower: michael
  followed: malory

three:
  follower: lana
  followed: michael

four:
  follower: archer
  followed: michael


上記のfixtureでは、
前半の2つでMichaelがLanaとMaloryをフォローし、
後半の2つでLanaとArcherがMichaelをフォローしている。

あとは、正しい数かどうかを確認するために、assert_matchメソッドを使ってプロフィール画面のマイクロポスト数をテストする。さらに、正しいURLかどうかをテストするコードも加えると、次のようになる。

  :
  def setup
    @user = users(:michael)
    log_in_as(@user)
  end

  test "following page" do
    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)
    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

なお、上では、このようなコードを書いているが、

assert_not @user.following.empty?

これは次のコードを確かめるためのテストであって

@user.following.each do |user|
  assert_select "a[href=?]", user_path(user)
end

無意味なテストでない(followersについても同様)。つまり、もし@user.following.empty?の結果がtrueであれば、assert_select内のブロックが実行されなくなるため、その場合においてテストが適切なセキュリティモデルを確認できなくなることを防いでいる。

上の変更を加えるとテストが成功

14.2.4 [Follow]ボタン(基本編)

ビューが整ってきた。いよいよ[Follow]/[Unfollow]ボタンを動作させる。
フォローとフォロー解除はそれぞれリレーションシップの作成と削除に対応しているため、まずはRelationshipsコントローラが必要。

Relationshipsコントローラの生成
$ rails g controller Relationships

Relationshipsコントローラのアクションでアクセス制御することはそこまで難しくない。
しかし、前回のアクセス制御の時と同様に最初にテストを書き、それをパスするように実装することでセキュリティモデルを確立させていく。
今回はまず、コントローラのアクションにアクセスするとき、ログイン済みのユーザーであるかどうかをチェックする。

もしログインしていなければログインページにリダイレクトされるので、Relationshipのカウントが変わっていないことを確認する。

require 'test_helper'

class RelationshipsControllerTest < ActionDispatch::IntegrationTest

  test "create should require logged-in user" do
    assert_no_difference 'Relationship.count' do
      post relationships_path
    end
    assert_redirected_to login_url
  end

  test "destroy should require logged-in user" do
    assert_no_difference 'Relationship.count' do
      delete relationship_path(relationships(:one))
    end
    assert_redirected_to login_url
  end
end

次に、上のテストをパスさせるために、logged_in_userフィルターをRelationshipsコントローラのアクションに対して追加する。

class RelationshipsController < ApplicationController
  before_action :logged_in_user

  def create
  end

  def destroy
  end
end

[Follow]/[Unfollow]ボタンを動作させるためには、フォームから送信されたパラメータを使って、followed_idに対応するユーザーを見つけてくる必要がある。

その後、見つけてきたユーザーに対して適切にfollow/unfollowメソッド(Userモデルで定義した)を使う。

class RelationshipsController < ApplicationController
  before_action :logged_in_user

  def create
    user = User.find(params[:followed_id])
    current_user.follow(user)
    redirect_to user
  end

  def destroy
    user = Relationship.find(params[:id]).followed
    current_user.unfollow(user)
    redirect_to user
  end
end

上を見てみれば、先ほどのセキュリティ問題が実はそれほど重要なものではないことを理解できる。
もし、ログインしていないユーザーが(curlなどのコマンドラインツールなどを使って)これらのアクションに直接アクセスするようなことがあれば、current_usernilになり、どちらのメソッドでも2行目で例外が発生する。

エラーにはなるが、アプリやデータに影響は生じない。このままでも支障はないが、このような例外には頼らない方がよいので、上ではひと手間かけて、セキュリティのためのレイヤーを追加した。

これで、フォロー/フォロー解除の機能が完成した。どのユーザーも、他のユーザーをフォローしたりリフォローしたりできる。
ブラウザ上でボタンをクリックして、確かめてみる。振る舞いを検証する統合テストは14.2.6で実装する。

14.2.5 [Follow]ボタン(Ajax編)

フォロー関連の機能の実装は完了したが、ステータスフィードに取りかかる前にもう1つだけ機能を洗練させてみる。
14.2.4では、Relationshipsコントローラのcreateアクションとdestroyアクションを単に元のプロフィールにリダイレクトしていた。つまり、

(1)ユーザーはプロフィールページを最初に表示し、
(2)それからユーザーをフォローし、
(3)その後すぐ元のページにリダイレクトされる

という流れになる。
ユーザーをフォローした後、本当にそのページから離れて元のページに戻らないといけないのか。この点を考えなおしてみる。
これは、Ajaxを使えば解決できる。

Ajaxを使えば、Webページからサーバーに「非同期」で、ページを移動させることなくリクエストを送信することができる。
WebフォームにAjaxを採用するのは今や当たり前になりつつあるので、RailsでもAjaxを簡単に実装できるようになっている。

フォロー用とフォロー解除用のパーシャルをこれに沿って更新するのは簡単。次のコードがあるとすると、

form_for

次のように置き換えるだけ

form_for ..., remote: true

これだけでRailsは自動的にAjaxを使うようになる。具体的な更新の結果は次の通り

<%= form_for(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 %>

<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
    html: { method: :delete },
    remote: true) do |f| %> #変更
    <%= f.submit "Unfollow", class: "btn" %>
<% end %>

ERBによって実際に生成されるHTMLはそれほど重要ではないが、次はその核心部分。

<form action="/relationships/117" class="edit_relationship" data-remote="true"
      id="edit_relationship_117" method="post" >
:
</form>

ここでは、formタグの内部でdata-remote="true"を設定している。
これは、JavaScriptによるフォーム操作を許可することをRailsに知らせるためのもの。現在のRailsではHTMLプロパティを使って簡単にAjaxが扱えるようになっている。

フォームの更新が終わったので、今度はこれに対応するRelationshipsコントローラを改造して、Ajaxリクエストに応答できるようにする。こういったリクエストの種類によって応答を場合分けする時は、respond_toメソッドを使う。

respond_to do |format|
  format.html { redirect_to user }
  format.js
end

上の(ブロック内の)コードのうち、いずれかの1行が実行されるという点が重要(このためrespond_toメソッドは、上から順に実行する逐次処理というより、if文を使った分岐処理に近いイメージ)。
RelationshipsコントローラでAjaxに対応させるために、respond_toメソッドをcreateアクションとdestroyアクションにそれぞれ追加してみる。

この時、ユーザーのローカル変数(user)をインスタンス変数(@user)に変更した点に注目!
これは、_follow.html.erb_unfollow.html.erbを実装したことにより、インスタンス変数が必要になったからである。

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 }
      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

上でAjaxリクエストに対応したので、今度はブラウザ側でJavaScriptが無効になっていた場合(Ajaxリクエストが送れない場合)でもうまく動くようにする。

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

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.
    :
    # 認証トークンをremoteフォームに埋め込む
    config.action_view.embed_authenticity_token_in_remote_forms = true #変更
  end
end

一方で、JavaScriptが有効になっていても、まだ十分に対応できていない部分がある。
というのも、Ajaxリクエストを受信した場合は、Railsが自動的にアクションと同じ名前を持つJavaScript用の埋め込みRubyファイル(create.js.erbdestroy.js.erbなど)を呼び出すから。これらのファイルではJavaScriptと埋め込みRuby(ERb)をミックスして現在のページに対するアクションを実行することができる。

ユーザーをフォローしたときや、フォロー解除した時にプロフィールページを更新するために、我々がこれから作成および編集しなければならないのは、これらのファイル。

JS-ERbファイルの内部では、DOM(Document Object Model)を使ってページ操作するため、RailsjQuery JavaScriptヘルパーを自動的に提供している。これによりjQueryライブラリの膨大なDOM操作用メソッドが使えるようになるが、今回使うのは2つ。

まず1つ目は、ドル記号($)とCSS idを使って、DOM要素にアクセスする文法について知る必要がある。
例えば、follow_formの要素をjQueryで操作するには、次のようにアクセスする。

$("#follow_form")

これはフォームを囲むdivタグであり、フォームそのものではなかった。
jQueryの文法はCSSの記法から影響を受けており、#シンボルを使ってCSSのidを指定する。jQueryCSSと同様、ドット.を使ってCSSクラスを操作できる。

次に必要なメソッドはhtml。これは、引数の中で指定された要素の内側にあるHTMLを更新する。
例えば、フォロー用フォーム全体を"foobar"という文字列で置き換えたい場合は、次のようなコードになる。

$("#follow_form").html("foobar")

純粋なJavaScriptと異なり、JS-ERbファイルでは組み込みRuby(ERb)が使える。
create.js.erbファイルでは、フォロー用のフォームをunfollowパーシャルで更新し、フォロワーのカウントを更新するのにERbを使っている。
このコードではescape_javascriptメソッドを使っている点に注目。
このメソッドは、JavaScriptファイル内にHTMLを挿入する時に実行結果をエスケープするために必要。

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

各行の末尾にセミコロン;があることに注目!これは1950年代中ごろに開発されたALGOLまで遡る。

destroy.js.erbファイルの方も同様

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

これらのコードにより、プロフィールページを更新させずにフォローとフォロー解除できるようになったはず。