Попробуйте:
class Game
has_many :players
end
Измените логику tableau_contains
следующим образом:
class Player < ActiveRecord::Base
has_one :tableau
belongs_to :game
def tableau_contains(card_id)
tableau.tableau_cards.any?{|tc| tc.deck_card.card.id == card_id}
end
end
Измените логику after_save
следующим образом:
def after_save(turn)
game = Game.find(turn.game_id, :include => :goals))
Rails.logger.info("Begin eager loading..")
players = game.players.all(:include => [:player_goals,
{:tableau => [:tableau_cards=> [:deck_card => [:card]]]} ])
Rails.logger.info("End eager loading..")
Rails.logger.info("Begin tableau_contains check..")
if players.any?{|player| player.tableau_contains(turn.card_id)}
# do something..
end
Rails.logger.info("End tableau_contains check..")
end
Вторая строка в методе after_save
eager загружает данные, необходимые для проверки tableau_contains
.Такие вызовы, как tableau.tableau_cards
и tc.deck_card.card
не должны / не попадут в БД.
Проблемы в вашем коде:
1) Присвоение массива для has_many
ассоциации
@game.players = Player.find :all, :include => ...
Вышеприведенный оператор не является простым оператором присваивания,Изменяет palyers
строк таблицы с game_id
данной игры.Я предполагаю, что это не то, что вы хотите.Если вы проверите таблицу БД, то заметите, что updated_time
строк таблицы игроков изменились после назначения.
Необходимо присвоить значение отдельной переменной, как показано в примере кода в after_save
method.
2) Связанное вручную кодирование SQL
Во многих местах кода вы вручную кодируете SQL для данных ассоциации.Rails предоставляет ассоциации для этого.
Например:
tcards= TableauCard.find :all, :include => [ {:deck_card => (:card)}],
:conditions => ['tableau_cards.tableau_id = ?', self.tableau.id]
Может быть переписан как:
tcards = tableau.tableau_cards.all(:include => [ {:deck_card => (:card)}])
Ассоциация карт tableau_cards
на модели Tableau
создаеттот же SQL, который вы написали от руки.
Вы можете еще больше улучшить приведенное выше утверждение, добавив has_many :through
связь к Player
классу.
class Player
has_one :tableau
has_many :tableau_cards, :through => :tableau
end
tcards = tableau_cards.all(:include => [ {:deck_card => (:card)}])
Редактировать 1
Iсоздал приложение для проверки этого кода.Работает как положено.Rails запускает несколько SQL для загрузки данных, например:
Begin eager loading..
SELECT * FROM `players` WHERE (`players`.game_id = 1)
SELECT `tableau`.* FROM `tableau` WHERE (`tableau`.player_id IN (1,2))
SELECT `tableau_cards`.* FROM `tableau_cards`
WHERE (`tableau_cards`.tableau_id IN (1,2))
SELECT * FROM `deck_cards` WHERE (`deck_cards`.`id` IN (6,7,8,1,2,3,4,5))
SELECT * FROM `cards` WHERE (`cards`.`id` IN (6,7,8,1,2,3,4,5))
End eager loading..
Begin tableau_contains check..
End tableau_contains check..
Я не вижу ни одного SQL, выполненного после быстрой загрузки данных.
Изменить 2
Внести следующие изменения в ваш код.
def after_save(pa)
turn = Turn.find(pa.turn_id, :include => :player_actions)
game = Game.find(turn.game_id, :include => :goals)
players = game.players.all(:include => [ :player_goals, {:tableau => [:tableau_cards => [:deck_card => [:card]]]} ])
if turn.phase_complete(pa, game, players)
for player in game.players
if(player.tableau_contains(card))
...
end
end
end
end
def phase_complete(phase, game, players)
all_players_complete = true
for player in players
if(!player_completed_phase(player, phase))
all_players_complete = false
end
end
return all_players_complete
end
Кэширование работает следующим образом:
game.players # cached in the game object
game.players.all # not cached in the game object
players = game.players.all(:include => [:player_goals])
players.first.player_goals # cached
Второй приведенный выше оператор приводит к пользовательскому запросу на сопоставление.Следовательно, AR не кэширует результаты.Где player_goals
кэшируются для каждого объекта проигрывателя в 3-м операторе, поскольку они выбираются с использованием стандартной ассоциации SQL.