Machinist, how do I reference the object I'm making and pass it to an association? (AssociationTypeMismatch)

1.6k Views Asked by At

I'm trying to build factories for relatively complex models.

I have a Pressroom model, which belongs to Source, and Source has many Pressrooms. When creating the Source, if pressrooms is empty, an initial pressroom is created in an after_create filter. The pressroom site must be unique per source.

class Source
  has_many :pressrooms
  after_create :create_initial_pressroom! # if pressrooms.empty?
  ...
end

class Pressroom
  belongs_to :source
  # source.pressrooms.map(&:site) should have unique elements
  validate_on_create :check_unique_site
end

This leads to my problem: My Pressroom.make fails, because it builds a Source, which has no pressrooms, so the after_create callback creates one, and when the Pressroom.make tries to finish up, its site is not unique. I don't want to create two pressrooms when I run Pressroom.make

My attempt to solve this is to make the source association in the pressroom blueprint reference the pressroom. Sort of what Source.create :pressrooms => [Pressroom.new] would do.

Pressroom.blueprint do
  source { Source.make :pressrooms => [self] }
  site { source.site }
end

Unfortunatly, self is not yet a Pressroom. It's an instance of Machinist::Lathe, so I get an ActiveRecord::AssociationTypeMismatch exception.

I'm a bit of a newbie when it comes to factories and Machinist. I don't want to have to change the business logic, and I want to be able to cleanly make pressrooms with Pressroom.make without making two pressrooms in the process. If switching to factory-girl would help, I'm open to that.

I'd be grateful for any ideas on how to solve this.

2

There are 2 best solutions below

4
On

Are you using Machinist 1 or 2? These suggestions for Machinist 2 and may or may not work in Machinist 1. I can't remember how you do this in Machinist 1 (and can't be bothered to google!).

To do it in the way you're suggesting, you need to use object:

Pressroom.blueprint do
  source { Source.make :pressrooms => [object] }
  site { source.site }
end

But a much nicer way to do it is to take advantage of the fact Machinst knows about the models associations and just let it do its thing:

Pressroom.blueprint do
  source
  site { source.site }
end

Assuming your associations are setup correctly, that should work. See the Blueprints wiki page for more.

0
On

Googling around, I found some hints on http://webcrisps.wordpress.com/2009/08/13/stubbing-before_create-callbacks-in-a-machinist-blueprint/ – to stub the after_create :create_initial_pressroom! callback on Source, in the Source blueprint – using Machinist 2 and Mocha here:

Pressroom.blueprint do
  source { Source.make!(:without_initial_pressroom) }
  site   { object.source.site }
end

Source.blueprint do
  site
end

Source.blueprint(:without_initial_pressroom) do
  object.stubs(:create_initial_pressroom!).returns(true)
end

This way, Pressroom.make! works like it should, Source.make! works like it should, and... I guess I'm happy. But still a bit perplexed by the problems I ran into in the solution I tried above (both in machinist 1 and 2).

If anyone knows how to make this work with object, let me know. It'd be a lot cleaner, and besides, I generally don't like accepting my own answers here on stackoverflow.