Rails的协会 - 问题改变价值观,而过分的缓存! [英] Rails associations - problems with altering values, and too much caching!

查看:110
本文介绍了Rails的协会 - 问题改变价值观,而过分的缓存!的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个卡片游戏应用程序,其特点是播放器模型,其中有一个动作整数列;和模式。玩家可以扮演他们自己的存储卡,花费一个动作;一个特定的卡补助时,它的打了两个动作。

Suppose I've got a card-game app, which features a Player model, which has an actions integer column; and a Card model. A player can play a card they own, which costs an action; one particular card grants two actions when it's played.

如果我code此如下:

If I code this as follows:

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

...那么播放器#play_card 的净效应是递减动作 1。只有这样,我发现使双方更改应用到同一个对象,从而导致1行动的净增量,是这样定义的功能:

... then the net effect of Player#play_card is to decrement actions by 1. The only way I've found to make both changes apply to the same object, thereby resulting in a net increment of 1 action, is to define the functions like this:

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

但是,这变成一个单一的数据库写入两个写和读!当然,必须有一个更好的办法。我缺少什么?

But that turns a single database write into two writes and a read! Surely there must be a better way. What am I missing?

推荐答案

在您的code要装入的表玩家在同一行,但同时你期待的第一个版本的Rails足够聪明地认识到,它在内存中已经加载此行,铁轨不这样的。所以,当你在播放器发出+ = 2确实他+对另一个实例比你做了一个2 = - = 1

In the first version of your code you are loading the same row of the table players but while you are expecting rails to be smart enough to recognize that it has already load this row in memory, rails doesn't work that way. So when you are issuing a +=2 on player it does he +=2 on another instance than the one on which you have done -=1.

我设置一个小例子来说明,有太多相同的行实例:

i've setup a little example to show that there are too 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

所以最后你还没有实例保存与+ = 2应用,这里只有一个与保存在-1

So finally as you haven't save the instance with the +=2 applied, there's only the one with the -1 that is saved

更新

您可以尝试欺骗轨玩家使用的同一个实例的所有道路。这是一个有点难看,但它的工作原理。

You can try to trick rails to use the same instance of player all the way. This is a little bit 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 input those 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 right number of actions in player, and in the logs card is only loaded 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

要总结一下整个事情,我会说,有一些错误在code设计。如果我没有理解好,你想的是一个表行的每个AR实例是在ObjectSpace中相同的对象,但我想,如果轨道是构建方式,它会引入怪异的行为,你可以工作在半支持对象改变在验证等挂钩。

To sum up the whole thing, I would say that there's something wrong in your code design. If i understand well,what you would like is that every AR instance of a table row is the same object in the ObjectSpace, but I guess that if rails was build that way it would introduce strange behaviors where you could work on half backed object changed in validations and other hooks.

这篇关于Rails的协会 - 问题改变价值观,而过分的缓存!的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