2013-03-07 3 views
1

Я столкнулся с stack level too deep и имеет бесконечный цикл, но я не могу понять, почему, поэтому я надеюсь, что кто-то еще сможет это обнаружить.Rails Infinite Loop - Уровень стека слишком глубокий

У меня есть этот метод в моей модели игры:

def self.record_win_or_tie(game_id) 
    game = Game.find(game_id) 

    if game.home_team_score > game.away_team_score 
    game.home_team_won = true 
    game.save 
    end 

end 

Когда я запускаю его из консоли для игры, где условный истинно (т.е. game.home_team_score больше game.away_team_score), если продолжает работать один и тот же запрос снова и снова.

SELECT `games`.* FROM `games` WHERE `games`.`id` = 1 LIMIT 1 

Если я запускаю код для game_id, где условная ложь, запрос ищет игру только один раз и нет бесконечного цикла.

* UPDATE *

я понял, что проблема заключалась в том, что я звоню метод из моего GameObserver:

class GameObserver < ActiveRecord::Observer 
    def after_save(game) 
    Game.record_win_or_tie(game.id) 
    end 
end 

Однако, я не знаю, как настроить свой код. Требование состоит в том, чтобы автоматически обновлять либо game.home_team_won, либо game.away_team_won после того, как кто-то обновил game.home_team_score или game.away_team_score.

Кажется, я не могу использовать наблюдателя для этого.

+0

Какой метод сообщается в сообщении об ошибке, которое выполняется снова и снова? И педантично, у вас нет бесконечного цикла, а скорее бесконечная рекурсия. –

+0

Не могли бы вы также добавить свой метод 'find()'? –

+0

Когда я запускаю игру.record_win_or_tie (game_id) 'из консоли, он показывает запрос на поиск игры, он показывает, что он устанавливает' home_team_won = true', а затем повторяет запрос поиска игры повторно и заканчивается 'SystemStackError: уровень стека слишком глубокий Может быть, ошибка IRB! ' – yellowreign

ответ

5

Используйте переменную экземпляра, чтобы убедиться, что он только будет сохранен один раз. Однако, поскольку это метод класса, он не будет потокобезопасным. Вместо того, чтобы сделать этот метод экземпляра следующим образом:

def record_win_or_tie 
    return if @inside_callback 
    @inside_callback = true 

    if home_team_score > away_team_score 
    update_attributes(:home_team_won => true) 
    end  
end 

Теперь вы можете иметь свой наблюдатель вызвать instance_method так:

class GameObserver < ActiveRecord::Observer 
    observe :game 

    def after_save(game) 
    game.record_win_or_tie 
    end 
end 

Обратите внимание, что вы можете избежать всего этого, если вы выполняете эту логику в before_save обратного вызова (без фактического сохранения внутри обратного вызова) вместо after_save:

class Game < ActiveRecord::Base 
    def record_win_or_tie 
    self.home_team_won = true if home_team_score > away_team_score 
    end 
end 

class GameObserver < ActiveRecord::Observer 
    observe :game 

    def before_save(game) 
    game.record_win_or_tie 
    end 
end 
+0

Это звучит как лучшее решение, но я не совсем понимаю, как изменить свой код, чтобы сделать это. – yellowreign

+0

@yellowreign - обновлено для этого. – PinnyM

+0

Спасибо Пинни. Я получил after_save для работы, но по какой-то причине before_save никогда не сохранял home_team_won, хотя он и попал в game.record_win_or_tie (я добавил высказывания puts, чтобы видеть). Является ли метод before_save лучшим? – yellowreign

3

Возможно, вы определили обратный вызов after_save, который снова вызывает Game.record_win_or_tie? Это объясняет бесконечную рекурсию.

В противном случае мы должны были бы увидеть всю модель Game

0
class Game < ActiveRecord::Base 
    # def self.record_win_or_tie(game_id) # deprecated 
end 

class GameObserver < ActiveRecord::Observer 
    def after_save(game) 
    if (game.home_team_score > game.away_team_score) && game.home_team_won != true 
     game.home_team_won = true 
     game.save 
    end 
    end 
end 
0

Если по какой-то причине он должен быть в after_save, вместо сохранения текущего экземпляра и запуска после сохранения или добавления ложных переменных экземпляра, немедленно вызовите обновление на db.

if game.home_team_score > game.away_team_direct 
    Game.update_all({:home_team_won => true}, :id => id) 
end 
# Check the syntax, as I wrote it off the top of my head 

Но лично, если возможно, я переместил бы его в before_save, как указано в другом ответе.

Смежные вопросы