Help us understand the problem. What is going on with this article?

Rails チュートリアル 第10章 まとめ

全体像の把握

editアクションで、編集フォームを作成。updateアクションで編集部分を保存。
edit,update等のアクションに縛りを設けるため、beforeアクションを利用。
indexアクションで、ユーザー一覧ページを作成し、その際、ページネーションを利用。
destroyアクションはadmin権限で使用可能にした。

editアクション -User編集ページ

GET /users/:id/edit

def edit
  @user = User.find(params[:id]) # GETリクエストのidを使い、ユーザー情報を取得
end

ViewはUserコントローラのnewとほぼ同じ
コード上の違いがないのに、newはcreateアクションを発行し、editはupdateアクションを発行する?

newはインスタンスを新しく作っていて型だけだが、editはfindを使ってデータを取得しているのでidが入っている。それで判断している

privateメソッド
このメソッド以下のメソッドは全てprivateになる
このコントローラ内でだけ使って外からのアクセスで書き換えられたくないという時に使う
paramsを精査するとかいう場所で使う

updateアクション -編集内容をDBに保存

def update
   @user = User.find(params[:id]) # 入力された内容を取得し、@userに代入
    if @user.update_attributes(user_params) #引数には更新したいカラム名
      flash[:success] = "Profile updated"
      redirect_to @user
   else
      render "edit"
   end
end

updateアクションのテストでpasswordを空にして編集できるようにするallow_nilが機能しなかった

認可

ログインしていない人がページにアクセスできてしまう問題を解決

1 権限のないユーザーがアクセスしたらログインするように促す

beforeフィルター
何かを実行する前に、指定した機能を追加してください
→ 編集ページに行く前に、ログインしてください
before_action :logged_in_user, only: [:edit, :update]

def logged_in_user
      unless logged_in? #もしログインしていなかったら
        flash[:danger] = "Please log in." #コメント表示
        redirect_to login_url # loginページに
      end
    end

beforeのフォローとして、testにログインしたことを確認するコードを書く

beforeが機能しているかコメントアウトしてチェック
beforeが機能していないとErrorとなるようにテストを書く

users_controller_test
  test "should redirect edit when not logged in" do
    get edit_user_path(@user) # loginせずにページにアクセス
    assert_not flash.empty? # flashが出る
    assert_redirected_to login_url # loginページへ
  end

  test "should redirect update when not logged in" do
    patch user_path(@user), params: { user: { name: @user.name,
                                              email: @user.email } }
# patchリクエストがブラウザ経由ではなく直接送りつけられた場合
    assert_not flash.empty?
    assert_redirected_to login_url
  end

2 自分のプロフィールページだけを編集できるように

TDDで
テストは

users_controller_test
 test "should redirect edit when logged in as wrong user" do
    log_in_as(@other_user) #  ログイン
    get edit_user_path(@user) # 違う人の編集ページに行こうとする
    assert flash.empty? # flash
    assert_redirected_to root_url # topページに
  end
 # ...(略)

flashが逆であるのが真だとErrorが出た
users_ymlはmemberを左にくっつけないとErrorになる

正しいユーザーかどうかチェックする機能を実装

users_controller
def correct_user
      # GET /users/:id/edit
      # PATCH /users/:id どちらかから送られてきたリクエストをparamsに格納
      @user = User.find(params[:id])
      # currentは第8章で実装、今ログインしているユーザーを示す
      redirect_to(root_url) unless @user == current_user
end

indexアクション - User一覧ページ

TDD
ログインしてないと行けないというテスト

user_controller_test
test "should redirect index when not logged in" do
    get users_path # users_pathは詳細ページを表す、詳細ページへ移動
    assert_redirected_to login_url # beforeアクションでloginページへ
end
user_controller
def index
  @users = User.all # @usersに注意、User情報を全て取得
end

View

<% provide(:title, 'All users') %>
<h1>All users</h1>

