Reputation: 21261
update: Is it the case that a call to update_attributes gets it's own transaction?
I've looked at this question and for reasons in addition to that question, i've decided to go with after_commit as the proper hook. The problem is it's being called multiple (exactly three) times. The code is a little complex to explain, but basically there is a profile model that has
include Traits::Blobs::Holder
in holder.rb I have:
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
finally in profile.rb itself I have:
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
it appears that the method is entered 3 times. I've been trying to figure this out for a few days so any guidance you can provide will be appreciated.
controller code:
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
Upvotes: 4
Views: 3727
Reputation: 84114
So it's happening 3 times because the profile is being saved 3 times: once when the user is saved (I assume that User accepts_nested_attributes_for :profile
, once when you call update_attributes(:first_time => true,...)
and once when you call save in the if params[:resume]
block. Every save creates a new transaction (unless one is already in progress) you end up with multiple calls to after_commit
after_commit
does take an :on
option (which can take the values :create
, :update
, :destroy
) so that you can limit it to new records. This would obviously fire on the first save so you wouldn't be able to see the profile's resumé and so on.
You could in addition wrap the entirety of those updates in a single transaction, in that case after_commit
only gets called once, no matter how many saves take place inside the transaction by doing something like
User.transaction do
if @user.save
...
end
end
The transaction will get rolled back if an exception is raised (you can raise ActiveRecord::Rollback
if you want to bail out)
Upvotes: 4