2012-01-05 7 views
3

update: Это тот случай, когда вызов update_attributes получает свою собственную транзакцию?after_commit callback вызывается несколько раз

Я рассмотрел вопрос this и по причинам в дополнение к этому вопросу, я решил пойти с after_commit как правильный крючок. Проблема в том, что он называется множественным (ровно три) раза. Код немного сложно объяснить, но в основном есть модель, которая имеет профиль

include Traits::Blobs::Holder 

в holder.rb у меня есть:

module ClassMethods 

    def belongs_to_blob(name, options = {}) 
     clazz = options[:class_name] ? options[:class_name].constantize : Blob 
     foreign_key = options[:foreign_key] || :"#{name}_id" 

     define_method "save_#{name}" do 
     blob = self.send(name) 
     if self.errors.any? && blob && blob.valid? 
      after_transaction do 
      blob.save! 
      #self[foreign_key] = blob.id 
      #save resume anyway 
      self.update_attribute(foreign_key, blob.id) 
      end 
     end 
     end 
     after_validation "save_#{name}" 

     belongs_to name, options 

     accepts_nested_attributes_for name 
    end 

    end 

, наконец, в самом profile.rb у меня есть:

after_commit :send_messages_after_registration! 

protected 

def send_messages_after_registration! 
    Rails.logger.debug("ENTERED : send_messages_after_registration " + self.owner.email.to_s) 
    if self.completed? 
    Rails.logger.debug("completed? is true " + self.owner.email.to_s) 
    JobSeekerNotifier.webinar_notification(self.owner.id).deliver 
    Resque.enqueue_in(48.hours, TrackReminderWorker, self.owner.id) 
    end 
end 

Кажется, что метод введен 3 раза. Я пытался понять это в течение нескольких дней, поэтому любое руководство, которое вы можете предоставить, будет оценено по достоинству.

код

контроллера:

def create 
    @user = Customer.new(params[:customer].merge(
    :source => cookies[:source] 
)) 
    @user.require_password = true 

    respond_to do |f| 
    if @user.save 
     promote_provisional_user(@user) if cookies[:provisional_user_id] 

     @user.profile.update_attributes(:firsttime => true, :last_job_title => params[:job_title]) unless params[:job_title].blank? 

     if params[:resume] 
     @user.profile.firsttime = true 
     @user.profile.build_resume(:file => params[:resume]) 
     @user.profile.resume.save 
     @user.profile.save 
     end 
    ... 
end 
+0

Имеет ли запрос профиля own_to_blob? –

+0

Как выглядит код вызова? – klochner

+0

обновил мой вопрос с кодом контроллера – Ramy

ответ

3

Так это происходит в 3 раза, потому что профиль сохраняется в 3 раза: один раз, когда пользователь будет сохранен (я предполагаю, что User accepts_nested_attributes_for :profile, один раз, когда вы звоните update_attributes(:first_time => true,...) и один раз, когда вы звоните сохранить в if params[:resume] блоке. Каждый бросок создает новую транзакцию (если не уже идет), вы в конечном итоге с несколькими вызовами after_commit

after_commit ли принимать :on вариант (который может принимать значения :create, :update, :destroy), чтобы вы могли ограничить его новыми записями. Это, очевидно, срабатывает при первом сохранении, поэтому вы не сможете увидеть резюме профиля и так далее.

Вы не могли бы в дополнение завернуть полноту этих обновлений в одной транзакции, в этом случае after_commit только вызывается один раз, независимо от того, сколько экономит иметь место внутри сделки, делая что-то вроде

User.transaction do 
    if @user.save 
    ... 
    end 
end 

The транзакция будет отменена, если будет поднято исключение (вы можете поднять ActiveRecord::Rollback, если вы хотите выручить)

+0

Не могли бы вы немного рассказать о том, как я буду «обертывать все эти обновления в одной транзакции», что звучит как желаемое поведение. Спасибо за руль! – Ramy

+0

Я обновил свой ответ на примере –

+0

, что было отличной идеей, но все равно, кажется, отправляет два письма. – Ramy

Смежные вопросы