I just hit something very interesting today. You remember about attr_accessible, right ? And you use it to protect your models from injection, right ?
Anyway, I added attr_accessible to my models and then ran my tests. BAM! Hundreds of errors. Ooops, time to revert and go slowly here.
Let’s take one example:
app/models/tournament.rb
1 class Tournament < ActiveRecord::Base
2 belongs_to :game
3 validates_presence_of :game_id
4 end
We want to protect Tournament from injection attacks. The accessible columns are: name, game_id. So, let’s add that:
app/models/tournament.rb
1 class Tournament < ActiveRecord::Base
2 belongs_to :game
3 attr_accessible :name, :game_id
4 validate do |tournament|
5 tournament.errors.add_to_base("Game must be active") unless tournament.game.active?
6 end
7 end
Run the tournament tests, and chaos ensues. Looking at the errors, I can see a recurring pattern: all of my tests are complaining about a NoMethodError active? on NilClass. What ? Look here, buddy, I am giving you a game:
test/unit/tournament_test.rb
1 class TournamentWithGameTest < Test::Unit::TestCase
2 fixtures :games
3
4 def setup
5 @tournament = Tournament.create!(:game => Game.find(:first),
6 :name => ‘My Tournament’)
7 end
8 end
The fix is very obvious, once you think about it:
app/models/tournament.rb
1 class Tournament < ActiveRecord::Base
2 belongs_to :game
3 attr_accessible :name, :game_id, :game
4 validate do |tournament|
5 tournament.errors.add_to_base("Game must be active") unless tournament.game.active?
6 end
7 end
Since ActiveRecord::Base is doing a straight key removal from the attributes Hash when using attr_accessible, it is important to also include any relational attributes as well.
UPDATE 2006-12-06: Trevor Squires is the author of the post I was searching for. Both of his articles are referenced above.