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:
parent
13d988cd98
commit
98e6c51866
8 changed files with 179 additions and 86 deletions
|
@ -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.
|
||||
#
|
||||
|
|
37
lib/factory_girl/aliases.rb
Normal file
37
lib/factory_girl/aliases.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
29
test/aliases_test.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue