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和其他钩子。
- Heroku:推送拒绝,无法编译Ruby应用程序
- 获取最大预订数量
- Rails:查找父对象的所有关联对象
- refile simple_form undefined方法attachment_field
- 安装Xcode 4.3.2并缺少gcc时如何使用RVM安装Ruby 1.9.3?
- 在Heroku上运行Ruby / Rails应用程序的浏览器没有“销毁”确认
- Ruby on Rails:如何让多个提交按钮转到不同的方法(可能带有with_action?)
- Windows 7上的Paperclip :: Errors :: NotIdentifiedByImageMagickError
- 如何使用Turbolinks刷新页面