Permalink
Browse files

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)
  • Loading branch information...
joshuaclayton committed Feb 8, 2012
1 parent b79a525 commit 7b2fbeac5c8c44016e7c49c0379e51401adbddaf
View
@@ -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'
@@ -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
@@ -29,7 +29,10 @@ def initialize(build_strategy, overrides = {})
@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
@@ -41,7 +41,7 @@ class Strategy #:nodoc:
# # author association, and saves the Post.
# FactoryGirl.create(:post)
#
- def association(name, overrides = {})
+ def association(runner, overrides)
end
def result(attribute_assigner, to_create)
@@ -1,27 +1,15 @@
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)
attribute_assigner.object.tap do |result_instance|
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
@@ -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 @@ def result(attribute_assigner, to_create)
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
@@ -3,9 +3,8 @@ class Strategy
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)
@@ -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
@@ -1,56 +1,67 @@
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)
factory.stubs(:run)
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)
factory.stubs(:run)
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

0 comments on commit 7b2fbea

Please sign in to comment.