1
0
Fork 0
mirror of https://github.com/thoughtbot/factory_bot.git synced 2022-11-09 11:43:51 -05:00

#12 - added attribute aliases

This commit is contained in:
Joe Ferris 2008-07-30 15:47:12 -04:00
parent 13d988cd98
commit 98e6c51866
8 changed files with 179 additions and 86 deletions

View file

@ -3,6 +3,7 @@ require 'factory_girl/factory'
require 'factory_girl/attribute_proxy'
require 'factory_girl/attribute'
require 'factory_girl/sequence'
require 'factory_girl/aliases'
# Shortcut for Factory.create.
#

View file

@ -0,0 +1,37 @@
class Factory
cattr_accessor :aliases #:nodoc:
self.aliases = [
[/(.*)_id/, '\1'],
[/(.*)/, '\1_id']
]
# Defines a new alias for attributes
#
# Arguments:
# pattern: (Regexp)
# A pattern that will be matched against attributes when looking for
# aliases. Contents captured in the pattern can be used in the alias.
# replace: (String)
# The alias that results from the matched pattern. Captured strings can
# be insert like String#sub.
#
# Example:
#
# Factory.alias /(.*)_confirmation/, '\1'
def self.alias (pattern, replace)
self.aliases << [pattern, replace]
end
def self.aliases_for (attribute) #:nodoc:
aliases.collect do |params|
pattern, replace = *params
if pattern.match(attribute.to_s)
attribute.to_s.sub(pattern, replace).to_sym
else
nil
end
end.compact << attribute
end
end

View file

@ -1,8 +1,7 @@
class Factory
cattr_accessor :factories, :sequences #:nodoc:
cattr_accessor :factories #:nodoc:
self.factories = {}
self.sequences = {}
attr_reader :factory_name
@ -25,41 +24,6 @@ class Factory
self.factories[instance.factory_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 ||= class_for(@options[:class] || factory_name)
end
@ -221,8 +185,9 @@ class Factory
def build_attributes_hash (values, strategy)
values = values.symbolize_keys
passed_keys = values.keys.collect {|key| Factory.aliases_for(key) }.flatten
@attributes.each do |attribute|
unless values.key?(attribute.name)
unless passed_keys.include?(attribute.name)
proxy = AttributeProxy.new(self, attribute.name, strategy, values)
values[attribute.name] = attribute.value(proxy)
end

View file

@ -2,11 +2,12 @@ class Factory
class Sequence
def initialize (&proc)
def initialize (&proc) #:nodoc:
@proc = proc
@value = 0
end
# Returns the next value for this sequence
def next
@value += 1
@proc.call(@value)
@ -14,4 +15,42 @@ class Factory
end
cattr_accessor :sequences #:nodoc:
self.sequences = {}
# 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
end

29
test/aliases_test.rb Normal file
View file

@ -0,0 +1,29 @@
require(File.join(File.dirname(__FILE__), 'test_helper'))
class AliasesTest < Test::Unit::TestCase
should "include an attribute as an alias for itself by default" do
assert Factory.aliases_for(:test).include?(:test)
end
should "include the root of a foreign key as an alias by default" do
assert Factory.aliases_for(:test_id).include?(:test)
end
should "include an attribute's foreign key as an alias by default" do
assert Factory.aliases_for(:test).include?(:test_id)
end
context "after adding an alias" do
setup do
Factory.alias(/(.*)_suffix/, '\1')
end
should "return the alias in the aliases list" do
assert Factory.aliases_for(:test_suffix).include?(:test)
end
end
end

View file

@ -53,28 +53,6 @@ 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
@ -252,6 +230,24 @@ class FactoryTest < Test::Unit::TestCase
end
context "overriding an attribute with an alias" do
setup do
@factory.add_attribute(:test, 'original')
Factory.alias(/(.*)_alias/, '\1')
@result = @factory.attributes_for(:test_alias => 'new')
end
should "use the passed in value for the alias" do
assert_equal 'new', @result[:test_alias]
end
should "discard the predefined value for the attribute" do
assert_nil @result[:test]
end
end
should "guess the build class from the factory name" do
assert_equal User, @factory.build_class
end
@ -419,29 +415,4 @@ 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

@ -74,6 +74,10 @@ class IntegrationTest < Test::Unit::TestCase
assert @instance.author.new_record?
end
should "not assign both an association and its foreign key" do
assert_equal 1, Factory.build(:post, :author_id => 1).author_id
end
end
context "a created instance" do

View file

@ -26,4 +26,51 @@ class SequenceTest < 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 "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