Class Factory: Factory_girl-like syntax for dynamically creating Ruby classes

Class Factory will dynamically create classes using a factories model similar to factory_girl. But instead of passing a block with model attributes into the factory definition, you pass in a migration defining the attributes of the new model class you want to create:

ClassFactory.define :person do |p|
  p.string  :first_name
  p.string  :last_name
  p.integer :age
end


Now when you need a “Person” class in your tests you create one like this:

ClassFactory :person
=> Person(id: integer, first_name: string, last_name: string, age: integer)


This can be useful if you're writing tests for a gem or plugin and don't want to load the entire Rails environment, or have access to existing models in a target application. By default Class Factory creates ActiveRecord model classes, but using the :super option you can create any sort of Ruby class. Class Factory also makes it easy for each of your tests to use a different variation on a target class. For example, this will delete the Person model we created above, and create a new Person model that belongs to a group:

ClassFactory :person, :class_eval => 'belongs_to :group' do |p|
  p.string  :first_name
  p.string  :middle_name
  p.string  :last_name
  p.string  :group_id
end
=> Person(id: integer, first_name: string, middle_name: string, last_name: string,
   group_id: string)


Creating different variations of the same class can be useful if you're writing tests for a generator, plugin or some other code which has different behavior depending on what classes you run it against.

Options

Default: create a new ActiveRecord model, along with a corresponding table in your database:

ClassFactory :person


Execute a migration on the new table specified as a block, defining the attributes of the new model class:

ClassFactory :person do |p|
  p.string  :first_name
  p.string  :last_name
  p.integer :age
end


Create a class with a specified superclass (default is ActiveRecord::Base):

ClassFactory :person_array, :super => Array


If the super class is not a subclass of ActiveRecord::Base then Class Factory won't create a table or run a migration. You can use this to create plain Ruby object classes.

Create a class called “DifferentClass” instead of “Person:”

ClassFactory :person, :class => 'DifferentClass'


Run the given code inside the new class using class_eval:

ClassFactory :person, :class_eval => 'has_many :shoes'


Create a table with the given name, instead of a table called “people:”

ClassFactory :person, :class_eval => 'set_table_name :table_name',
             :table => 'table_name'


If you provide options when the factory is defined they will be applied to each class created with the factory. You can also provide options when you create a class, in which case they will override the factory options.

Install

gem install class_factory


Code

http://github.com/patshaughnessy/class_factory


Detailed Example

Start an irb session and require class_factory (this will also require active_record):

$ irb
> require 'rubygems'
=> true
> require 'class_factory'
=> true


Create an in-memory SQLite test database:

> ActiveRecord::Base.establish_connection({ :adapter => 'sqlite3',
  :database => ':memory:' })
=> #<ActiveRecord::ConnectionAdapters::ConnectionPool:0x19cfecc...


Define a person class factory, and create a Person class:

> ClassFactory.define :person do |p|
>   p.string  :first_name
>   p.string  :last_name
>   p.integer :age
> end
=> #<ClassFactory:0x19c6fac @definition={:name=>:person, :migration=>...
> ClassFactory :person
=> Person(id: integer, first_name: string, last_name: string, age: integer)


Now create an instance of a Person and count how many records we have in our test database:

> Person.create :first_name => 'Barack', :last_name => 'Obama', :age => 48
=> #<Person id: 1, first_name: "Barack", last_name: "Obama", age: 48>
> Person.count
=> 1


Next redefine the Person class and override the options set in the factory above; this time it will belong to a group. Note that the existing people table will be dropped and a new, empty people table created:

> ClassFactory :person, :class_eval => 'belongs_to :group' do |p|
>   p.string  :first_name
>   p.string  :middle_name
>   p.string  :last_name
>   p.string  :group_id
> end
=> Person(id: integer, first_name: string, middle_name: string, last_name: string,
   group_id: string)
> Person.count
=> 0


Create a group class that has many people:

> ClassFactory.define :group, :class_eval => 'has_many :people' do |g|
>   g.string :name
> end
=> #<ClassFactory:0x18a05d8 @definition={:class_eval=>"has_many :people", ...
> ClassFactory :group
=> Group(id: integer, name: string)


Finally, recreate the Barack person record and add him to the “presidents” group:

> g = Group.create :name => 'Presidents'
=> #<Group id: 1, name: "Presidents">
> p = Person.new :first_name => 'Barack', :last_name => 'Obama', :age => 48,
  :group => g
=> #<Person id: nil, first_name: "Barack", last_name: "Obama", group_id: 1,
            age: 48>
> p.save
=> true
> g.people
=> [#<Person id: 1, first_name: "Barack", last_name: "Obama", group_id: "1",
   age: 48>]