Reputation: 3638
I'm running into a stack level too deep
and have an infinite loop, but I can't figure out why, so I'm hoping someone else can spot it.
I have this method in my Game model:
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
When I run it from the console for a game where the conditional is true (ie game.home_team_score is greater than game.away_team_score) if keeps running the same query over and over again.
SELECT `games`.* FROM `games` WHERE `games`.`id` = 1 LIMIT 1
If I run the code for a game_id where the conditional is false, the query looking for the game only happens once and there is no infinite loop.
* UPDATE *
I figured out that the problem was that I was calling the method from my GameObserver:
class GameObserver < ActiveRecord::Observer
def after_save(game)
Game.record_win_or_tie(game.id)
end
end
However, I don't know how to adjust my code. The requirement is to automatically update either game.home_team_won
or game.away_team_won
after someone has updated game.home_team_score
or game.away_team_score
.
It seems like I can't use an observer to do this.
Upvotes: 1
Views: 3691
Reputation: 35531
Use an instance variable to ensure it only gets saved once. However, because this is a class method, it will not be thread safe. Instead make this an instance method like so:
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
Now you can have your observer trigger the instance_method like this:
class GameObserver < ActiveRecord::Observer
observe :game
def after_save(game)
game.record_win_or_tie
end
end
Note that you can also avoid all this if you perform this logic in a before_save
callback (without actually saving inside the callback) instead of 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
Upvotes: 5
Reputation: 3963
If for some reason it has to be in an after_save, instead of saving the current instance and triggering the after save, or adding spurious instance variables, call update on the db directly.
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
But personally, if possible I'd move it to a before_save as mentioned in another answer.
Upvotes: 0
Reputation: 4496
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
Upvotes: 0
Reputation: 3266
Could it be that you have defined an after_save callback that calls Game.record_win_or_tie again? That would explain the infinite recursion.
Otherwise we'd need to see the entire Game model
Upvotes: 3