0

When updating my User model, I have a callback which updates the credit field:

class User < ActiveRecord::Base
  around_update :adjust_credit

  ...

  def adjust_credit
    ...
    yield
    ...
    puts "LEAVING: credit was #{self.credit}"

    new_credit = self.credit - User.average_active
    puts "new credit =  #{new_credit}"

    success = self.update_attribute :credit, new_credit

    puts "AFTER SAVE CREDIT: #{User.find(self.id).credit}"
    puts "Successful? #{success}"

  end
end  

For some reason, credit doesn't update, as verified by the console log:

#console log
LEAVING: credit was 40
new credit =  -25
AFTER SAVE CREDIT: 40
Successful? true
user1618840
  • 453
  • 1
  • 5
  • 14
  • The model is saved during the `yield` to update. Your changes occur after update completes so you will need to save the model again afterwards –  Apr 27 '15 at 09:34

2 Answers2

2

Edit: Your problem was that if you modify an attribute "in place", this means: without assigning it a new value, then Rails will think that there is nothing new to be saved, so it "optimizes" the save away.

Try this:

def adjust_credit
    ...
    yield #saves
    ...
    puts "LEAVING: credit was #{self.credit}"

    new_credit = self.credit - User.average_active
    puts "new credit =  #{new_credit}"

    self.credit = new_credit
    success = self.update_column(:credit, new_credit)


    puts "AFTER SAVE CREDIT: #{User.find(self.id).credit}"
    puts "Successful? #{success}"

end

http://apidock.com/rails/ActiveRecord/Persistence/update_column


You should understand the flow of around_ callback first. The around_* callback is called around the action and inside the before_* and after_* actions. For example:

class User
  def before_save
    puts 'before save'
  end

  def after_save
    puts 'after_save'
  end

  def around_save
    puts 'in around save'
    yield
    puts 'out around save'
  end
end

User.save
  before save
  in around save
  out around save
  after_save
=> true

for more details of callbacks: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

via: Pan Thomakos

Gagan Gami
  • 10,121
  • 1
  • 29
  • 55
  • I understand this, but I can only update `credit` once yield has been called. I should still be able to save the credit separately from the yield update anyway right? – user1618840 Apr 27 '15 at 09:47
  • @user1618840 : I have updated my answer plz try and let me know it works for you or not? If any error occur then also let me know – Gagan Gami Apr 27 '15 at 09:58
  • Thanks but unfortunately this gives the same values – user1618840 Apr 27 '15 at 10:24
  • @user1618840 : Let try with `update_column` I have updated my answer – Gagan Gami Apr 27 '15 at 11:42
  • Thanks Gagan, this works great! I still don't entirely understand why though, as I thought that .save also assigns a new value – user1618840 Apr 28 '15 at 07:47
  • @user1618840 : When you call `update_column` or `update_attribute` it update and save the data. No need to again save it manually. – Gagan Gami Apr 28 '15 at 07:53
0

first why you use:

self.update_attribute(:credit, new_credit)
success = self.save

update_attribute already save the record and self.save will confuse you

success = self.update_attribute(:credit, new_credit)

and i suggest to use

self.update_column(:credit, new_credit)

the different is update_column will not trigger the callbacks again, but update_attribute will

mohamed-ibrahim
  • 10,837
  • 4
  • 39
  • 51