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

Add AssociationRunner for running associations from strategies

This extracts logic for running factories based on name and either
strategy class, symbol representing a strategy, or nil (defaulting to
the create strategy)
This commit is contained in:
Joshua Clayton 2012-02-08 12:13:27 -05:00
parent b79a525c09
commit 7b2fbeac5c
9 changed files with 116 additions and 48 deletions

View file

@ -1,6 +1,7 @@
require "active_support/core_ext/module/delegation" require "active_support/core_ext/module/delegation"
require 'factory_girl/errors' require 'factory_girl/errors'
require 'factory_girl/association_runner'
require 'factory_girl/strategy' require 'factory_girl/strategy'
require 'factory_girl/registry' require 'factory_girl/registry'
require 'factory_girl/null_factory' require 'factory_girl/null_factory'

View file

@ -0,0 +1,47 @@
module FactoryGirl
class AssociationRunner
def initialize(factory_name)
@factory_name = factory_name
end
def run(strategy_name_or_object, overrides)
strategy = StrategyCalculator.new(strategy_name_or_object).strategy
factory.run(strategy, overrides.except(:method))
end
private
def factory
FactoryGirl.factory_by_name(@factory_name)
end
class StrategyCalculator
def initialize(name_or_object)
@name_or_object = name_or_object
end
def strategy
if strategy_is_object?
@name_or_object
else
strategy_name_to_object
end
end
private
def strategy_is_object?
@name_or_object.is_a?(Class) && @name_or_object.ancestors.include?(::FactoryGirl::Strategy)
end
def strategy_name_to_object
case @name_or_object
when :build then Strategy::Build
when :create then Strategy::Create
when nil then Strategy::Create
else raise "unrecognized method #{@name_or_object}"
end
end
end
end
end

View file

@ -29,7 +29,10 @@ module FactoryGirl
@build_strategy.add_observer(CallbackRunner.new(self.class.callbacks, self)) @build_strategy.add_observer(CallbackRunner.new(self.class.callbacks, self))
end end
delegate :association, :to => :@build_strategy def association(factory_name, overrides = {})
runner = AssociationRunner.new(factory_name)
@build_strategy.association(runner, overrides)
end
def instance=(object_instance) def instance=(object_instance)
@instance = object_instance @instance = object_instance

View file

@ -41,7 +41,7 @@ module FactoryGirl
# # author association, and saves the Post. # # author association, and saves the Post.
# FactoryGirl.create(:post) # FactoryGirl.create(:post)
# #
def association(name, overrides = {}) def association(runner, overrides)
end end
def result(attribute_assigner, to_create) def result(attribute_assigner, to_create)

View file

@ -1,9 +1,8 @@
module FactoryGirl module FactoryGirl
class Strategy #:nodoc: class Strategy #:nodoc:
class Build < Strategy #:nodoc: class Build < Strategy #:nodoc:
def association(factory_name, overrides = {}) def association(runner, overrides)
factory = FactoryGirl.factory_by_name(factory_name) runner.run(overrides[:method], overrides)
factory.run(get_method(overrides[:method]), overrides.except(:method))
end end
def result(attribute_assigner, to_create) def result(attribute_assigner, to_create)
@ -11,17 +10,6 @@ module FactoryGirl
run_callbacks(:after_build, result_instance) run_callbacks(:after_build, result_instance)
end end
end end
private
def get_method(method)
case method
when :build then Strategy::Build
when :create then Strategy::Create
when nil then Strategy::Create
else raise "unrecognized method #{method}"
end
end
end end
end end
end end

View file

@ -1,9 +1,8 @@
module FactoryGirl module FactoryGirl
class Strategy #:nodoc: class Strategy #:nodoc:
class Create < Strategy #:nodoc: class Create < Strategy #:nodoc:
def association(factory_name, overrides = {}) def association(runner, overrides)
factory = FactoryGirl.factory_by_name(factory_name) runner.run(overrides[:method], overrides)
factory.run(get_method(overrides[:method]), overrides.except(:method))
end end
def result(attribute_assigner, to_create) def result(attribute_assigner, to_create)
@ -13,17 +12,6 @@ module FactoryGirl
run_callbacks(:after_create, result_instance) run_callbacks(:after_create, result_instance)
end end
end end
private
def get_method(method)
case method
when :build then Strategy::Build
when :create then Strategy::Create
when nil then Strategy::Create
else raise "unrecognized method #{method}"
end
end
end end
end end
end end

View file

@ -3,9 +3,8 @@ module FactoryGirl
class Stub < Strategy #:nodoc: class Stub < Strategy #:nodoc:
@@next_id = 1000 @@next_id = 1000
def association(factory_name, overrides = {}) def association(runner, overrides)
factory = FactoryGirl.factory_by_name(factory_name) runner.run(Strategy::Stub, overrides)
factory.run(Strategy::Stub, overrides.except(:method))
end end
def result(attribute_assigner, to_create) def result(attribute_assigner, to_create)

View file

