Railsチュートリアル10章について大事だと思ったことをメモっときます。
テストコードはRspecを使っています。
フレンドリーフォワーティング
ログイン前にそのページに行こうとしていたら、ログインした後は行こうとしたページになっていること。
Sessionを使ってurlを保存してあげる。
ログインする前にurlをsessionに保存(ログイン前、ページに行こうとした時)
ログインした後にそのurlにリダイレクト。行こうとしていたページがない場合はそのまま指定されたページ(デフォルト)。デフォルトのページは引数で渡されている。
sessions_helper.rb
# 記憶したURL(もしくはデフォルト値)にリダイレクト
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
# アクセスしようとしたURLを覚えておく こっちが先に行われる!!
def store_location
session[:forwarding_url] = request.original_url if request.get?
end
url保存はusersコントローラーのbeforeアクション
def logged_in_user
unless logged_in?
store_location ここ!!
flash[:danger] = "Please log in."
redirect_to login_url
end
end
保存されたurlに行くのはsessionsコントローラーのcreateアクション(ログインする時)
def create
user = User.find_by(email: params[:session][:email].downcase)
if user&.authenticate(params[:session][:password])
# 元々はこう user && user.authenticate(params[:session][:password])
# このauthenticateはhas_secure_passwordが持つ特性なので定義しなくても使える。
# 一方remember_tokenとかはhas_secure_passwordとは関係ないのでauthenticateメソッドを定義する必要がある
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_back_or user ここ!!
defaultがuserにあたる。 つまりはuser_path showにあたる。
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
---------------------------------------------------------------------------------------
pagination ページ分割
gemを入れる。
gem 'will_paginate', '3.1.8'
gem 'bootstrap-will_paginate', '1.0.0' これはbootstrapのcssに対応するようにしている
ページ分割にはwill_paginateを使う。
usersビューのコードの中から@usersを自動で見つけていき他のページにアクセスするページネーションリンクを作成。
<%= will_paginate %> ここ!!
これを書くだけでページネーションリンク
1 2 3 NEXT のようなリンクを作れる。デフォルトで1ページに30こ
<ul class="users">
<% @users.each do |user| %>
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
</li>
<% end %>
</ul>
<%= will_paginate %> ここ!!
2つ使っているのは上と下にそれぞれ置きたいから。1つでも問題ない。
userコントローラー
def index
@users = User.paginate(page: params[:page])
end
リファクタリング 上の記述をリファクタリングします。
users/index.html.erb
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>
<ul class="users">
<%= render @users %> ここ!!
こう書くことでまず_user.html.erbを呼び出す。
そして@usersに含まれているやつを1つ1つ呼び出す。
つまりeachなしでもそれぞれ呼び出すことができる。
</ul>
<%= will_paginate %>
users/_user.html.erb
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</li>
---------------------------------------------------------------------------------------
admin 管理ユーザーになる
まずはmigration
def change
add_column :users, :admin, :boolean, default: false
# これは無くてもadminがnilになるのでいいらしい。
# ただ見た人にわかりやすくしている
end
end
このmigrationでadminかどうかを調べるadmin?メソッドも使えるようになる
---------------------------------------------------------------------------------------
ユーザーを一気に大量に作る方法
db/seedsに書く。
# メインのサンプルユーザーを1人作成する
User.create!(name: "Example User",
email: "example@railstutorial.org",
password: "foobar",
password_confirmation: "foobar",
admin: true)
# 追加のユーザーをまとめて生成する ここ!!
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)
end
ちなみにテストコードでユーザーを大量に追加する方法は
RSpec.describe "Index 大量のuser", type: :system do
before do
@user = FactoryBot.create(:user)
@other_user= FactoryBot.create(:user)
@users=FactoryBot.create_list(:user, 100) ここ!! create_listでユーザーを大量に作成
今回だと100
@users=は別に必要ないと思う。
end
---------------------------------------------------------------------------------------
テストコード
テストコードはsystem,requestそれぞれに書いたので順番通りになってないです。
RSpec.describe "Users", type: :system do
before do
@user = FactoryBot.create(:user)
@other_user= FactoryBot.create(:user)
end
it "unsuccessful edit" do
log_in(@user)
visit edit_user_path(@user)
fill_in 'Name', with:"AAA"
fill_in 'Email', with:"aaa@111.com"
fill_in 'Password', with: "abcdef"
fill_in 'Password confirmation', with: "fbaxfe"
find('input[name="commit"]').click
expect(has_css?('.alert-danger')).to be_truthy
end
it "successful edit" do
log_in(@user)
visit edit_user_path(@user)
fill_in 'Name', with:"AAA"
fill_in 'Email', with:"aaa@111.com"
fill_in 'Password', with: "abcdef"
fill_in 'Password confirmation', with: "abcdef"
find('input[name="commit"]').click
expect(current_path).to eq(user_path(@user))
expect(page).to have_content'Profile updated'
@user.reload
expect(@user.name).to eq("AAA")
expect(@user.email).to eq("aaa@111.com")
end
it "should redirect edit when not logged in" do
visit edit_user_path(@user)
expect(current_path).to eq(login_path)
end
# ログインしていないupdateのテストはrequest specで書く(下)
it 'should redirect edit when logged in as wrong user' do
log_in(@user)
visit edit_user_path(@other_user)
expect(current_path).to eq(root_path)
end
it "successful edit with friendly forwarding" do
visit edit_user_path(@user)
log_in(@user)
expect(current_path).to eq(edit_user_path(@user))
fill_in 'Name', with:"BBB"
fill_in 'Email', with:"abc@111.com"
find('input[name="commit"]').click
expect(current_path).to eq(user_path(@user))
expect(page).to have_content'Profile updated'
end
it "should redirect index when not logged in" do
visit users_path
expect(current_path).to eq(login_path)
end
end
RSpec.describe "Index 大量のuser", type: :system do
before do
@user = FactoryBot.create(:user)
@other_user= FactoryBot.create(:user)
@users=FactoryBot.create_list(:user, 100)
end
it 'index including pagination' do
log_in(@user)
visit users_path
User.paginate(page: 1).each do |user| ページネーションはこうやってやる
expect(page).to have_link href: user_path(user)
expect(page).to have_content(user.name)
end do~endで書かれてる
end
end
RSpec.describe "Users", type: :request do
before do
@user = FactoryBot.create(:user)
@other_user= FactoryBot.create(:user)
end
it 'should redirect update when not logged in' do
patch user_path(@user), params: { user: { name: @user.name,
email: @user.email } }
expect(response).to redirect_to login_path
end
it "should redirect update when logged in as wrong user" do
log_in_as(@other_user)
patch user_path(@user), params: { user: { name: @user.name,
email: @user.email } }
expect(response).to redirect_to root_path
end
it 'should not allow the admin attribute to be edited via the web' do
log_in_as(@other_user)
patch user_path(@other_user), params: {
user: { password: "password",
password_confirmation: "password",
admin: true } }
expect(@other_user.admin?).to be_falsy
end
end
RSpec.describe "Admin", type: :request do
before do
@user = FactoryBot.create(:user,:admin)
@other_user= FactoryBot.create(:user)
end
it "should redirect destroy when not logged in" do
expect do
delete user_path(@other_user)
end.to change { User.count }.by(0)
expect(response).to redirect_to login_url
end
it 'should redirect destroy when logged in as a non-admin' do
log_in_as(@other_user)
expect do 下に説明書いた
delete user_path(@user)
end.to change { User.count }.by(0)
expect(response).to redirect_to root_path
end
it "index as admin including pagination and delete links" do
log_in_as(@user)
expect do ここ!! request specでも expect do~ end.to~は使える。
今回はdelete_pathに行くとUserのカウントが1減るよう書いてる
delete user_path(@other_user)
end.to change { User.count }.by(-1)
expect(response).to redirect_to users_path
end
end
以上で10章終わりです!