<ul class="users">
  <% @users.each do |user| %>
    <li>
      <%= gravatar_for user, size: 50 %>
      <%= link_to user.name, user %> # リンクのuserでユーザー詳細に
# どこでやったか
    </li>
  <% end %>
</ul>

サンプルユーザーの生成 Fake

Pagenation
install
Viewではページネーションしたいものを囲む

<%= will_paginate @users %> #引数にUserの集合@usersを渡すことで、Userを対象に指定
:

<%= will_paginate %>

Controllerでは

def index
    @users = User.paginate(page: params[:page]) 
# parameterにpageが格納されるようになるので、それを@usersに保存
# 初期では30ずつ
end

IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "index including pagination" do
    log_in_as(@user) # login
    get users_path # indexページにGETリクエスト
    assert_template 'users/index' # indexページに
    assert_select 'div.pagination' # paginationが機能するか
    User.paginate(page: 1).each do |user| #それぞれのリンクが正しく貼られているか
      assert_select 'a[href=?]', user_path(user), text: user.name
    end
  end

リファクタリング 
renderを使って、UserオブジェクトのPartialを作り、それをrender @userで呼び出す

index.html.erb
    <li>
      <%= gravatar_for user, size: 50 %>
      <%= link_to user.name, user %>
    </li>

この部分は

<%= render user %>
=> app/views/リソース名/モデル名.html.erb
=> app/views/users/_user.html.erb というPartialが呼び出される

とすることができる
今までPartialのrenderはsharedを使っていたが、引数にUserオブジェクトを渡すことができる

さらに、

<%= render @users %>

Userオブジェクトの集合体もrenderの引数として使うことができ、それによりその引数を展開して内部でrenderを呼び出してくれる
つまり

<% @users.each do |user| %>
   <%= render user %>
 <% end %>

この部分と等価になり、省略できる

destroyアクション -削除

管理権限を持った人ならアカウントを削除できる

admin
add_column :users, :admin, :boolean, default: false
の,を忘れず

index.htmlのrender @userが_user.html.erbを呼ぶので

_user.html.erb
<% if current_user.admin? && !current_user?(user) %>
# admin なら以下のリンクが見える
    | <%= link_to "delete", user_path(user), method: :delete,
                                  data: { confirm: "You sure?" } %>
# method:で指定しているのでDELETEリクエストが/users/:idに送られる
# 
<% end %>

を書き加える
すると、index -> delete という流れが作れる

def destroy
# 上でDELETE /user/:idに送られたparameterを使い、Userを探し、destroy
    User.find(params[:id]).destroy
    flash[:success] = "User deleted"
    redirect_to users_url # indexへ
end

controller_testの方のテストでadmin権限を付与していないacherではdeleteできないというテストが機能しない、acherもdeleteできる仕様になってしまっている、原因不明

test "should redirect destroy when logged in as a non-admin" do
    log_in_as(@other_user)
    assert_no_difference 'User.count' do
      delete user_path(@user)
    end
    assert_redirected_to root_url
end

IntegrationTest
indexのテストに追加している

# adminであれば、deleteが見えているはずという意味。なんでifじゃない?
 unless user == @admin
        assert_select 'a[href=?]', user_path(user), text: 'delete'
 end
...
 delete user_path(@non_admin) #adminなので、non_adminを削除する

...
 test "index as non-admin" do
    log_in_as(@non_admin) #loginしてみる
    get users_path # indexページに行く
    assert_select 'a', text: 'delete', count: 0 #しかし、削除されているので行けない
 end

Integrationtestでもfailureが出た
このテストで確認していることは、三つある
1ログインしていないユーザーならdeleteリクエストを送ってもユーザー数に変化なし
2admin権限を付与していないユーザーならdeleteリクエストを送ってもユーザー数に変化なし
3Integrationでは、admin権限のあるユーザーでログインし、deleteを送ると、ユーザー数が変化する

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした