##Intro
Generator frameworks spend way too much time creating obscure and pointless DSL syntax just to make the code trendy and cute. This generator comes in the form of pure ruby, instantiates real model objects (which fire their pre-, post- and other hooks appropriately), and still allows for a simple syntax to override parameters of generated objects.
Standard Ruby syntax allows for easier adoption by new programmers and reinforces ruby paradigms, instead of breaking them down with a cute, often incomplete, and entirely superfluous DSL syntax.
Specific complaints against existing frameworks:
- Rails fixtures: definition syntax (yaml) doesn't provide a powerful enough interface to rapidly creating objects.
- FactoryGirl: Factories are evaluated at load-time, making some factories just awful to define. No further comment on DSLs.
- Machinist: See FactoryGirl
- Fabrication: Where do I start. Requiring a full block to override object attributes at test-time is an indicator of over-engineering. Requiring a block within that block to set a variable equal to a value is downright excessive. Finally, what on earth would give you the idea that you need to animate your entire website?!
###A test directory might look like this:
- test/
- generate/ <---- folder which contains generators, autoladed by generate.rb
address.rb
line_item.rb
order.rb
pcb.rb
user.rb
- integration/ <---- a few folders containing tests
- mailers/
- models/
- support/
generate.rb <---- from this gist
test_helper.rb
###In test helper: require 'generate'
###Next, in a generate file (generate/whatever.rb):
class Generate
def self.address
a = Address.new
a.first_name = "Elwin"
a.last_name = "Ransom"
a.street_1 = "1 Malacandra Street"
a.street_2 = "Hyoi's Water Dwelling n"
a.city = "Malacandra"
a.country = Country.usa
a.province = a.country.provinces[ rand(a.country.provinces.count)]
a.postal_code = '80019'
a
end
def self.domestic_address
a = Generate.new :address
a.first_name = "Jonathan"
a.last_name = "Edwards"
a.street_1 = '409 Prospect Street'
a.city = 'New Haven'
a.postal_code = '06511'
a.province_id = 14 #CT
a.country_id = 223 #USA
a
end
def self.international_address
a = Generate.new :address
a.first_name = 'Martin'
a.last_name = 'Luther'
a.street_1 = 'Friedrichstrasse 1a'
a.city = 'Lutherstadt Wittenberg'
a.postal_code = 'D-06886'
a.country = Country.find_by_name('Germany')
a
end
end
###Lastly, in your tests: @password = '010203'
# Using the default generator code:
@other_user = Generate.create(:user)
# The hash keys indicate the names of functions to be called, and the values their parameter.
# If 'key=' is found as a function, it'll call that instead of 'key'.
@user = Generate.create(:user, new_password: @password, new_password_confirmation: @password)
###A note about sequences:
I've provided a simple construct to create named sequences which allow for creating, for example, email addresses which do not collide in the database. From within any subclass of Generate, you can easily call sequence(:sequence_name)
to fetch a number. By default, a new sequence is initialized to zero. All sequences increment by one at each call.
Additionally, you can specify a number you want to initialize the sequence to as the second parameter: sequence(:email_counter, 32767)
. Use sparingly.
The Generate&sequence
method is publicly available should you desire to use it directly in a test.
###Further usage explanation:
Find on page 'advanced'...or, you know, scroll down.
###Contact
If for some reason you want to contact me with some question or comment on the contents of this gist, feel free to email me.