Added support for sequences

This commit is contained in:
Joe Ferris 2008-06-01 10:46:50 -07:00
parent 6ee6c0282b
commit fa36b1da4a
7 changed files with 181 additions and 2 deletions

17
README
View File

@ -71,6 +71,23 @@ When using the association method, the same build strategy (build, create, or at
post.new_record? # => true
post.author.new_record # => true
== Sequences
Unique values in a specific format (for example, e-mail addresses) can be
generated using sequences. Sequences are defined by calling Factory.sequence,
and values in a sequence are generated by calling Factory.next:
# Defines a new sequence
Factory.sequence :email do |n|
"person#{n}@example.com"
end
Factory.next :email
# => "person1@example.com"
Factory.next :email
# => "person2@example.com"
== Using factories
# Build and save a User instance

View File

@ -1,6 +1,7 @@
require 'activesupport'
require 'factory_girl/factory'
require 'factory_girl/attribute_proxy'
require 'factory_girl/sequence'
# Shortcut for Factory.create.
#

View File

@ -1,7 +1,8 @@
class Factory
cattr_accessor :factories #:nodoc:
cattr_accessor :factories, :sequences #:nodoc:
self.factories = {}
self.sequences = {}
attr_reader :name
@ -24,6 +25,41 @@ class Factory
self.factories[name] = instance
end
# Defines a new sequence that can be used to generate unique values in a specific format.
#
# Arguments:
# name: (Symbol)
# A unique name for this sequence. This name will be referenced when
# calling next to generate new values from this sequence.
# block: (Proc)
# The code to generate each value in the sequence. This block will be
# called with a unique number each time a value in the sequence is to be
# generated. The block should return the generated value for the
# sequence.
#
# Example:
#
# Factory.sequence(:email) {|n| "somebody_#{n}@example.com" }
def self.sequence (name, &block)
self.sequences[name] = Sequence.new(&block)
end
# Generates and returns the next value in a sequence.
#
# Arguments:
# name: (Symbol)
# The name of the sequence that a value should be generated for.
#
# Returns:
# The next value in the sequence. (Object)
def self.next (sequence)
unless self.sequences.key?(sequence)
raise "No such sequence: #{sequence}"
end
self.sequences[sequence].next
end
def build_class #:nodoc:
@build_class ||= @options[:class] || name.to_s.classify.constantize
end

View File

@ -0,0 +1,17 @@
class Factory
class Sequence
def initialize (&proc)
@proc = proc
@value = 0
end
def next
@value += 1
@proc.call(@value)
end
end
end

View File

@ -52,6 +52,28 @@ class FactoryTest < Test::Unit::TestCase
end
context "defining a sequence" do
setup do
@sequence = mock('sequence')
@name = :count
Factory::Sequence.stubs(:new).returns(@sequence)
end
should "create a new sequence" do
Factory::Sequence.expects(:new).with().returns(@sequence)
Factory.sequence(@name)
end
should "use the supplied block as the sequence generator" do
Factory::Sequence.stubs(:new).yields(1)
yielded = false
Factory.sequence(@name) {|n| yielded = true }
assert yielded
end
end
context "a factory" do
setup do
@ -283,4 +305,29 @@ class FactoryTest < Test::Unit::TestCase
end
context "after defining a sequence" do
setup do
@sequence = mock('sequence')
@name = :test
@value = '1 2 5'
@sequence. stubs(:next).returns(@value)
Factory::Sequence.stubs(:new). returns(@sequence)
Factory.sequence(@name) {}
end
should "call next on the sequence when sent next" do
@sequence.expects(:next)
Factory.next(@name)
end
should "return the value from the sequence" do
assert_equal @value, Factory.next(@name)
end
end
end

View File

@ -19,7 +19,11 @@ class IntegrationTest < Test::Unit::TestCase
f.first_name 'Ben'
f.last_name 'Stein'
f.admin true
f.email {|a| "#{a.first_name}.#{a.last_name}@example.com".downcase }
f.email { Factory.next(:email) }
end
Factory.sequence :email do |n|
"somebody#{n}@example.com"
end
end
@ -108,4 +112,32 @@ class IntegrationTest < Test::Unit::TestCase
end
context "an attribute generated by a sequence" do
setup do
@email = Factory.attributes_for(:admin)[:email]
end
should "match the correct format" do
assert_match /^somebody\d+@example\.com$/, @email
end
context "after the attribute has already been generated once" do
setup do
@another_email = Factory.attributes_for(:admin)[:email]
end
should "match the correct format" do
assert_match /^somebody\d+@example\.com$/, @email
end
should "not be the same as the first generated value" do
assert_not_equal @email, @another_email
end
end
end
end

29
test/sequence_test.rb Normal file
View File

@ -0,0 +1,29 @@
require(File.join(File.dirname(__FILE__), 'test_helper'))
class SequenceTest < Test::Unit::TestCase
context "a sequence" do
setup do
@sequence = Factory::Sequence.new {|n| "=#{n}" }
end
should "start with a value of 1" do
assert_equal "=1", @sequence.next
end
context "after being called" do
setup do
@sequence.next
end
should "use the next value" do
assert_equal "=2", @sequence.next
end
end
end
end