From 7b2fbeac5c8c44016e7c49c0379e51401adbddaf Mon Sep 17 00:00:00 2001 From: Joshua Clayton Date: Wed, 8 Feb 2012 12:13:27 -0500 Subject: [PATCH] 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) --- lib/factory_girl.rb | 1 + lib/factory_girl/association_runner.rb | 47 ++++++++++++++++++++ lib/factory_girl/evaluator.rb | 5 ++- lib/factory_girl/strategy.rb | 2 +- lib/factory_girl/strategy/build.rb | 16 +------ lib/factory_girl/strategy/create.rb | 16 +------ lib/factory_girl/strategy/stub.rb | 5 +-- spec/factory_girl/association_runner_spec.rb | 31 +++++++++++++ spec/support/shared_examples/strategy.rb | 41 ++++++++++------- 9 files changed, 116 insertions(+), 48 deletions(-) create mode 100644 lib/factory_girl/association_runner.rb create mode 100644 spec/factory_girl/association_runner_spec.rb diff --git a/lib/factory_girl.rb b/lib/factory_girl.rb index a658110..43a88de 100644 --- a/lib/factory_girl.rb +++ b/lib/factory_girl.rb @@ -1,6 +1,7 @@ require "active_support/core_ext/module/delegation" require 'factory_girl/errors' +require 'factory_girl/association_runner' require 'factory_girl/strategy' require 'factory_girl/registry' require 'factory_girl/null_factory' diff --git a/lib/factory_girl/association_runner.rb b/lib/factory_girl/association_runner.rb new file mode 100644 index 0000000..fb27b4e --- /dev/null +++ b/lib/factory_girl/association_runner.rb @@ -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 diff --git a/lib/factory_girl/evaluator.rb b/lib/factory_girl/evaluator.rb index 8d55378..1dbd83d 100644 --- a/lib/factory_girl/evaluator.rb +++ b/lib/factory_girl/evaluator.rb @@ -29,7 +29,10 @@ module FactoryGirl @build_strategy.add_observer(CallbackRunner.new(self.class.callbacks, self)) 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) @instance = object_instance diff --git a/lib/factory_girl/strategy.rb b/lib/factory_girl/strategy.rb index 5dc2c14..1c38fc5 100644 --- a/lib/factory_girl/strategy.rb +++ b/lib/factory_girl/strategy.rb @@ -41,7 +41,7 @@ module FactoryGirl # # author association, and saves the Post. # FactoryGirl.create(:post) # - def association(name, overrides = {}) + def association(runner, overrides) end def result(attribute_assigner, to_create) diff --git a/lib/factory_girl/strategy/build.rb b/lib/factory_girl/strategy/build.rb index 625a865..54e5b49 100644 --- a/lib/factory_girl/strategy/build.rb +++ b/lib/factory_girl/strategy/build.rb @@ -1,9 +1,8 @@ module FactoryGirl class Strategy #:nodoc: class Build < Strategy #:nodoc: - def association(factory_name, overrides = {}) - factory = FactoryGirl.factory_by_name(factory_name) - factory.run(get_method(overrides[:method]), overrides.except(:method)) + def association(runner, overrides) + runner.run(overrides[:method], overrides) end def result(attribute_assigner, to_create) @@ -11,17 +10,6 @@ module FactoryGirl run_callbacks(:after_build, result_instance) 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 diff --git a/lib/factory_girl/strategy/create.rb b/lib/factory_girl/strategy/create.rb index f74849d..0d8d3f5 100644 --- a/lib/factory_girl/strategy/create.rb +++ b/lib/factory_girl/strategy/create.rb @@ -1,9 +1,8 @@ module FactoryGirl class Strategy #:nodoc: class Create < Strategy #:nodoc: - def association(factory_name, overrides = {}) - factory = FactoryGirl.factory_by_name(factory_name) - factory.run(get_method(overrides[:method]), overrides.except(:method)) + def association(runner, overrides) + runner.run(overrides[:method], overrides) end def result(attribute_assigner, to_create) @@ -13,17 +12,6 @@ module FactoryGirl run_callbacks(:after_create, result_instance) 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 diff --git a/lib/factory_girl/strategy/stub.rb b/lib/factory_girl/strategy/stub.rb index 36dd4d2..b51ae92 100644 --- a/lib/factory_girl/strategy/stub.rb +++ b/lib/factory_girl/strategy/stub.rb @@ -3,9 +3,8 @@ module FactoryGirl class Stub < Strategy #:nodoc: @@next_id = 1000 - def association(factory_name, overrides = {}) - factory = FactoryGirl.factory_by_name(factory_name) - factory.run(Strategy::Stub, overrides.except(:method)) + def association(runner, overrides) + runner.run(Strategy::Stub, overrides) end def result(attribute_assigner, to_create) diff --git a/spec/factory_girl/association_runner_spec.rb b/spec/factory_girl/association_runner_spec.rb new file mode 100644 index 0000000..23bc7df --- /dev/null +++ b/spec/factory_girl/association_runner_spec.rb @@ -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 diff --git a/spec/support/shared_examples/strategy.rb b/spec/support/shared_examples/strategy.rb index 9f6d1f3..3d6a478 100644 --- a/spec/support/shared_examples/strategy.rb +++ b/spec/support/shared_examples/strategy.rb @@ -1,21 +1,29 @@ shared_examples_for "strategy without association support" do 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 - subject.association(:user, {}).should be_nil + association_named(:user, {}).should be_nil end it "does not attempt to look up the factory when accessing the association" do FactoryGirl.stubs(:factory_by_name) - subject.association(:awesome) + association_named(:awesome, {}) FactoryGirl.should have_received(:factory_by_name).never end end shared_examples_for "strategy with association support" do |factory_girl_strategy_class| - let(:factory) { stub("associate_factory") } - let(:overrides) { { :great => "value" } } - let(:factory_name) { :author } + let(:factory) { stub("associate_factory") } + + def association_named(name, overrides) + runner = FactoryGirl::AssociationRunner.new(name) + subject.association(runner, overrides) + end before do FactoryGirl.stubs(:factory_by_name => factory) @@ -23,20 +31,23 @@ shared_examples_for "strategy with association support" do |factory_girl_strateg end it "runs the factory with the correct overrides" do - subject.association(factory_name, overrides) - factory.should have_received(:run).with(factory_girl_strategy_class, overrides) + association_named(:author, :great => "value") + factory.should have_received(:run).with(factory_girl_strategy_class, :great => "value") end it "finds the factory with the correct factory name" do - subject.association(factory_name, overrides) - FactoryGirl.should have_received(:factory_by_name).with(factory_name) + association_named(:author, :great => "value") + FactoryGirl.should have_received(:factory_by_name).with(:author) end end shared_examples_for "strategy with :method => :build" do |factory_girl_strategy_class| - let(:factory) { stub("associate_factory") } - let(:overrides) { { :method => :build, :great => "value" } } - let(:factory_name) { :author } + let(:factory) { stub("associate_factory") } + + def association_named(name, overrides) + runner = FactoryGirl::AssociationRunner.new(name) + subject.association(runner, overrides) + end before do FactoryGirl.stubs(:factory_by_name => factory) @@ -44,13 +55,13 @@ shared_examples_for "strategy with :method => :build" do |factory_girl_strategy_ end 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" }) end it "finds the factory with the correct factory name" do - subject.association(factory_name, overrides) - FactoryGirl.should have_received(:factory_by_name).with(factory_name) + association_named(:author, :method => :build, :great => "value") + FactoryGirl.should have_received(:factory_by_name).with(:author) end end