I'm using Ruby 2.1.1p76 and Rails 4.0.4 and the Fabrication gem.
Is it possible to refer to the object currently being fabricated?
I have a class Foo and a class Bar. I have fabricators for each. My problem is that each of class Foo and Bar contain a field that refers to the other class:
class Foo < ActiveRecord::Base
has_many :bars
belongs_to :current_bar, class_name: "Bar"
end
class Bar < ActiveRecord::Base
belongs_to: :foo
end
It's bothersome to have to fabricate one, then the other and then set the reference for the first in my specs:
let!( :foo ) { Fabricate( :foo ) }
let!( :bar ) { Fabricate( :bar, foo: foo ) }
before( :each ) do
foo.update( current_bar: bar )
end
I'd much rather just fabricate a Foo and have its current_bar fabricated and already referring to the Foo I'm fabricating. I've read through the fabrication gem documentation and I can't find any way that this is possible. I may just be overlooking it. Does anyone know of a way to accomplish this?
For completeness -- fabricators:
Fabricator( :foo ) do
current_bar nil
end
Fabricator( :bar ) do
foo
end
Yup. overlooked it in the documentation.
You can define them in your Fabricators as a block that optionally receives the object being fabricated and a hash of any transient attributes defined. As with anything that works in the Fabricator, you can also define them when you call Fabricate and they will work just like you’d expect. The callbacks are also stackable, meaning that you can declare multiple of the same type in a fabricator and they will not be clobbered when you inherit another fabricator.
Fabricator(:place) do
before_validation { |place, transients| place.geolocate! }
after_create { |place, transients| Fabricate(:restaurant, place: place) }
end
Also, in my case, I needed to use the after_save callback. I was able set the current_bar
on my foo
object inside the fabricator, but once in the spec, the current_bar
was still nil
. The update
method isn't available inside after_create (I'm new to ruby so I'm not sure why), but it is available inside after_save. Calling update got me going.
Fabricator(:foo) do
transient :current_bar_data
after_save { |foo, transients|
bar = Fabricate( :bar, foo: foo, bar_data: transients[ :current_bar_data ] )
foo.update( current_bar: bar )
}
current_bar nil
end
Now I can fabricate a foo
complete with current_bar
for my specs:
let!( :some_foo ) { Fabricate( :foo, current_bar_data: "some bar data" ) }