@ -0,0 +1,31 @@
require "spec_helper"
describe FactoryGirl::AssociationRunner do
let(:factory) { stub("factory", :run => instance) }
let(:instance) { stub("instance") }
before do
FactoryGirl.stubs(:factory_by_name => factory)
end
it "runs a strategy based on a factory name" do
FactoryGirl::AssociationRunner.new(:user).run(FactoryGirl::Strategy::Build, {})
factory.should have_received(:run).with(FactoryGirl::Strategy::Build, {})
end
it "strips only method from overrides" do
FactoryGirl::AssociationRunner.new(:user).run(FactoryGirl::Strategy::Build, { :method => :build, :name => "John" })
factory.should have_received(:run).with(FactoryGirl::Strategy::Build, { :name => "John" })
end
it "runs a strategy inferred by name based on a factory name" do
FactoryGirl::AssociationRunner.new(:user).run(:build, { :method => :build, :name => "John" })
factory.should have_received(:run).with(FactoryGirl::Strategy::Build, { :name => "John" })
end
it "raises if the strategy cannot be inferred" do
expect do
FactoryGirl::AssociationRunner.new(:user).run(:bogus_strategy, { :method => :build, :name => "John" })
end.to raise_error("unrecognized method bogus_strategy")
end
end

View file

@ -1,21 +1,29 @@
shared_examples_for "strategy without association support" do shared_examples_for "strategy without association support" do
let(:attribute) { FactoryGirl::Attribute::Association.new(:user, :user, {}) } let(:attribute) { FactoryGirl::Attribute::Association.new(:user, :user, {}) }
def association_named(name, overrides)
runner = FactoryGirl::AssociationRunner.new(name)
subject.association(runner, overrides)
end
it "returns nil when accessing an association" do it "returns nil when accessing an association" do
subject.association(:user, {}).should be_nil association_named(:user, {}).should be_nil
end end
it "does not attempt to look up the factory when accessing the association" do it "does not attempt to look up the factory when accessing the association" do
FactoryGirl.stubs(:factory_by_name) FactoryGirl.stubs(:factory_by_name)
subject.association(:awesome) association_named(:awesome, {})
FactoryGirl.should have_received(:factory_by_name).never FactoryGirl.should have_received(:factory_by_name).never
end end
end end
shared_examples_for "strategy with association support" do |factory_girl_strategy_class| shared_examples_for "strategy with association support" do |factory_girl_strategy_class|
let(:factory) { stub("associate_factory") } let(:factory) { stub("associate_factory") }
let(:overrides) { { :great => "value" } }
let(:factory_name) { :author } def association_named(name, overrides)
runner = FactoryGirl::AssociationRunner.new(name)
subject.association(runner, overrides)
end
before do before do
FactoryGirl.stubs(:factory_by_name => factory) FactoryGirl.stubs(:factory_by_name => factory)
@ -23,20 +31,23 @@ shared_examples_for "strategy with association support" do |factory_girl_strateg
end end
it "runs the factory with the correct overrides" do it "runs the factory with the correct overrides" do
subject.association(factory_name, overrides) association_named(:author, :great => "value")
factory.should have_received(:run).with(factory_girl_strategy_class, overrides) factory.should have_received(:run).with(factory_girl_strategy_class, :great => "value")
end end
it "finds the factory with the correct factory name" do it "finds the factory with the correct factory name" do
subject.association(factory_name, overrides) association_named(:author, :great => "value")
FactoryGirl.should have_received(:factory_by_name).with(factory_name) FactoryGirl.should have_received(:factory_by_name).with(:author)
end end
end end
shared_examples_for "strategy with :method => :build" do |factory_girl_strategy_class| shared_examples_for "strategy with :method => :build" do |factory_girl_strategy_class|
let(:factory) { stub("associate_factory") } let(:factory) { stub("associate_factory") }
let(:overrides) { { :method => :build, :great => "value" } }
let(:factory_name) { :author } def association_named(name, overrides)
runner = FactoryGirl::AssociationRunner.new(name)
subject.association(runner, overrides)
end
before do before do
FactoryGirl.stubs(:factory_by_name => factory) FactoryGirl.stubs(:factory_by_name => factory)
@ -44,13 +55,13 @@ shared_examples_for "strategy with :method => :build" do |factory_girl_strategy_
end end
it "runs the factory with the correct overrides" do it "runs the factory with the correct overrides" do
subject.association(factory_name, overrides) association_named(:author, :method => :build, :great => "value")
factory.should have_received(:run).with(factory_girl_strategy_class, { :great => "value" }) factory.should have_received(:run).with(factory_girl_strategy_class, { :great => "value" })
end end
it "finds the factory with the correct factory name" do it "finds the factory with the correct factory name" do
subject.association(factory_name, overrides) association_named(:author, :method => :build, :great => "value")
FactoryGirl.should have_received(:factory_by_name).with(factory_name) FactoryGirl.should have_received(:factory_by_name).with(:author)
end end
end end