In the first version of your code, you load the same row of players into the table, but while you expect the rails to be smart enough to recognize that it is already loading this row into memory, the rails do not work that way. Therefore, when you set + = 2 on the player, it + = 2 on a different instance than the one on which you did - = 1.
I have a small example showing that there is too one instance of the same row:
ruby-1.8.7-p174 > p_instance_1 = Player.first => #<Player id: 1, actions: -1, created_at: "2010-10-13 17:07:22", updated_at: "2010-10-13 17:11:00"> ruby-1.8.7-p174 > c = Card.first => #<Card id: 1, player_id: 1, created_at: "2010-10-13 17:07:28", updated_at: "2010-10-13 17:07:28"> ruby-1.8.7-p174 > p_instance_2 = c.player => #<Player id: 1, actions: -1, created_at: "2010-10-13 17:07:22", updated_at: "2010-10-13 17:11:00"> ruby-1.8.7-p174 > p_instance_1.object_id => 2158703080 ruby-1.8.7-p174 > p_instance_2.object_id => 2156926840 ruby-1.8.7-p174 > p_instance_1.actions += 1 => 0 ruby-1.8.7-p174 > p_instance_2.actions += 1 => 0
So, since you did not save the instance with + = 2 attached, there will only be one that has -1, which is saved
UPDATE
You can try to trick the rails to use the same player instance. This is a little ugly, but it works.
class Player < ActiveRecord::Base has_many :cards def play_card(card) raise "Not yours!" unless cards.include? card new_self = card.player card.play new_self.actions -= 1 new_self.save! end end class Card < ActiveRecord::Base belongs_to :player def play player.actions += 2 end end
so when you enter these commands:
ruby-1.8.7-p174 > p = Player.first => #<Player id: 1, actions: 0, created_at: "2010-10-14 13:33:51", updated_at: "2010-10-14 13:33:51"> ruby-1.8.7-p174 > p.play_card(Card.first) => true ruby-1.8.7-p174 > p => #<Player id: 1, actions: 0, created_at: "2010-10-14 13:33:51", updated_at: "2010-10-14 13:33:51"> ruby-1.8.7-p174 > p.reload => #<Player id: 1, actions: 1, created_at: "2010-10-14 13:33:51", updated_at: "2010-10-14 13:34:40">
You have the correct number of actions in the player, and in the logs the map is loaded only once:
Player Load (0.5ms) SELECT * FROM "players" LIMIT 1 Card Load (0.2ms) SELECT * FROM "cards" LIMIT 1 Card Load (0.2ms) SELECT "cards".id FROM "cards" WHERE ("cards"."id" = 1) AND ("cards".player_id = 1) LIMIT 1 Player Load (0.1ms) SELECT * FROM "players" WHERE ("players"."id" = 1) Player Update (0.6ms) UPDATE "players" SET "updated_at" = '2010-10-14 13:34:40', "actions" = 1 WHERE "id" = 1
To summarize everything, I would say that there is something wrong with your code. If I understand well what you would like, each AR instance of a table row is the same object in ObjectSpace, but I assume that if the rails were constructed this way, this would create strange behavior that you could work in on a partially reinforced object in checks and other hooks.