Rails无限循环 - 堆栈级别太深

时间:2013-03-07 21:57:12

标签: ruby-on-rails ruby

我遇到了stack level too deep并且有一个无限循环,但我无法弄清楚为什么,所以我希望别人能发现它。

我的游戏模型中有这个方法:

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

当我从控制台运行条件为true的游戏时(例如game.home_team_score大于game.away_team_score)如果一遍又一遍地运行相同的查询。

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

如果我运行条件为false的game_id的代码,则查找游戏的查询只发生一次,并且没有无限循环。

*更新*

我发现问题在于我是从我的GameObserver调用方法:

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

但是,我不知道如何调整我的代码。要求是在有人更新game.home_team_wongame.away_team_won后自动更新game.home_team_scoregame.away_team_score

好像我不能用观察者来做这件事。

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:3)

你是否定义了一个再次调用Game.record_win_or_tie的after_save回调?这可以解释无限递归。

否则我们需要看到整个游戏模型

答案 2 :(得分: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

答案 3 :(得分:0)

如果由于某种原因它必须在after_save中,而不是保存当前实例并触发保存后,或添加虚假实例变量,请直接在db上调用update。

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,如另一个答案所述。