はじめに
最近、プロジェクト管理業務が業務の大半を占めており、
プログラムを書く機会がなかなかありません。
このままだとプログラムがまったく書けない人になってしまう危機感(迫り来る35歳定年説)と、
新しいことに挑戦したいという思いから、
Ruby on Rails チュートリアル実例を使ってRailsを学ぼう 第4版を学習中です。
業務で使うのはもっぱらJavaなのですが、Rails楽しいですね。
これまでEvernoteに記録していましたが、ソースコードの貼付けに限界を感じたため、
Qiitaで自分が学習した結果をアウトプットしていきます。
個人の解答例なので、誤りがあればご指摘ください。
動作環境
- 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
-
should unfollow a user with Ajax
上記を踏まえて実装する。
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になる理由がわかっていません。
もやもやするな・・・。