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

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


#editアクション　-User編集ページ
GET /users/:id/edit

```rb:
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に保存

```ruby:
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]`

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

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

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

```ruby: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で
テストは

```ruby: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になる

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

```ruby: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
ログインしてないと行けないというテスト

```ruby: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
```

```rb: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では

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

IntegrationTest

```ruby:
  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 という流れが作れる

```ruby:
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のテストに追加している

```ruby:
# 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を送ると、ユーザー数が変化する