обратный вызов after_commit вызывается несколько раз - PullRequest
3 голосов
/ 06 января 2012

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

Я посмотрел на этот вопрос, и по причинам, помимо этого, я решил использовать 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

1 Ответ

3 голосов
/ 06 января 2012

Так происходит 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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...