Rails ассоциации - проблемы с изменением значений и слишком много кеширования! - PullRequest
0 голосов
/ 13 октября 2010

Предположим, у меня есть приложение для карточных игр, в котором есть модель 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

Но это превращаетзапись в одну базу данных в две записи и чтение!Конечно, должен быть лучший способ.Чего мне не хватает?

1 Ответ

0 голосов
/ 13 октября 2010

В первой версии вашего кода вы загружаете тот же ряд проигрывателей таблиц, но, хотя вы ожидаете, что рельсы будут достаточно умными, чтобы признать, что он уже загрузил этот ряд в память, рельсы не работают таким образом.Поэтому, когда вы выдаете + = 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,

ОБНОВЛЕНИЕ

Вы можете попытаться обмануть рельсы, чтобы полностью использовать один и тот же экземпляр игрока.Это немного уродливо, но это работает.

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, но я предполагаю, что если бы рельсы создавались таким образом, это привело бы к странному поведению, при котором вы могли бы работать с измененным объектом на половинув валидациях и других хуках.

...