railstutorial.jp 9章メモ
- テーマ
- ユーザーの更新・表示・削除
- 認証と認可
- フレンドリーフォワーディング
- ページング機能
webブラウザからPATHリクエストができないので、POSTのhiddenパラメータ_method
にpatch
が入っている
<form accept-charset="UTF-8" action="/users/2" class="edit_user" id="edit_user_2" method="post"> <input name="_method" type="hidden" value="patch" />
Active Recordのnew_record?メソッド
Railsは、form_for(@user)を使用してフォームを構成すると、
@user.new_record?
がtrueのときにはPOSTを、falseのときにはPATCHを使用します。
spec/support/utilities.rb
sign_inメソッド
no_capyabara: trueオプション
変更を保存した後に@user.reloadメソッドを使ってDBから再読込した値とチェック
specify { expect(user.reload.name).to eq new_name } specify { expect(user.reload.email).to eq new_email }
utilities.rbにsign_inメソッド足してからテストが通らなくなった。謎。
Failure/Error: sign_in user NoMethodError:
user_pages_spec.rbとauthentication_pages_spec.rbに以下を追記したらテストが通った。謎。
include 'spec/support/utilities.rb'
Capybaraではeditのアクションはテストできても、updateのアクションはテストできない。
なので、Capybaraでpatchリクエスト(更新)を直接実行しテストする。(get, post, deleteなども同様にメソッドがある。)
response
オブジェクトが返ってくる。
describe "submitting to the update action" do before { patch user_path(user) } specify { expect(response).to redirect_to(signin_path) } end
before_action
before_action :signed_in_user, only: [:edit, :update]
redirect_toの引数にハッシュを渡すとflashの設定になる。
flashは:notice、:success、:errorの3種類が設定できる。
redirect_to signin_url, notice: "Please sign in." unless signed_in? # 上と同じ unless signed_in? flash[:notice] = "Please sign in." redirect_to signin_url end
FactoryGirlで生成したユーザ情報を一部更新するには第2引数にハッシュ
let(:user) { FactoryGirl.create(:user) } let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") }
フレンドリーフォワーディング
ログイン前に見ようとしていたページへログイン後に遷移させる機能
request
オブジェクト
def redirect_back_or(default) redirect_to(session[:return_to] || default) # セッションから削除 session.delete(:return_to) end def store_location # リクエストのURLをrequestオブジェクトから取得してセッションへ保存 session[:return_to] = request.url end
Railsで予約されているオブジェクト名がいままでいろいろでてきた
- params
- request
- response
- flash
- cookies
- session
gravatar_forでエラーが出る。。。
1) User pages index Failure/Error: visit users_path ActionView::Template::Error: wrong number of arguments (2 for 1) # ./app/helpers/users_helper.rb:4:in `gravatar_for'
UsersHelper::gravatar_forの引数にデフォルトの値を与えるように修正して動いた。
module UsersHelper # 与えられたユーザーのGravatar (http://gravatar.com/) を返す。 # def gravatar_for(user) # gravatar_id = Digest::MD5::hexdigest(user.email.downcase) # gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}" # image_tag(gravatar_url, alt: user.name, class: "gravatar") # end def gravatar_for(user, options={size:50}) gravatar_id = Digest::MD5::hexdigest(user.email.downcase) size = options[:size] gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" image_tag(gravatar_url, alt: user.name, class: "gravatar") end end
Faker gemでダミーのユーザデータをrakeタスクで生成する
gem 'faker'
lib/tasks/sample_data.rake
以下コマンドでもgenerateできた
$ rails g task sample_data
db:populateタスク(:dbはただの名前空間)
task populate: :environment do
はRakeタスクがUserモデルなどのローカルのRails環境にアクセスできるようにする
User.create!は失敗したときにfalseではなく例外を発生させる
99.times do |n| end
Faker::Name.nameでダミーデータを作成する
$ rake db:reset
$ rake db:populate
$ rake test:prepare
タスク実行に成功したが、データは古いままだった。
いったんrails sをおとしてからもう一度ログインし直したら、データが新しくなった。謎。
ページネーションの実装
gem 'will_paginate' gem 'bootstrap-will_paginate'
FactoryGirlのsequenceメソッドで連続データを作る
factory :user do sequence(:name) { |n| "Person #{n}" } sequence(:email) { |n| "person_#{n}@example.com"}
timesのブロック引数にFactoryGirl.createが与えられると、sequesceでインクリメントされる
before(:all) { 30.times { FactoryGirl.create(:user) } } after(:all) { User.delete_all }
before(:each)
ブロックのテストに対して1回づつ?
before(:all)
ブロックにあるすべてのテストの前に一括実行
after(:all)
ブロックにある全てのテストの後に一括実行
will_paginate
usersビューのコードの中から@usersオブジェクトを自動的に見つけ出し、 それから他のページにアクセスするためのページネーションリンクを作成
<%= will_paginate %>
rails consoleでpaginateメソッドをやってみると、デフォルト30件ずつ取得することがわかる。
nilの場合は最初の1ページ目
> User.paginate(page: 1) User Load (2.1ms) SELECT "users".* FROM "users" LIMIT 30 OFFSET 0 > User.paginate(page: 2) User Load (0.5ms) SELECT "users".* FROM "users" LIMIT 30 OFFSET 30 > User.paginate(page: nil) User Load (0.4ms) SELECT "users".* FROM "users" LIMIT 30 OFFSET 0
def index @users = User.paginate(page: params[:page]) end
パーシャルにUserクラスのuser変数を渡すと、_user.html.erb
を探しに行く
見ているのは変数名ではなく、変数のクラス名。
<ul class="users"> <% @users.each do |user| %> <%= render user %> <% end %> </ul>
パーシャルにオブジェクトのコレクションを渡すと、自動でコレクションの要素分、クラス名に対応するパーシャル_user.html.erb
をレンダリングする
each文を省略できる
<ul class="users"> <%= render @users %> </ul>
admin権限のテスト
toggle!
引数のadmin属性を反転させてDBに保存
@user.toggle!(:admin)
be_admin
admin?メソッドを実行し、trueが返ること
it { should be_admin }
$ rails generate migration add_admin_to_users admin:boolean
default: false
でデフォルト値を与えている
def change add_column :users, :admin, :boolean, default: false end
user番号17を管理者へ変更するPATCHリクエスト
Strong Parametersによってパラメータ名adminを許可していないので防ぐことができる
patch /users/17?admin=1
:userのadminだけ上書きした:adminを定義
# FactoryGirl.create(:admin)が使える factory :admin do admin true end
match: :first
expect do # Capybaraが最初に見つけたdeleteのリンクをクリック click_link('delete', match: :first) end.to change(User, :count).by(-1)
User.find(params[:id]).destroy
herokuの本番データのリセット
$ git push heroku $ heroku pg:reset DATABASE $ heroku run rake db:migrate $ heroku run rake db:populate