在您的代码的第一个版本中,您正在加载桌面播放器的同一行,但是虽然您希望 rails 足够聪明,可以识别它已经将这一行加载到内存中,但 rails 不能以这种方式工作。因此,当您在播放器上发出 +=2 时,它会在另一个实例上 +=2,而不是您已在 -=1 上执行过的实例。
我已经设置了一个小例子来表明同一行的实例太多:
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
最后,由于您还没有保存应用 +=2 的实例,因此只有保存了 -1 的实例
更新
您可以尝试欺骗 rails 以一直使用相同的播放器实例。这有点难看,但它有效。
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
所以当你输入这些命令时:
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">
您在播放器中有正确数量的操作,并且在日志中卡片仅加载一次:
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
总结一下,我想说你的代码设计有问题。如果我理解得很好,您想要的是表行的每个 AR 实例都是 ObjectSpace 中的同一个对象,但我猜如果以这种方式构建 rails 会引入奇怪的行为,您可以在半支持对象更改时工作在验证和其他钩子中。