【devise】user情報の編集がやっとできた話〜もうパスワードなんていらない〜

### なんの話？
deviseを導入してuser情報を管理していたが、編集をしようと思ったら一筋縄でいかなかった話です。


### 自分がやったこと

- usersコントローラーにeditアクション、updateアクションをそれぞれ記述
- viewファイルを変更



```users_controller.rb
  def edit
    @user = User.find(params[:id])
  end

  def update
    @user = User.find(params[:id])
    if @user.update(user_params)
      redirect_to root_path, dark: 'ユーザー情報の編集ができました'
    else
      flash[:danger] = 'ユーザー情報の編集ができませんでした'
      render :edit
    end
  end
```

すごーく単純な作業だからすぐできると思っていた。
その結果。
![cb06073e6877eba08feabd88aceb6d99.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/887559/5646dd9e-05f5-c8bb-f3fa-c48ece2b042c.png)

色々と調べてみたところ、deviseのコントローラーを新しく追加すればいけるようだったのでやってみる。

### １・まずはコントローラーを作成

```console
% rails g devise:controllers users

⬇️⬇️⬇️実行結果⬇️⬇️⬇️

Running via Spring preloader in process 7028
      create  app/controllers/users/confirmations_controller.rb
      create  app/controllers/users/passwords_controller.rb
      create  app/controllers/users/registrations_controller.rb
      create  app/controllers/users/sessions_controller.rb
      create  app/controllers/users/unlocks_controller.rb
      create  app/controllers/users/omniauth_callbacks_controller.rb
===============================================================================

Some setup you must do manually if you haven't yet:

  Ensure you have overridden routes for generated controllers in your routes.rb.
  For example:

    Rails.application.routes.draw do
      devise_for :users, controllers: {
        sessions: 'users/sessions'
      }
    end

===============================================================================

```

### ２・作成した`registrations_controller`に記述


```registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
 before_action :configure_account_update_params, only: [:update]

  protected

  def update_resource(resource, params)
    resource.update_without_password(params)
  end

  def configure_account_update_params
    devise_parameter_sanitizer.permit(:account_update,
      keys: [:nickname, :gender_id, :profile, :genre_id])⬅️keys:の中身はカラム名に
  end
end
```
`devise_parameter_sanitizer.permit(:account_update,
　　keys: [:nickname, :gender_id, :profile, :genre_id])`
このkeys:の部分には自分のDBのカラム名を設定。
email、password、password_confirmationは書かない。
アカウントをupdate(編集)する時に、どのデータの受け取りを許可させるか指定する。

### ３・ルーティングに記述

```routes.rb
Rails.application.routes.draw do

  devise_for :users, controllers: {
    registrations: 'users/registrations'
  }

〜以下略〜
```
`devise_for :users`の部分に追加。


### ４・userモデルに記述
```user.rb
class User < ApplicationRecord
 
〜中略〜

  PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze
  #on: :create ➡️ createアクションの時にだけvalidatesをかける
  validates :password, format: { with: PASSWORD_REGEX }, on: :create
  validates :password, confirmation: true, on: :create

end
```
passwordとpassword_confirmationの部分に`on: :create`を追記。
createアクションを経由するときだけにバリデーションをかけるようにする。
つまりupdateアクションの時にpasswordを必要としない(バリデーションをかけない)ように変更。


### ５・viewファイルを変更
入力フォームを空にして送信すると突然見たことがないviewファイルが読み込まれる。
`app>　views>　devise>　registration>　edit.html.erb`
どうやらこのファイルを読み込んでいるようなのでviewファイルを変更する。
`app>　views>　users>　edit.html.erb`
ここに作ったものをそのままコピペ。
この時にemail、password、password_confirmationの項目は表示させないようにするため削除しておく。

### ６・flashメッセージの設定


```users.controller.rb
  def update
    @user = User.find(params[:id])
    if @user.update(user_params)
      redirect_to root_path, dark: 'ユーザー情報の編集ができました'
    else
      flash[:danger] = 'ユーザー情報の編集ができませんでした'
      render :edit
    end
  end
```

usersコントローラーを経由しなくなりflashメッセージが出なくなってしまったので、`registrations_controller`に記述する。

```registrations_controller.rb
〜略〜

  # PUT /resource
  # def update
  #   super
  # end

〜略〜
```


updateアクションはデフォルトでこうなっていた。
superで呼び出している内容は



```ruby
  def update
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
    prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)

    resource_updated = update_resource(resource, account_update_params)
    yield resource if block_given?
    if resource_updated
      set_flash_message_for_update(resource, prev_unconfirmed_email)
      bypass_sign_in resource, scope: resource_name if sign_in_after_change_password?

      respond_with resource, location: after_update_path_for(resource)
    else
      clean_up_passwords resource
      set_minimum_password_length
      respond_with resource
    end
  end
```



これを書き換えたらこうなった。


```registrations_controller.rb
  # PUT /resource
  def update
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
    prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
    resource_updated = update_resource(resource, account_update_params)
    yield resource if block_given?
    if resource_updated
      redirect_to @user, dark: 'ユーザー情報の編集ができました'
      bypass_sign_in resource, scope: resource_name if sign_in_after_change_password?
    else
      flash[:danger] = 'ユーザー情報の編集ができませんでした'
      clean_up_passwords resource
      set_minimum_password_length
      respond_with resource
    end
  end
```

elseの部分は加えるだけで良かったがifのほうはそのまま加えると
`AbstractController::DoubleRenderError in Users::RegistrationsController#update`
というエラーが出て、ダブルレンダリングしてるよと怒られてしまった。
該当の部分を削除して完成し、ようやくやりたいことが実現できた。
修正部分などありましたらご指摘下さい。


### 参考にさせて頂きました
@coxcoa様
[Deviseでユーザー情報の編集
](https://qiita.com/coxcoa/items/fb74ee10f1a99b8ad622)

[heartcombo/devise][GitHub](https://github.com/heartcombo/devise/blob/1bb5fcbbecfb9452e194341e030fdc1d364f6ee4/app/controllers/devise/registrations_controller.rb#L46)


ありがとうございました。