Rails关联 – 改变值的问题,以及过多的缓存!

假设我有一个卡片游戏应用程序,其中包含一个Player模型,它有一个actions整数列; 和Card模型。 玩家可以玩他们拥有的牌,这需要一个动作; 一张特定的牌在播放时会发出两个动作。

如果我按如下方式编码:

 class Player < ActiveRecord::Base has_many :cards def play_card(card) raise "Not yours!" unless cards.include? card self.actions -= 1 card.play save! end end class Card < ActiveRecord::Base belongs_to :player def play player.actions += 2 end end 

…然后, Player#play_card的净效果是将actions减少1.我发现两个更改的唯一方法适用于同一个对象,从而导致1个操作的净增量,是定义函数像这样:

 class Player < ActiveRecord::Base has_many :cards def play_card(card) raise "Not yours!" unless cards.include? card self.actions -= 1 // Stick that change in the Database save! card.play end end class Card < ActiveRecord::Base belongs_to :player def play // Force reload of the player object player(true).actions += 2 // And save again player.save! end end 

但这会将单个数据库写入两次写入和读取! 当然必须有更好的方法。 我错过了什么?

在您的代码的第一个版本中,您正在加载表格播放器的同一行,但是当您期望rails足够智能以识别它已经将此行加载到内存中时,rails不会以这种方式工作。 因此,当您在玩家身上发出+ = 2时,他在另一个实例上+ = 2而不是您已完成的实例 – = 1。

我设置了一个小例子来说明同一行的实例也是如此:

 ruby-1.8.7-p174 > p_instance_1 = Player.first => # ruby-1.8.7-p174 > c = Card.first => # ruby-1.8.7-p174 > p_instance_2 = c.player => # 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的实例

UPDATE

您可以尝试欺骗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 => # ruby-1.8.7-p174 > p.play_card(Card.first) => true ruby-1.8.7-p174 > p => # ruby-1.8.7-p174 > p.reload => # 

您在播放器中拥有正确数量的操作,并且在日志卡中仅加载一次:

  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是这样构建的,那么它会引入奇怪的行为,你可以在半个被支持的对象上工作在validation和其他钩子。