概要
モデルに定義されたコールバックをスキップしたい場合Model.skip_callback
が公開APIとして提供されている。しかしこのメソッドはクラスメソッドとして提供されているため、影響が全てのインスタンスに及んでしまう。
特定のインスタンスでだけコールバックをスキップする方法がないか調べたのでメモする。
結論
インスタンスレベルでコールバックをスキップする公開APIは用意されていないが、内部APIを利用して無理やりスキップすることができるようだった。
やり方
以下のように呼び出すことで、作成時のコールバックをスキップできる
ActiveRecord::Persistence.instance_method(:_create_record).bind(User.new).call
Rails 5.1.1 で検証
以下のようなモデルがある
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# token :string(255) not null
# secret :string(255) not null
# name :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
class User < ActiveRecord::Base
before_save do
self.token = 'foo'
self.secret = 'bar'
end
end
コールバックが呼び出されレコードが作成される
pry(main)> User.new.save!
INSERT INTO `users` (`token`, `secret`, `created_at`, `updated_at`) VALUES ('foo', 'bar', '2017-06-01 04:54:17', '2017-06-01 04:54:17')
=> true
コールバックが呼び出されないため、NotNullViolation
例外が発生する
pry(main)> ActiveRecord::Persistence.instance_method(:_create_record).bind(User.new).call
INSERT INTO `users` (`token`, `secret`, `name`, `created_at`, `updated_at`) VALUES (NULL, NULL, NULL, NULL, NULL)
=> ActiveRecord::NotNullViolation: Mysql2::Error: Column 'token' cannot be null