先日開発中のアプリケーションをRails5.1.3にアップデートしたところ、Deviseの挙動が少しおかしくなったので、それを解消するまでの話です。
Undefined method will_save_change_to_email?
とりあえずアップデートをかけてテストしてみたら以下のようなエラー。
undefined method `will_save_change_to_email?' for #<User:0x007fedc1671310>
Did you mean? will_save_change_to_name?
will_save_change_to_id?
will_save_change_to_uid?
will_save_change_to_token?
will_save_change_to_attribute?
とりあえずGithubの中身を確認
devise/lib/devise/models/validatable.rb
module Devise
module Models
# Validatable creates all needed validations for a user email and password.
# It's optional, given you may want to create the validations by yourself.
# Automatically validate if the email is present, unique and its format is
# valid. Also tests presence of password, confirmation and length.
#
# == Options
#
# Validatable adds the following options to devise_for:
#
# * +email_regexp+: the regular expression used to validate e-mails;
# * +password_length+: a range expressing password length. Defaults to 8..72.
#
module Validatable
# All validations used by this module.
VALIDATIONS = [:validates_presence_of, :validates_uniqueness_of, :validates_format_of,
:validates_confirmation_of, :validates_length_of].freeze
def self.required_fields(klass)
[]
end
def self.included(base)
base.extend ClassMethods
assert_validations_api!(base)
base.class_eval do
validates_presence_of :email, if: :email_required?
if Devise.activerecord51?
validates_uniqueness_of :email, allow_blank: true, if: :will_save_change_to_email?
validates_format_of :email, with: email_regexp, allow_blank: true, if: :will_save_change_to_email?
else
validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
end
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: password_length, allow_blank: true
end
end
def self.assert_validations_api!(base) #:nodoc:
unavailable_validations = VALIDATIONS.select { |v| !base.respond_to?(v) }
unless unavailable_validations.empty?
raise "Could not use :validatable module since #{base} does not respond " <<
"to the following methods: #{unavailable_validations.to_sentence}."
end
end
protected
# Checks whether a password is needed or not. For validations only.
# Passwords are always required if it's a new record, or if the password
# or confirmation are being set somewhere.
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
end
def email_required?
true
end
module ClassMethods
Devise::Models.config(self, :email_regexp, :password_length)
end
end
end
end
if Devise.activerecord51?
validates_uniqueness_of :email, allow_blank: true, if: :will_save_change_to_email?
validates_format_of :email, with: email_regexp, allow_blank: true, if: :will_save_change_to_email?
ここでActiveRecord5.1だった時の処理が追加されていますね。
ここの条件として利用されているのがwill_save_change_to_email?という関数になります。ですが、どうやらdeviseのレポジトリの中のどこにもその関数が定義されていないようです。
バグっぽい?(2017/08/08時点)
Issuesでも指摘されているように、どうやらバグっぽいのですね。自分でこのdeviseを適用しているモデルにこの関数を定義してあげる必要があります。
app/models/user.rb
def will_save_change_to_email?
false
end
今回私のアプリケーションではEmailを使っていなかったので、falseに設定しています。Emailを使っているならtrueにするといいでしょう。