Fabrication

Github Repo: http://www.github.com/paulelliott/fabrication

One of my takeaways from RailsConf this year was inspiration to write a new gem. I was sitting at a fast testing round table discussion and people kept bringing up the use of build versus create in factories.

That gave me the idea to lazily generate activerecord associations. Doing that allows you to define your entire object graph in your Fabricators and only generate the pieces you need for the spec you are running. When the association is first accessed, the Fabricators will generate the defined objects, persist them to the database, and return the set as if they were there to begin with. While I was at it, I figured it would be handy to be able to generate regular old ruby objects and Mongoid documents, so I baked in support for doing that as well!

Let’s say we have a Person object that is just a regular old class in Ruby. We can define a Fabricator with static or evaluated attribute values as seen below. You can then Fabricate as many as you want and override specific fields as you go.


class Person; attr_accessor :first_name, :last_name, :age, :friends end

Fabricator(:person) do
  first_name “Joe”
  last_name { Faker::Name.last_name }
  age { rand(100) }
  friends(:count => 10) { |index| Fabricate(:person) }
end

person = Fabricate(:person, :last_name => “Schmoe”)

Notice that you don’t need to pass a parameter to the block. I figured that would just be extra and unnecessary syntax.

Also note the :count parameter. When generating a collection, this allows you to tell Fabrication how many of the object you want it to generate. It will automatically build that many and return an array of the new objects. The block receives the index of the generation as a parameter in case you need it.

If we want to get a little bit more fancy, we can generate active record models as well.

class Company < ActiveRecord::Base
  has_many :divisions
  belongs_to :location
end

Fabricator(:company) do
  divisions(:count => 3) { |company, index| Fabricate(:division, :company => company) }
  after_create { |company| company.update_attribute(:location, Fabricate(:location) }
end

company = Fabricate(:company)
# At this point, we have a location but no divisions.
Division.count
# => 0, because they haven’t been created.
company.divisions.any?
# => true, because the divisions get created when the “getter” for the association is called.
Division.count
# => 3, because now they exist!

Of course, with great power comes great responsibility. This feature is very handy but can also be very dangerous. It is something you need to keep in mind as you are writing your specs.

And finally, you can generate Mongoid Documents very easily.


class Author
  include Mongoid::Document
  field :name
  field :books => Array
end

Fabricator(:author) do
  name { Faker::Name.name }
  books(:count => 5) { Fabricate(:book) }
end

author = Fabricate(:author)

It is that simple! I have been successfully using this in a new Rails 3/Mongoid project I am working on. It won’t step on the toes of other frameworks, so you can also use it alongside your existing factory_girl or machinist implementation.

Please check it out, try it out in your projects, and let me know what you think! Also report any bugs or feature requests you find!

Sorry, Everyone!

I really messed up recently and I want to apologize to anyone who may have been misinformed or mislead by me. I have no idea how I came to think this nugget of unwisdom that I have been repeating. I must have read an article on it somewhere that was either wrong or maybe I just misunderstood and then began to propagate some misinformation. I have no one to blame but myself though and I feel really terrible and embarrassed about it.

It came out today during Hashrocket's live book club. I was explaining an issue I found in a codebase today where they were using contexts in jQuery to scope selectors. I was explaining that when you pass in a jQuery object as a context, the selector function ignores this parameter as it is expecting a raw DOM object.

I was called out during my monologue and as it turns out, I was dead wrong about it. One of the folks sitting in the circle looked up the jQuery API to get some more context for what I was talking about and noticed that the docs said something contrary to what I was saying. I tested it after the book club and of course it works just like the api docs say it does.

It gets even worse because I have talked about this in numerous blog posts, presentations, and conversations with other developers. The good news is that what I was saying you should do is valid, so anyone listening to me would still be writing working code.

The real problem here is that I took something on faith and didn't look it up or research it for myself. The takeaway is that reading the source code for libraries is critical. Not only will you understand exactly how the code we depend on every day works, but I guarantee you will learn something. I failed to do that in this instance and it caused quite a problem. At the very least, read the API docs before you go running your mouth.

So, to set the record straight, when you are using a context in jQuery you can pass in a raw DOM element, a jQuery object, or a Document and it will work as you expect.

Again, I am deeply sorry for confusion this has caused.