给定以下规范:
describe Participation do
describe "invitation by email", :focus do
let(:participation) { build :participation } # Factory
let(:email) { participation.email }
it "should send an invitation" do
# This one is failing
binding.pry # The code below is executed here
participation.should_receive(:invite_user!)
participation.save!
end
context "when user already exists" do
let!(:existing) { create :user, :email => email }
it "should not send an invitation" do
participation.should_not_receive(:invite_user!)
participation.save!
end
end
end
end
我似乎无法通过以下实现传递它:
class Participation < ActiveRecord::Base
attr_accessor :email
belongs_to :user
validates :email, :email => true, :on => :create, :if => :using_email?
before_validation :set_user_by_email, :if => :using_email?, :on => :create
before_create :mark_for_invitation, :unless => :user_exists?
after_create :invite_user!, :if => :marked_for_invitation?
def using_email?
email.present?
end
def user_exists?
user.present? and user.persisted?
end
def set_user_by_email
self.user = User.find_by_email(email)
self.user ||= User.new(email: email).tap do |u|
u.status = :invited
end
end
def mark_for_invitation
@invite_user = true
true # make sure not cancelling the callback chain
end
def marked_for_invitation?
!!@invite_user
end
def invite_user!
# TODO: Send the invitation email or something
end
end
我真的看不出我做错了什么。这是失败规范的“控制台”输出:
# Check the before_validation callback options:
participation.user # nil
participation.valid? # true
participation.user # User{id: nil}
# Check the before_create callback options:
participation.user_exists? # false
participation.mark_for_invitation # true
# Check the after_create callback options:
participation.marked_for_invitation? # true
# After all this I expect the "invite_user!" to be called:
participation.stub(:invite_user!) { puts "Doesn't get called :(" }
participation.save! # => true, Nothing is printed, which is consistent with the spec
participation.user_id # => 11, so the user has been saved
你能找出为什么 User#invite_user!
没有被调用的问题吗?
最佳答案
belongs_to 关联默认为 :autosave => true
,因此当保存 Partecipation 时用户记录会被持久化,事件记录实现它的方式是简单地定义一个保存用户的 before_save 回调.
由于 before_create 回调在 before_save 回调之后被调用 [1] mark_for_invitation 回调在用户关联被保存之后被“调用”,因此它永远不会像 :user_exists?
方法那样实际执行在这一点上总是正确的。
解决办法是改变
before_create :mark_for_invitation, :unless => :user_exists?
变成:
before_save :mark_for_invitation, :on=>:create, :unless => :user_exists?
放在belongs_to之前
这是一篇对此进行解释的文章:http://pivotallabs.com/users/danny/blog/articles/1767-activerecord-callbacks-autosave-before-this-and-that-etc-
[1] 参见:http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
关于ruby-on-rails - ActiveRecord 回调未执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7988718/