1

に遭遇しstack level too deep、無限ループが発生しましたが、理由がわかりません。他の誰かがそれを見つけてくれることを願っています。

Game モデルに次のメソッドがあります。

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 のコードを実行すると、ゲームを探すクエリは 1 回だけ実行され、無限ループは発生しません。

* アップデート *

問題は、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_wongame.home_team_scoregame.away_team_score

これを行うためにオブザーバーを使用できないようです。

4

4 に答える 4

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
于 2013-03-07T22:15:52.280 に答える
3

Game.record_win_or_tie を再度呼び出す after_save コールバックを定義したのではないでしょうか? それは無限再帰を説明するでしょう。

それ以外の場合は、ゲーム モデル全体を確認する必要があります

于 2013-03-07T22:13:02.973 に答える
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
于 2013-03-07T22:44:29.993 に答える
0

何らかの理由で after_save に含める必要がある場合は、現在のインスタンスを保存して 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 に移動します。

于 2013-03-07T23:59:03.247 に答える