We’re using FK constraints at Attendio. As you all know, this means we must be careful about the order we load fixtures in our tests when calling fixtures. But what if you can’t control the order of the fixture load as when using the FixtureSets or FixtureScenarios plugins? Or maybe you simply don’t want to have to worry about the order?

One solution is to disable FK constraints before loading the fixtures, then enable them afterward. This is liable to be a vendor-specific solution, however, and it just feels bad. Another solution is to determine the correct order and ensure that the fixtures you want to load are loaded according to that order (which would require a modification to the plugins, but at least they could then work without the user needing to specify an order). Let’s say you have a few models:

  1.  
  2. class User < ActiveRecord::Base
  3.   has_many :memberships
  4.   has_many :groups, :through => :memberships
  5.   has_many :taggings, :as => :taggable
  6. end
  7.  
  8. class Group < ActiveRecord::Base
  9.   has_many :memberships
  10.   has_many :users, :through => :memberships
  11.   has_many :taggings, :as => :taggable
  12. end
  13.  
  14. class Membership < ActiveRecord::Base
  15.   belongs_to :user
  16.   belongs_to :group
  17. end
  18.  
  19. class Tag < ActiveRecord::Base
  20.   has_many :taggings
  21. end
  22.  
  23. class Tagging < ActiveRecord::Base
  24.   belongs_to :tag
  25.   belongs_to :taggable, :polymorphic => true
  26. end
  27.  

One correct order for loading the fixtures is users, groups, memberships, tags, taggings (there are four correct ways to load them). All we really have to do is look at the non-polymorphic belongs_to associations, and we’re set. Here’s some code that does just that.

Update: the code is cleaner now, and probably faster since it doesn’t build up an entire dependency tree.

  1.  
  2. def load_order
  3.   models = Dir[RAILS_ROOT + ‘/app/models/**/*.rb]
  4.   models.each { |file| require(file) }
  5.  
  6.   klasses = models.map { |file| File.basename(file, ‘.rb).camelize.constantize }
  7.   klasses.reject! { |klass| !klass.ancestors.include?(ActiveRecord::Base) }
  8.  
  9.   deps = klasses.inject({}) do |h,klass|
  10.     h[klass] = klass.reflect_on_all_associations.select { |a| a.macro == :belongs_to && !a.options[:polymorphic] }
  11.     h[klass] = h[klass].map { |a| a.class_name.constantize }
  12.     h
  13.   end
  14.  
  15.   order = []
  16.   until deps.empty?
  17.     free = deps.keys.select { |k| deps[k] == [] }
  18.     deps = deps.without(*free)
  19.     deps.each { |k,v| deps[k] = v - free }
  20.     order += free
  21.   end
  22.   order.map(&:table_name)
  23. end
  24.  

The code is rough and not yet integrated in a useful fashion, but it gives you an idea and will let you integrate it into your fixture solution as I have. It won’t work with HABTM relationships since there’s no model for the join table. Other than that it seems decent, if a bit on the ugly side. If anyone wants to clean it up I’d be obliged. Oh and by the way it’s under the Creative Commons Share-Alike license.