【Rails】ユーザー編集時だけパスワードのバリデーションを無効にする（devise）

# 環境
macOS: Big Sur Ver11.2.2
Rails: 6.0.0
Ruby: 2.6.5

# この記事の目標
devise のデフォルトのユーザー編集画面ではパスワードの変更ができるが、現在のパスワードを入力しないとユーザー情報が編集できないようになっている。

そこで、
①現在のパスワードを入力しなくてもユーザー情報ができるようにする
②ユーザー編集のときだけモデルに設定しているパスワードのバリデーションを解除し、パスワード変更欄が空欄のままでもユーザー編集ができるようにする

の２点を実装することを目標とします。

# 現在のパスワードを入力不要にする
これについては多くの記事が出ています。以下が参考になります。

[参考①](https://easyramble.com/user-account-update-without-password-on-devise.html)
[参考②](https://wcf-drill.com/devise%E3%81%AE%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E6%9B%B4%E6%96%B0%E3%81%8C%E5%87%BA%E6%9D%A5%E3%81%AA%E3%81%84-752.html)
[GitHub公式のwiki](https://github.com/heartcombo/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password)

# ユーザー編集時だけパスワードのバリデーションを解除する
まず、Userモデルのバリデーションは以下のとおり

```ruby:app/models/user.rb
class User < ApplicationRecord
  has_many :menus

  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to :prefecture
  belongs_to :category

  has_one_attached :image

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  # パスワードは半角英数混合で８文字
  validates :password, format: { with: /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]{8}\z/i }
  with_options presence: true do
    validates :shop_name
    validates :address
    validates :business_hours
    validates :holiday
    validates :image
    validates :phone_number, format: { with: /\A0[1-9]\d{0,3}[-(]\d{1,4}[-)]\d{4}\z/ }
    with_options numericality: { other_than: 0, message: 'を選択してください' } do
      validates :category_id
      validates :prefecture_id
    end
  end
end
```

真ん中あたりにパスワードカラムのバリデーションに関する記述があり、正規表現を使っているため自動的に *presence: true* の存在性のバリデーションが効いています。
これは新規登録時には必須なのですが、編集時にパスワードを変えずに他の項目だけ変更したい場合にバリデーションエラーのもとになってしまいます。

### 解決策（オプションを記述する）
上記のパスワードカラムに対するバリデーションに *on: :create* オプションを記述します。
これにより新規登録時のみバリデーションがかかるようになります！
[参考：Railsガイド「onオプション」](https://railsguides.jp/active_record_validations.html#on)

```ruby:app/models/user.rb
class User < ApplicationRecord
(略)

  # on: :createオプションを追記
  validates :password, format: { with: /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]{8}\z/i }, on: :create
  with_options presence: true do
    validates :shop_name
    validates :address
    validates :business_hours
    validates :holiday
    validates :image
    validates :phone_number, format: { with: /\A0[1-9]\d{0,3}[-(]\d{1,4}[-)]\d{4}\z/ }
    with_options numericality: { other_than: 0, message: 'を選択してください' } do
      validates :category_id
      validates :prefecture_id
    end
  end
end
```

ただし、これではユーザー編集時にパスワードに関するバリデーション（今回は半角英数混合で８文字）がまったく効かなくなってしまい、「abc」とかでもパスワード設定できてしまいます。
そこで、今回はバリデーションの記述を２行に分け、*allow_blank: true* オプションを追記します。

ということで、最終的なモデルのコードはこんな感じ。

```ruby:app/models/user.rb
class User < ApplicationRecord
  has_many :menus

  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to :prefecture
  belongs_to :category

  has_one_attached :image

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  # パスワードに関するバリデーションをcreateとupdateで別々に記述しました
  validates :password, format: { with: /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]{8}\z/i }, on: :create
  validates :password, format: { with: /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]{8}\z/i, message: 'は半角英数混合で8文字です' }, allow_blank: true, on: :update
  with_options presence: true do
    validates :shop_name
    validates :address
    validates :business_hours
    validates :holiday
    validates :image
    validates :phone_number, format: { with: /\A0[1-9]\d{0,3}[-(]\d{1,4}[-)]\d{4}\z/ }
    with_options numericality: { other_than: 0, message: 'を選択してください' } do
      validates :category_id
      validates :prefecture_id
    end
  end

  def update_without_current_password(params)
    if params[:password].blank? && params[:password_confirmation].blank?
      params.delete(:password)
      params.delete(:password_confirmation)
    end
    update(params)
  end

end

```

これでユーザー編集時にパスワードの変更をしない場合は空欄のまま変更ができ、パスワードの変更をする場合は正規表現を適用することができました！

以上！