はじめに
Ruby on Rails チュートリアル実例を使ってRailsを学ぼう 第4版の 14章 14.2 Relationshipモデルの演習まとめ&解答例です。
個人の解答例なので、誤りがあればご指摘ください。
動作環境
- cloud9
- ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
- Rails 5.0.0.1
14.2.1 フォローのサンプルデータ
本章での学び
【seeds】サンプルデータにfollowing/followerの関係性を追加する
rails db:seed
を使って、DBにサンプルデータを登録できるようにする。
- 最初のユーザは、3〜51までのユーザをフォローする
- ユーザ4〜41は、最初のユーザをフォローする
# リレーションシップ users = User.all user = User.first following = users[2..50] followers = users[3..40] following.each { |followed| user.follow(followed) } followers.each { |follower| follower.follow(user) }
【DB】DBのリセット
yokoyan:~/workspace/sample_app (following-users) $ rails db:migrate:reset Dropped database 'db/development.sqlite3' Dropped database 'db/test.sqlite3' Created database 'db/development.sqlite3' Created database 'db/test.sqlite3' == 20170417215343 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0026s == 20170417215343 CreateUsers: migrated (0.0028s) ============================= == 20170423125906 AddIndexToUsersEmail: migrating ============================= -- add_index(:users, :email, {:unique=>true}) -> 0.0016s == 20170423125906 AddIndexToUsersEmail: migrated (0.0017s) ==================== == 20170424041610 AddPasswordDigestToUsers: migrating ========================= -- add_column(:users, :password_digest, :string) -> 0.0021s == 20170424041610 AddPasswordDigestToUsers: migrated (0.0022s) ================ == 20170514215307 AddRememberDigestToUsers: migrating ========================= -- add_column(:users, :remember_digest, :string) -> 0.0011s == 20170514215307 AddRememberDigestToUsers: migrated (0.0013s) ================ == 20170614215124 AddAdminToUsers: migrating ================================== -- add_column(:users, :admin, :boolean, {:default=>false}) -> 0.0015s == 20170614215124 AddAdminToUsers: migrated (0.0016s) ========================= == 20170618221238 AddActivationToUsers: migrating ============================= -- add_column(:users, :activation_digest, :string) -> 0.0035s -- add_column(:users, :activated, :boolean, {:dafault=>false}) -> 0.0009s -- add_column(:users, :activated_at, :datetime) -> 0.0009s == 20170618221238 AddActivationToUsers: migrated (0.0056s) ==================== == 20170703042006 AddResetToUsers: migrating ================================== -- add_column(:users, :reset_digest, :string) -> 0.0019s -- add_column(:users, :reset_sent_at, :datetime) -> 0.0006s == 20170703042006 AddResetToUsers: migrated (0.0027s) ========================= == 20170719214241 CreateMicroposts: migrating ================================= -- create_table(:microposts) -> 0.0026s -- add_index(:microposts, [:user_id, :created_at]) -> 0.0014s == 20170719214241 CreateMicroposts: migrated (0.0042s) ======================== == 20170820040443 AddPictureToMicroposts: migrating =========================== -- add_column(:microposts, :picture, :string) -> 0.0015s == 20170820040443 AddPictureToMicroposts: migrated (0.0016s) ================== == 20170823121831 CreateRelationships: migrating ============================== -- create_table(:relationships) -> 0.0013s -- add_index(:relationships, :follower_id) -> 0.0008s -- add_index(:relationships, :followed_id) -> 0.0012s -- add_index(:relationships, [:follower_id, :followed_id], {:unique=>true}) -> 0.0023s == 20170823121831 CreateRelationships: migrated (0.0061s) =====================
【DB】初期データの投入
yokoyan:~/workspace/sample_app (following-users) $ rails db:seed
演習1
コンソールを開き、User.first.followers.countの結果がリスト 14.14で期待している結果と合致していることを確認してみましょう。
38人にフォローされていることを確認。
yokoyan:~/workspace/sample_app (following-users) $ rails console Running via Spring preloader in process 7369 Loading development environment (Rails 5.0.0.1) >> user = User.first User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2017-08-26 04:58:19", updated_at: "2017-08-26 04:58:19", password_digest: "$2a$10$0FM4wRDzxU.nOpDa5RmxZuyTz/omIcX4Qm4nguWAde0...", remember_digest: nil, admin: true, activation_digest: "$2a$10$gQQiCXRCh4ZDsruNiSv5euZW4Vwt7kcKm6tZkxM1MMg...", activated: true, activated_at: "2017-08-26 04:58:19", reset_digest: nil, reset_sent_at: nil> >> ?> user.followers.count (0.5ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 1]] => 38
演習2
先ほどの演習と同様に、User.first.following.countの結果も合致していることを確認してみましょう。
49人のユーザーをフォローしていることを確認。
?> user.following.count (0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? [["follower_id", 1]] => 49
14.2.2 統計と [Follow] フォーム
本章での学び
【routes】Usersコントローラにアクションを追加する
Usersコントローラに、followingアクションと、followerアクションを追加する。
member
メソッドを使うことで、メンバールーティングとして追加できる。
/users/1/following や、/users/2/followers/ というURLをGETで送信する。
resources :users do member do get :following,:followers end end
ルーティングの結果を確認する。 following_user_pathと、follower_users_pathが使えるようになっていることを確認。
yokoyan:~/workspace/sample_app (following-users) $ rails routes Prefix Verb URI Pattern Controller#Action root GET / static_pages#home help GET /help(.:format) static_pages#help about GET /about(.:format) static_pages#about contact GET /contact(.:format) static_pages#contact signup GET /signup(.:format) users#new login GET /login(.:format) sessions#new POST /signup(.:format) users#create POST /login(.:format) sessions#create logout DELETE /logout(.:format) sessions#destroy following_user GET /users/:id/following(.:format) users#following followers_user GET /users/:id/followers(.:format) users#followers
【view】フォロワーの統計情報を表示するパーシャル
パーシャルを新規作成する。
yokoyan:~/workspace/sample_app (following-users) $ touch app/views/shared/_stats.html.erb
中身を実装する。
<% @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>
【view】統計情報パーシャルの呼び出し
作成した統計情報パーシャルを呼び出す。
<div class="row"> <aside class="col-md-4"> <section class="user_info"> <%= render 'shared/user_info' %> </section> <section class="stats"> <%= render 'shared/stats' %> </section> <section> <%= render 'shared/micropost_form' %> </section> </aside> <div class="col-md-8"> <h3>Micropost Feed</h3> <%= render 'shared/feed' %> </div> </div>
【scss】Homeページのサイドバー用のSCSS
本章で使うSCSSを作成する。
.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; }
【view】フォロー・フォロー解除のパーシャル
パーシャルを新規作成する。
yokoyan:~/workspace/sample_app (following-users) $ touch app/views/users/_follow_form.html.erb
パーシャルの中身を実装する。
<% unless current_user?(@user) %> <div id="follow_form"> <% if current_user.following?(@user) %> <%= render 'unfollow' %> <% else %> <%= render 'follow' %> <% end %> </div> <% end %>
【routes】Relationshipリソース用のルーティングを追加する
フォロー(create)と、フォロー解除(destroy)を追加する。
resources :relationships, only: [:create, :destroy]
追加したルーティングを確認する。
yokoyan:~/workspace/sample_app (following-users) $ rails routes Prefix Verb URI Pattern Controller#Action ・・・略・・・ relationships POST /relationships(.:format) relationships#create relationship DELETE /relationships/:id(.:format) relationships#destroy
【view】フォロー・フォロー解除のフォームパーシャルを作成する
パーシャルを新規作成する。
yokoyan:~/workspace/sample_app (following-users) $ touch app/views/users/_follow.html.erb yokoyan:~/workspace/sample_app (following-users) $ touch app/views/users/_unfollow.html.erb
フォロー時のフォームパーシャルを実装する。
フォロー登録を行うために、postメソッドを送信する。
フォローするユーザのfollowed_id
はhidden属性で生成し、submit時にコントローラへ送信する。
<%= 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 %>
フォロー解除時のフォームパーシャルを実装する。 フォロー解除を行うために、deleteメソッドを送信する。
<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id), html: { method: :delete}) do |f| %> <%= f.submit "Unfollow", class: "btn" %> <% end %>
【view】プロフィールページにパーシャルを追加する。
各ユーザのプロフィールページに、フォロー数・フォロワー数と、フォローボタンを追加する。
<% provide(:title, @user.name) %> <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? %>
演習1
ブラウザから /users/2 にアクセスし、フォローボタンが表示されていることを確認してみましょう。同様に、/users/5 では [Unfollow] ボタンが表示されているはずです。さて、/users/1 にアクセスすると、どのような結果が表示されるでしょうか?
/users/2にアクセス。 Followボタンが表示される。
/users/5にアクセス。 Unfollowボタンが表示される。
/users/1にアクセス。 どちらのボタンも表示されない。(ログインユーザ自身であるため)
演習2
ブラウザからHomeページとプロフィールページを表示してみて、統計情報が正しく表示されているか確認してみましょう。
Homeページを表示。 フォロー数、フォロワー数の統計情報が表示されている。
プロフィールページを表示。 フォロー数、フォロワー数の統計情報が表示されている。
演習3
Homeページに表示されている統計情報に対してテストを書いてみましょう。ヒント: リスト 13.28に示したテストを追加してみてください。同様にして、プロフィールページにもテストを追加してみましょう。
Homeページにフォロー数、フォロワー数が表示されているかテストする。
test "count relationships" do log_in_as(@user) get root_path assert_match @user.active_relationships.count.to_s, response.body assert_match @user.passive_relationships.count.to_s, response.body end
プロフィールページにフォロー数、フォロワー数が表示されているかテストする。
test "profile display" do get user_path(@user) assert_template 'users/show' assert_select 'title', full_title(@user.name) assert_select 'h1', text: @user.name assert_select 'h1>img.gravator' assert_match @user.microposts.count.to_s, response.body assert_select 'div.pagination' @user.microposts.paginate(page: 1) do |micropost| assert_match micropost.content, response.body end assert_select 'div.pagination', count:1 assert_match @user.active_relationships.count.to_s, response.body assert_match @user.passive_relationships.count.to_s, response.body end
テストがgreenになることを確認。
yokoyan:~/workspace/sample_app (following-users) $ rails test Running via Spring preloader in process 12301 Started with run options --seed 59991 DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only ] 35% Time: 00:00:03, ETA: 00:00:06 the following keyword arguments in future Rails versions: params, headers, env, xhr, as Examples: get '/profile', params: { id: 1 }, headers: { 'X-Extra-Header' => '123' }, env: { 'action_dispatch.custom' => 'custom' }, xhr: true, as: :json (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27) 64/64: [==================================================================================================================] 100% Time: 00:00:05, Time: 00:00:05 Finished in 5.64197s 64 tests, 277 assertions, 0 failures, 0 errors, 0 skips
14.2.3 [Following] と [Followers] ページ
本章での学び
【test】フォロー・フォロワーページの認可をテストする
どちらのページも、ログインを必須とする。
should redirect following when not logged in
- getリクエスト送信(following_user_path(@user))
- ログインページヘリダイレクトされること
should redirect followers when not logged in
- getリクエスト送信(followers_user_path(@user))
- ログインページヘリダイレクトされること
上記を踏まえて実装する。
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 follower_users_path(@user) assert_redirected_to login_url end
【controller】followingアクションとfollowersアクションの追加
Usersコントローラに2つのアクションを追加する。 それぞれのアクションで、タイトル設定、ユーザ検索、 フォロー(またはフォロワー)ユーザのページ処理、show_followビューの呼び出しを行う。
また、どちらのアクションもログインが必須であるため、before_actionに追加する。
class UsersController < ApplicationController 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
【view】フォロー(またはフォロワー)ユーザを表示する
コントローラから呼び出される新規ビューを作成する。
yokoyan:~/workspace/sample_app (following-users) $ touch app/views/users/show_follow.html.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>
【test】動作確認
現時点でテストがgreenになることを確認。
yokoyan:~/workspace/sample_app (following-users) $ rails test Running via Spring preloader in process 14693 Started with run options --seed 46484 DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only ] 62% Time: 00:00:03, ETA: 00:00:02 the following keyword arguments in future Rails versions: params, headers, env, xhr, as Examples: get '/profile', params: { id: 1 }, headers: { 'X-Extra-Header' => '123' }, env: { 'action_dispatch.custom' => 'custom' }, xhr: true, as: :json (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27) 66/66: [==================================================================================================================] 100% Time: 00:00:05, Time: 00:00:05 Finished in 5.25675s 66 tests, 279 assertions, 0 failures, 0 errors, 0 skips
【test】統合テストの作成
HTML構造を網羅的にチェックするテストは壊れやすいため、基本的なテストに留める。
統合テストを生成する。
yokoyan:~/workspace/sample_app (following-users) $ rails generate integration_test following Running via Spring preloader in process 14844 Expected string default value for '--jbuilder'; got true (boolean) invoke test_unit create test/integration/following_test.rb
【fixture】テストデータの作成
フォローとフォロワーのデータを生成する。 ユーザの関連付けは、idでも行うことができる。
one: follower: michael followed: lana two: follower: michael followed: malory three: follower: lana followed: michael four: follower: archer followed: michael
【test】統合テストの実装
setup
- fixtureからmichaelを取得
- michaelでログインする
following page
- getリクエストを送信(following_user_path(@user))
- フォローユーザが空ではないことを確認
- フォローユーザの数がHTML内に存在すること
- フォローユーザの数だけ繰り返す
- フォローユーザのプロフィールページへのリンクが存在すること
followers page
- getリクエストを送信(followers_user_path(@user))
- フォロワーユーザが空ではないことを確認
- フォロワーユーザの数がHTML内に存在すること
- フォロワーユーザの数だけ繰り返す
- フォロワーユーザのプロフィールページへのリンクが存在すること
上記を踏まえて実装する。
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 "followersh 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
テストの実施
テストがgreenになることを確認。
yokoyan:~/workspace/sample_app (following-users) $ rails test Running via Spring preloader in process 15264 Started with run options --seed 61814 DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only ] 16% Time: 00:00:01, ETA: 00:00:10 the following keyword arguments in future Rails versions: params, headers, env, xhr, as Examples: get '/profile', params: { id: 1 }, headers: { 'X-Extra-Header' => '123' }, env: { 'action_dispatch.custom' => 'custom' }, xhr: true, as: :json (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27) 68/68: [==================================================================================================================] 100% Time: 00:00:05, Time: 00:00:05 Finished in 5.08686s 68 tests, 289 assertions, 0 failures, 0 errors, 0 skips
演習1
ブラウザから /users/1/followers と /users/1/following を開き、それぞれが適切に表示されていることを確認してみましょう。サイドバーにある画像は、リンクとしてうまく機能しているでしょうか?
/users/1/followersへアクセス。
サイドバーの画像がリンクとして機能していることを確認。
/users/1/followingへアクセス。
演習2
リスト 14.29のassert_selectに関連するコードをコメントアウトしてみて、テストが正しく red に変わることを確認してみましょう。
該当箇所をコメントアウトする。
<% @users.each do |user| %> <%= #link_to gravatar_for(user, size:30), user %> <% end %>
テスト結果がredになることを確認。 (動作確認後は戻しておくこと)
yokoyan:~/workspace/sample_app (following-users) $ rails test Running via Spring preloader in process 16122 Started with run options --seed 59647 ERROR["test_followersh_page", FollowingTest, 1.2620783941820264] test_followersh_page#FollowingTest (1.26s) SyntaxError: SyntaxError: /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:19: unknown regexp options - ct /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:20: syntax error, unexpected '<' </aside> ^ /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:22: unknown regexp option - h /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:22: syntax error, unexpected tINTEGER, expecting ')' ...output_buffer.safe_append='</h3> ... ^ /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:24: syntax error, unexpected keyword_class, expecting keyword_do or '{' or '(' @output_buffer.safe_append=' <ul class="users follow"> ^ /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:26: syntax error, unexpected '<', expecting ')' </ul> ^ /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:29: unknown regexp options - dv /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:30: syntax error, unexpected '<' </div>'.freeze;@output_buffer.to_s ^ /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:30: unterminated regexp meets end of file app/controllers/users_controller.rb:69:in `followers' test/integration/following_test.rb:23:in `block in <class:FollowingTest>' ERROR["test_following_page", FollowingTest, 1.299861785955727] test_following_page#FollowingTest (1.30s) SyntaxError: SyntaxError: /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:19: unknown regexp options - ct /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:20: syntax error, unexpected '<' </aside> ^ /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:22: unknown regexp option - h /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:22: syntax error, unexpected tINTEGER, expecting ')' ...output_buffer.safe_append='</h3> ... ^ /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:24: syntax error, unexpected keyword_class, expecting keyword_do or '{' or '(' @output_buffer.safe_append=' <ul class="users follow"> ^ /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:26: syntax error, unexpected '<', expecting ')' </ul> ^ /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:29: unknown regexp options - dv /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:30: syntax error, unexpected '<' </div>'.freeze;@output_buffer.to_s ^ /home/ubuntu/workspace/sample_app/app/views/users/show_follow.html.erb:30: unterminated regexp meets end of file app/controllers/users_controller.rb:62:in `following' test/integration/following_test.rb:14:in `block in <class:FollowingTest>' DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only ] 30% Time: 00:00:03, ETA: 00:00:07 the following keyword arguments in future Rails versions: params, headers, env, xhr, as Examples: get '/profile', params: { id: 1 }, headers: { 'X-Extra-Header' => '123' }, env: { 'action_dispatch.custom' => 'custom' }, xhr: true, as: :json (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27) 68/68: [==================================================================================================================] 100% Time: 00:00:05, Time: 00:00:05 Finished in 5.38619s 68 tests, 279 assertions, 0 failures, 2 errors, 0 skips
14.2.4 [Follow] ボタン (基本編)
本章での学び
【controller】Relationshipsコントローラの作成
コマンドで自動生成する。
yokoyan:~/workspace/sample_app (following-users) $ rails generate controller Relationships Running via Spring preloader in process 1237 Expected string default value for '--jbuilder'; got true (boolean) Expected string default value for '--helper'; got true (boolean) Expected string default value for '--assets'; got true (boolean) create app/controllers/relationships_controller.rb invoke erb create app/views/relationships invoke test_unit create test/controllers/relationships_controller_test.rb invoke helper create app/helpers/relationships_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/relationships.coffee invoke scss create app/assets/stylesheets/relationships.scss
【test】Relationshipsコントローラのテストを実装
- create should require logged-in user
- Relationship.countが変わらないこと
- postリクエストを送信(relationships_path)
- ログイン画面へリダイレクトされること
- Relationship.countが変わらないこと
- destroy should require logged-in user
- Relationship.countが変わらないこと
- deleteリクエストを送信(relationship_path(relationship(:one)))
- fixtureのrelathionship内の:oneを削除
- deleteリクエストを送信(relationship_path(relationship(:one)))
- ログイン画面へリダイレクトされること
- Relationship.countが変わらないこと
上記を踏まえて実装する。
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
【controller】リレーションシップのアクセス制御
テストコードをgreenにするために、Relationshipsコントローラに、before_actionを追加する。また、createアクションとdestroyアクションを追加する。
class RelationshipsController < ApplicationController before_action :logged_in_user def create end def destroy end end
【controller】アクションの実装
createアクション、destroyアクションの中身を実装する。
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
演習1
ブラウザ上から /users/2 を開き、[Follow] と [Unfollow] を実行してみましょう。うまく機能しているでしょうか?
/users/2 を開く。
Followを実行。
Unfollowを実行。
演習2
先ほどの演習を終えたら、Railsサーバーのログを見てみましょう。フォロー/フォロー解除が実行されると、それぞれどのテンプレートが描画されているでしょうか?
フォロー時のログは以下の通り。
Rendering users/show.html.erb
が描画されている。
Started POST "/relationships" for 222.229.53.237 at 2017-08-27 12:31:59 +0000 Cannot render console from 222.229.53.237! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by RelationshipsController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"a9o2UgH0P0jYefj9EJk/xgQ3vBc54IOBKuqO/OCWIYH6yKHPGhW7I2lDLzcmdTgxwgH7pq4sfEZXbm5RweEJIA==", "followed_id"=>"2", "commit"=>"Follow"} User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] (0.1ms) begin transaction User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] SQL (1.8ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 2], ["created_at", 2017-08-27 12:31:59 UTC], ["updated_at", 2017-08-27 12:31:59 UTC]] (10.6ms) commit transaction Redirected to https://rails-tutorial-yokoyan.c9users.io/users/2 Completed 302 Found in 75ms (ActiveRecord: 15.2ms) Started GET "/users/2" for 222.229.53.237 at 2017-08-27 12:32:00 +0000 Cannot render console from 222.229.53.237! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by UsersController#show as HTML Parameters: {"id"=>"2"} User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] Rendering users/show.html.erb within layouts/application (0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? [["follower_id", 2]] (0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 2]] Rendered shared/_stats.html.erb (9.8ms) User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] User Exists (0.3ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 1], ["id", 2], ["LIMIT", 1]] Relationship Load (0.4ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ? [["follower_id", 1], ["followed_id", 2], ["LIMIT", 1]] Rendered users/_unfollow.html.erb (5.1ms) Rendered users/_follow_form.html.erb (11.7ms) Micropost Exists (0.2ms) SELECT 1 AS one FROM "microposts" WHERE "microposts"."user_id" = ? LIMIT ? [["user_id", 2], ["LIMIT", 1]] (0.2ms) SELECT COUNT(*) FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 2]] Micropost Load (0.4ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? OFFSET ? [["user_id", 2], ["LIMIT", 30], ["OFFSET", 0]] Rendered collection of microposts/_micropost.html.erb [30 times] (17.4ms) CACHE (0.0ms) SELECT COUNT(*) FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 2]] Rendered users/show.html.erb within layouts/application (62.4ms) Rendered layouts/_rails_default.html.erb (71.1ms) Rendered layouts/_shim.html.erb (0.5ms) Rendered layouts/_header.html.erb (1.3ms) Rendered layouts/_footer.html.erb (0.6ms) Completed 200 OK in 160ms (Views: 141.1ms | ActiveRecord: 3.4ms)
フォロー解除時のログは以下の通り。
Rendering users/show.html.erb
が描画されている。
Started DELETE "/relationships/89" for 222.229.53.237 at 2017-08-27 12:35:21 +0000 Cannot render console from 222.229.53.237! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by RelationshipsController#destroy as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"yORU0Rlu5i+Xb+U+5b40amqm6iCIj7qOZEbZhIKfkOlZ9sNMAo9iRCZVMvTTUjOdrJCtkR9DRUkZwjkpo+i4SA==", "commit"=>"Unfollow", "id"=>"89"} User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] Relationship Load (0.3ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."id" = ? LIMIT ? [["id", 89], ["LIMIT", 1]] User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] Relationship Load (0.4ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ? [["follower_id", 1], ["followed_id", 2], ["LIMIT", 1]] (0.2ms) begin transaction SQL (36.1ms) DELETE FROM "relationships" WHERE "relationships"."id" = ? [["id", 89]] (23.0ms) commit transaction Redirected to https://rails-tutorial-yokoyan.c9users.io/users/2 Completed 302 Found in 75ms (ActiveRecord: 60.6ms) Started GET "/users/2" for 222.229.53.237 at 2017-08-27 12:35:21 +0000 Cannot render console from 222.229.53.237! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by UsersController#show as HTML Parameters: {"id"=>"2"} User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] Rendering users/show.html.erb within layouts/application (0.4ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? [["follower_id", 2]] (0.4ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 2]] Rendered shared/_stats.html.erb (10.6ms) User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] User Exists (0.4ms) SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ? [["follower_id", 1], ["id", 2], ["LIMIT", 1]] Rendered users/_follow.html.erb (4.5ms) Rendered users/_follow_form.html.erb (11.1ms) Micropost Exists (0.4ms) SELECT 1 AS one FROM "microposts" WHERE "microposts"."user_id" = ? LIMIT ? [["user_id", 2], ["LIMIT", 1]] (0.3ms) SELECT COUNT(*) FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 2]] Micropost Load (0.9ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? OFFSET ? [["user_id", 2], ["LIMIT", 30], ["OFFSET", 0]] Rendered collection of microposts/_micropost.html.erb [30 times] (22.3ms) CACHE (0.0ms) SELECT COUNT(*) FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 2]] Rendered users/show.html.erb within layouts/application (107.3ms) Rendered layouts/_rails_default.html.erb (73.7ms) Rendered layouts/_shim.html.erb (0.4ms) Rendered layouts/_header.html.erb (1.5ms) Rendered layouts/_footer.html.erb (0.7ms) Completed 200 OK in 202ms (Views: 190.4ms | ActiveRecord: 3.6ms)
14.2.5 [Follow] ボタン (Ajax編)
本章での学び
フォロー・フォロー解除後に元のプロフィールページにリダイレクトしているが、 Ajaxを使ってリファクタリングを行う。
【view】Ajaxを使ったフォーム
form_for ・・・, remote:trure
でAjaxが使えるようになる。
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 %>
Ajaxを使ったフォロー解除フォーム。
<%= 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は以下の通り。
「JavaScriptは全面に出すべからず」という哲学により、data-remote="true"
という属性が追加されるだけ。
<form class="new_relationship" id="new_relationship" action="/relationships" accept-charset="UTF-8" data-remote="true" method="post">
【controller】RelationshipsコントローラでAjaxリクエストに対応する
コントローラでAjaxリクエストを受け取るために、respond_to
メソッドを追加する。
format.出力形式
で返すフォーマットを定義する。
1つのアクションから複数のフォーマットで返す場合、複数行記述する。
ただし、実行されるのはそのうち1つになる。
つまり、フォーマットがhtmlなら、ユーザプロフィールへリダイレクトして、 フォーマットがjsなら、Ajax処理を行う。
※ビューで変数を扱うために、user
を@user
に変更する。
def create @user = User.find(params[:followed_id]) current_user.follow(@user) # redirect_to 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) # redirect_to user respond_to do |format| format.html { redirect_to @user } format.js end end
【config】jsが無効になっていた時の設定
フォームで:remote => trueを使用した場合のauthenticity_tokenのデフォルトの動作を設定する。
class Application < Rails::Application # 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
【view】JavaScriptと埋め込みRubyを使ってフォローの関係性を作成・削除する
Ajaxリクエストを送信した際に、Railsは自動的にアクションと同じ名前を持つjs.erbファイルを呼び出す。(create.js.erbや、destroy.js.erbなど)
js.erbファイルを新規に作成する。
yokoyan:~/workspace/sample_app (following-users) $ touch app/views/relationships/create.js.erb yokoyan:~/workspace/sample_app (following-users) $ touch app/views/relationships/destroy.js.erb
以下の仕様で中身を実装する。
- id=follow_formのHTLM要素を、
render('users/unfollow')
で更新する。 - id=followersのHTML要素を、
@user.followers.count
の結果で更新する。
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>"); $("#followers").html('<%= @user.followers.count %>');
以下の仕様で中身を実装する。
- id=follow_formのHTML要素を、'render(‘users/follow’)‘で更新する。
- id=followersのHTML要素を、'@user.followers.count'の結果で更新する。
$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>"); $("#followers").html('<%= @user.followers.count %>');
演習1
ブラウザから /users/2 にアクセスし、うまく動いているかどうか確認してみましょう。
/users/2 にアクセスする。
フォローする。
フォロー解除する。
演習2
先ほどの演習で確認が終わったら、Railsサーバーのログを閲覧し、フォロー/フォロー解除を実行した直後のテンプレートがどうなっているか確認してみましょう。
フォローした時。
Processing by RelationshipsController#create as JS
が動いている。
Started POST "/relationships" for 222.229.53.237 at 2017-08-27 22:04:17 +0000 Cannot render console from 222.229.53.237! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by RelationshipsController#create as JS Parameters: {"utf8"=>"✓", "followed_id"=>"2", "commit"=>"Follow"} User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] (0.1ms) begin transaction CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] SQL (0.4ms) INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["follower_id", 1], ["followed_id", 2], ["created_at", 2017-08-27 22:04:17 UTC], ["updated_at", 2017-08-27 22:04:17 UTC]] (14.1ms) commit transaction Rendering relationships/create.js.erb Relationship Load (0.3ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ? [["follower_id", 1], ["followed_id", 2], ["LIMIT", 1]] Rendered users/_unfollow.html.erb (3.0ms) (0.2ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 2]] Rendered relationships/create.js.erb (7.1ms) Completed 200 OK in 39ms (Views: 9.9ms | ActiveRecord: 15.9ms)
フォロー解除した時。
Processing by RelationshipsController#destroy as JS
が動いている。
Started DELETE "/relationships/90" for 222.229.53.237 at 2017-08-27 22:06:08 +0000 Cannot render console from 222.229.53.237! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by RelationshipsController#destroy as JS Parameters: {"utf8"=>"✓", "commit"=>"Unfollow", "id"=>"90"} User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] Relationship Load (0.1ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."id" = ? LIMIT ? [["id", 90], ["LIMIT", 1]] User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]] Relationship Load (0.2ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ? [["follower_id", 1], ["followed_id", 2], ["LIMIT", 1]] (0.1ms) begin transaction SQL (0.4ms) DELETE FROM "relationships" WHERE "relationships"."id" = ? [["id", 90]] (18.4ms) commit transaction Rendering relationships/destroy.js.erb Rendered users/_follow.html.erb (2.3ms) (0.4ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 2]] Rendered relationships/destroy.js.erb (6.8ms) Completed 200 OK in 66ms (Views: 9.4ms | ActiveRecord: 20.6ms)
14.2.6 フォローをテストする
本章での学び
通常のリクエスト版とAjax版のテストコードを作成する。
【test】フォロー・フォロー解除ボタンをテストする
Ajaxのテストでは、xhr :true
オプションを使用する。
XmlHttpRequestの頭文字。
should follow a user the standard way
- @user.following.countが1増えないこと
- postリクエストを送信(relationship_path)
- params{ followed_id: @other.id}
- postリクエストを送信(relationship_path)
- @user.following.countが1増えないこと
should follow a user with Ajax
- @user.following.countが1増えないこと
- Ajaxでpostリクエストを送信(relationship_path, xhr: true
- params{ followed_id: @other.id}
- Ajaxでpostリクエストを送信(relationship_path, xhr: true
- @user.following.countが1増えないこと
should unfollow a user the standard way
- @userが@otherをフォローする
- @user.active_relationshipsから、@other.idを検索する
- @user.following.countが-1されないこと
- deleteリクエストを送信(relationship_path)
should unfollow a user with Ajax
- @userが@otherをフォローする
- @user.active_relationshipsから、@other.idを検索する
- @user.following.countが-1されないこと
- Ajaxでdeleteリクエストを送信(relationship_path)
上記を踏まえて実装する。
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 assert_difference '@user.following.count', 1 do post relationships_path, xhr: true, params: { followed_id: @other.id } 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
テスト実施
テスト結果がgreenになることを確認
yokoyan:~/workspace/sample_app (following-users) $ rails test Running via Spring preloader in process 1957 Started with run options --seed 24789 DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only the following keyword arguments in future Rails versions: params, headers, env, xhr, as Examples: get '/profile', params: { id: 1 }, headers: { 'X-Extra-Header' => '123' }, env: { 'action_dispatch.custom' => 'custom' }, xhr: true, as: :json (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27) 74/74: [====================================] 100% Time: 00:00:05, Time: 00:00:05 Finished in 5.83747s 74 tests, 297 assertions, 0 failures, 0 errors, 0 skips
演習1
リスト 14.36のrespond_toブロック内の各行を順にコメントアウトしていき、テストが正しくエラーを検知できるかどうか確認してみましょう。実際、どのテストケースが落ちたでしょうか?
フォロー1 通常tリクエストをコメントアウト。
respond_to do |format| # format.html { redirect_to @user } format.js end
post relationships_path, params: { followed_id: @other.id }
で落ちる。
ERROR["test_should_follow_a_user_the_standard_way", FollowingTest, 4.909926524851471] test_should_follow_a_user_the_standard_way#FollowingTest (4.91s) ActionController::UnknownFormat: ActionController::UnknownFormat: ActionController::UnknownFormat app/controllers/relationships_controller.rb:8:in `create' test/integration/following_test.rb:34:in `block (2 levels) in <class:FollowingTest>' test/integration/following_test.rb:33:in `block in <class:FollowingTest>'
フォロー2 Ajaxリクエストをコメントアウト。
respond_to do |format| format.html { redirect_to @user } # format.js end
この場合はテストはエラーにならず、テストは成功する。
yokoyan:~/workspace/sample_app (following-users) $ rails test Running via Spring preloader in process 1227 Started with run options --seed 12214 DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only ] 40% Time: 00:00:03, ETA: 00:00:05 the following keyword arguments in future Rails versions: params, headers, env, xhr, as Examples: get '/profile', params: { id: 1 }, headers: { 'X-Extra-Header' => '123' }, env: { 'action_dispatch.custom' => 'custom' }, xhr: true, as: :json (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27) 74/74: [=================================================================================================================================] 100% Time: 00:00:06, Time: 00:00:06 Finished in 6.70043s
フォロー解除1 通常のリクエストをコメントアウト。
respond_to do |format| # format.html { redirect_to @user } format.js end
delete relationship_path(relationship)
で落ちる。
ERROR["test_should_unfollow_a_user_the_standard_way", FollowingTest, 4.035211271606386] test_should_unfollow_a_user_the_standard_way#FollowingTest (4.04s) ActionController::UnknownFormat: ActionController::UnknownFormat: ActionController::UnknownFormat app/controllers/relationships_controller.rb:18:in `destroy' test/integration/following_test.rb:48:in `block (2 levels) in <class:FollowingTest>' test/integration/following_test.rb:47:in `block in <class:FollowingTest>'
フォロー解除2 Ajaxリクエストをコメントアウト。
respond_to do |format| format.html { redirect_to @user } # format.js end
フォロー時と同じく、成功する。 おそらく、Ajax無効時にtrueにする設定が入っているため??
yokoyan:~/workspace/sample_app (following-users) $ rails test Running via Spring preloader in process 1602 Started with run options --seed 3119 DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only================================= ] 87% Time: 00:00:04, ETA: 00:00:01 the following keyword arguments in future Rails versions: params, headers, env, xhr, as Examples: get '/profile', params: { id: 1 }, headers: { 'X-Extra-Header' => '123' }, env: { 'action_dispatch.custom' => 'custom' }, xhr: true, as: :json (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27) 74/74: [=================================================================================================================================] 100% Time: 00:00:05, Time: 00:00:05 Finished in 5.53267s 74 tests, 297 assertions, 0 failures, 0 errors, 0 skips
この状態で、Ajax無効時の設定をコメントアウトして、テストしてみる。
# 認証トークンをremoteフォームに埋め込む # config.action_view.embed_authenticity_token_in_remote_forms = true
それでもテストが通る。関係無かったようだ・・・。
yokoyan:~/workspace/sample_app (following-users) $ rails test Running via Spring preloader in process 1798 Started with run options --seed 55359 DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only ] 6% Time: 00:00:02, ETA: 00:00:31 the following keyword arguments in future Rails versions: params, headers, env, xhr, as Examples: get '/profile', params: { id: 1 }, headers: { 'X-Extra-Header' => '123' }, env: { 'action_dispatch.custom' => 'custom' }, xhr: true, as: :json (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27) 74/74: [=================================================================================================================================] 100% Time: 00:00:05, Time: 00:00:05 Finished in 5.70124s 74 tests, 297 assertions, 0 failures, 0 errors, 0 skips
演習2
リスト 14.40のxhr: trueがある行のうち、片方のみを削除するとどういった結果になるでしょうか? このとき発生する問題の原因と、なぜ先ほどの演習で確認したテストがこの問題を検知できたのか考えてみてください。
ポスト時のxhr: true
を削除する。
test "should follow a user with Ajax" do assert_difference '@user.following.count', 1 do # post relationships_path, xhr: true, params: { followed_id: @other.id } post relationships_path, params: { followed_id: @other.id } end end
この場合でもエラーは発生しない。
yokoyan:~/workspace/sample_app (following-users) $ rails test Running via Spring preloader in process 2013 Started with run options --seed 52161 DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept only========================== ] 82% Time: 00:00:03, ETA: 00:00:01 the following keyword arguments in future Rails versions: params, headers, env, xhr, as Examples: get '/profile', params: { id: 1 }, headers: { 'X-Extra-Header' => '123' }, env: { 'action_dispatch.custom' => 'custom' }, xhr: true, as: :json (called from block (2 levels) in <class:MicropostsInterfaceTest> at /home/ubuntu/workspace/sample_app/test/integration/microposts_interface_test.rb:27) 74/74: [=================================================================================================================================] 100% Time: 00:00:05, Time: 00:00:05 Finished in 5.16438s 74 tests, 297 assertions, 0 failures, 0 errors, 0 skips
おわりに
フォロー、フォロー解除機能の実装が完了しました。 RailsだとAjaxの処理も簡単に書けますね。 最後の演習で、ajax部分をコメントアウトしてもテストがgreenになる理由がわかっていません。 もやもやするな・・・。