Living on the edge isn’t easy.
You continuously risk that everything you rely on stops working.
NoMethodError: undefined method `configuration' for #<Rails::Application>
from rails/railties/lib/rails.rb:53:in `configuration'
from factory_girl-1.2.3/lib/factory_girl.rb:25:in `<top (required)>'
from rails/activesupport/lib/active_support/dependencies.rb:167:in `require'
from rails/activesupport/lib/active_support/dependencies.rb:167:in `block in require'
from rails/activesupport/lib/active_support/dependencies.rb:537:in `new_constants_in'
from rails/activesupport/lib/active_support/dependencies.rb:167:in `require'
from test/test_helper.rb:16
Et tu ,
factory_girl ?
The fix was easy enough1 , but
I started realized that I only really used a small fraction of what was
under the hood. I took a page out of
Ryan Davis ’
book ,
reordered the words, and wrote
Miniskirt to complement
minitest .
Miniskirt is factory_girl, relaxed. It’s the essential, with
some flare:
Blocks are avoidable with Ruby 1.9 string interpolation.
Avoid blocks if you can. Your models should handle their own logic.
f . email '%{first_name}.%{last_name}@example.com' # ...not...
f . email { | u | " #{ u . first_name } . #{ u . last_name } @example.com" }
Sequences are simpler (why bother with blocks?):
f . email 'person%d@example.com' # ...instead of...
f . sequence ( :email ) { | n | "person #{ n } @example.com" }
What’s missing?
Factory.attributes_for :model.
Call Factory.build(:model).attributes, instead.
Factory.stub.
Stub your models like you stub everything else.
Factory#association.
Almost as short, and explicit: f.user { Factory :user }.
Factory#callback.
I can’t think of a callback that should be on a factory but not the
model.
Here she is. All of her.
# Factory girl, relaxed.
#
# Factory.define :user do |f|
# f.login 'johndoe%d' # Sequence.
# f.email '%{login}@example.com' # Interpolate.
# f.password f.password_confirmation('foobar') # Chain.
# end
#
# Factory.define :post do |f|
# f.user { Factory :user } # Blocks, if you must.
# end
class Miniskirt < Struct . new ( :__klass__ )
undef_method * instance_methods . grep ( /^(?!__|object_id)/ ) # BlankerSlate.
@@factories = {} and private_class_method :new
class << self
def define ( name )
@@factories [ name = name . to_s ] = {} and yield new ( name )
end
def build ( name , attrs = {})
( name , n = name . to_s ) and ( m = name . classify . constantize ) . new do | rec |
attrs . stringify_keys! . reverse_update ( @@factories [ name ] ) . each do | k , v |
rec . send " #{ k } =" , case v when String # Sequence and interpolate.
v . sub ( /%\d*d/ ) { | d | d % n ||= m . maximum ( :id ) . to_i + 1 } % attrs % n
when Proc then v . call ( rec ) else v
end
end
end
end
def create ( name , attrs = {})
build ( name , attrs ) . tap { | record | record . save }
end
end
def method_missing ( name , value = nil , & block )
@@factories [ __klass__ ][ name ] = block || value
end
end
def Miniskirt ( name , attrs = {})
Miniskirt . create ( name , attrs )
end
Factory = Miniskirt
alias Factory Miniskirt
Not bad for under 30 LOC .
Miniskirt isn't for everyone. The slightly more conservative, more
backwards-compatible Minidress
may suit you better. Or maybe factory_girl, with all her bells
and whistles, fits you just
fine.2
Whatever you choose, though, remember: write less , and
write better .
Up and running:
# test/test_helper.rb
Rails :: Application . configuration . class_eval { alias configuration config }
require "factory_girl" # Now she loads just fine...
require "factories" # ...and the rest needs to load explicitly.
I like factory_girl, and may find myself
returning to her when my platform is a bit more stable, especially if she
picks up some of these interpolative tricks.