after_create callback called twice in test env when using fixtures

All,

I've been trying to figure out a strange bug in one of my applications
that I think I've narrowed down to a problem with rails fixtures. It
seems as though the after_create callback is being run twice when I
save a record. This is only happening a) in the test environment, and
b) when there is a fixture file for that table.

The test below only prints "Doing stuff" one time if I remove the
'fixtures' line. Likewise, saving the item through ./script/console
in development mode also does not call the do_stuff method twice.

I'm inclined to think that this is a bug in the handling of fixtures,
but I'd love to be proven wrong. I couldn't find this in trac, but if
there's an open ticket, then I'd like to know about it. If not, and
if everyone agrees that this is a likely bug, then I'll open one.

The model:

  class Item < ActiveRecord::Base
    after_create :do_stuff
    def do_stuff
      puts "Doing stuff"
    end
  end

The test:

  require File.dirname(__FILE__) + '/../test_helper'
  require 'item'

  class ItemTest < Test::Unit::TestCase
    fixtures :items

    def test_callback
      @item = Item.new
      puts "========================"
      @item.save
      puts "========================"
    end
  end

The fixture file:

  one:
    id: 1
  two:
    id: 2

The rake run:

  # rake test:units
  (in /Users/tsaleh/Documents/Programming/svn/bug/trunk)
  /opt/local/bin/ruby -Ilib:test "/opt/local/lib/ruby/gems/1.8/gems/
rake-0.7.3/lib/rake/rake_test_loader.rb" "test/unit/item_test.rb"
  Loaded suite /opt/local/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake/
rake_test_loader
  Started

Tammer Saleh wrote:

All,

I've been trying to figure out a strange bug in one of my applications
that I think I've narrowed down to a problem with rails fixtures. It

Try this in your environment.rb:

module ActiveRecord
  module Callbacks
    def callbacks_for(method)
      (self.class.read_inheritable_attribute(method.to_sym) or []).uniq
    end
  end
end

- OR -

Use the alternate form of defining a callback:

def after_create
  # ...
end

From what I understand, the problem seems to be that the 'after_create
:foo, :bar' syntax appends those symbols to an array without doing a
'uniq'. It seems that if you happen to load the class twice, you'll get
double entries in the callback list. This can happen easily because the
Ruby 'require' method doesn't seem to convert paths to absolute paths,
e.g the following will cause the file '/lib/foo.rb' to be loaded twice:

# In \app\models\bar.rb
require File.join(RAILS_ROOT, 'lib', 'foo')
require '../../lib/foo'
class Bar < ActiveRecord::Base

end

Brian Hartin wrote:

Tammer Saleh wrote:

All,

I've been trying to figure out a strange bug in one of my applications

Or, perhaps more cleanly:

module Callbacks
  def callbacks_for_with_uniq(method)
    callbacks_for_without_uniq(method).uniq
  end

  alias_method_chain :callbacks_for, :uniq
end