after_update attributes problem

Hey guys,

I'm having a hard time w/ the after_update callback on rails... As
far as I can tell, when I define a after_update callback on a model,
the attributes of the object have the same values that they had
*before* Base.save was called. I'm probably wrong so here's the code:

UNIT TEST:
  def test_register_item_adjusts_account_balance
    account = accounts(:cams_credit_card)
    transaction = RegisterItem.new(:date => '2006-10-31',
                                   :description => 'Chinese food',
                                   :amount => '-13.44')
    transaction.account = account
    transaction.save
    assert_equal -113.44, account.balance #this assertion passes

    transaction.amount = -15.50
    transaction.save
    assert_equal -115.50, account.balance # FAILS

    transaction.destroy
    assert_equal -100.00, account.balance #this assertion will pass
  end

FAILURE OUTPUT:
Loaded suite test/unit/register_item_test
Started
.F.
Finished in 0.202591 seconds.

  1) Failure:
test_register_item_adjusts_account_balance(RegisterItemTest)
[test/unit/register_item_test.rb:31]:
<-115.5> expected but was
<-113.44>.

3 tests, 6 assertions, 1 failures, 0 errors

So changing the amount on a transaction (RegisterItem) should change
the current balance of an account. Here's the relevent callbacks:

REGISTER_ITEM UPDATE CALLBACKS:
  def before_update
    account.balance -= amount
    account.save
  end

  def after_update
    account.balance += amount
    account.save
  end

So the idea is that before the transaction is updated i subtract the
amount of the transaction back from the account, and then add the new
amount back on. It looks like @amount is still the old value during
the after_update callback though. is that correct? how can i access
the new value?

Thanks,
Cameron Matheson

the steps are:

before_update
[update operation]
after_update

so to keep things simple, say your account balance is 0.0:

...
# existing transaction
transaction.amount = 10.0 # transaction.account.balance = 0.0 still

transaction.save
  # before_update: transaction.amount = 10.0,
transaction.account.balance = -10.0
  # update operation
  # after_update: transaction.amount = 10.0, transaction.account.balance = 0.0

so you're back where you started, which is what your test is telling you.

Hey thanks for the response:

so to keep things simple, say your account balance is 0.0:

...
# existing transaction
transaction.amount = 10.0 # transaction.account.balance = 0.0 still

transaction.save
  # before_update: transaction.amount = 10.0,
transaction.account.balance = -10.0
  # update operation
  # after_update: transaction.amount = 10.0, transaction.account.balance = 0.0

so you're back where you started, which is what your test is telling you.

Ok so there is a problem in the way i understand the before_update
callback, or in your logic (probably my problem which would explain
the bug). Anyway, this is a before/after_*update* callback, it looks
like the scenario you're describing is a before/after_*create* kind of
deal. so the way i would understand it would be like so:

#account.balance == 0
transaction.amount = 10.0 #transaction.amount formerly == 0
before_update: transaction.amount -= 0\
after_update: transaction.amount += 10
# account.balance == 10

is that wrong?
Cameron Matheson

Hey Steve,

Would calling account.reload after the save cause problems for you
elsewhere?

I wasn't aware of the reload method. I called it after save and
although it didn't cause any problems, it didn't change the behavior
of the program either (unless i called self.reload on the transaction,
in which case it looks like the after_update callback didn't get
called)

Thanks!
Cam

these are the callbacks that are run during an update operation

before_validation
before_validation_on_update
after_validation
after_validation_on_update
before_save
before_update
<UPDATE OPERATION>
after_update
after_save

if you are creating a new record, replace *_update with *_create in
the above list. thats the difference.

so, knowing this, we see that your before and after updates will run
around the update operation.

# transaction.account.balance = -100.00
transaction.amount = -15.50
   transaction.save # before_update, update, after_update

   # before_update callback
   # -----------------------------------
   # account.balance -= amount
   # -100.00 - (-15.50) = -(100.00 - 15.50) = -84.50
   # account.save

hey,

follow?

it makes sense... i hadn't understood how it worked previously. i
figured that since it was before save was called, that the data would
have been the old data. so is there no way to access the previous
data w/ callbacks? i guess i could store the old amount in all of the
RegisterItem model, but i'd rather not go that route.

thanks,
cam