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

Factory evaluators use inheritance

This commit is contained in:
Joshua Clayton 2012-01-03 09:31:40 -05:00
parent ac1df1da3b
commit f2e41389ea
7 changed files with 61 additions and 14 deletions

View file

@ -2,10 +2,10 @@ require "active_support/basic_object"
module FactoryGirl
class AttributeAssigner
def initialize(build_class, evaluator, attribute_list)
def initialize(build_class, evaluator)
@build_class = build_class
@evaluator = evaluator
@attribute_list = attribute_list
@attribute_list = evaluator.class.attribute_list
@attribute_names_assigned = []
end

View file

@ -1,13 +1,24 @@
require "active_support/core_ext/class/attribute"
module FactoryGirl
class Evaluator
class_attribute :callbacks, :attribute_lists
def self.attribute_list
AttributeList.new.tap do |list|
attribute_lists.each do |attribute_list|
list.apply_attributes attribute_list.to_a
end
end
end
undef_method(:id) if method_defined?(:id)
def initialize(build_strategy, overrides = {}, callbacks = [])
def initialize(build_strategy, overrides = {})
@build_strategy = build_strategy
@overrides = overrides
@cached_attributes = overrides
@build_strategy.add_observer(CallbackRunner.new(callbacks, self))
@build_strategy.add_observer(CallbackRunner.new(self.class.callbacks, self))
end
delegate :association, :to => :@build_strategy

View file

@ -1,13 +1,22 @@
module FactoryGirl
class EvaluatorClassDefiner
def initialize(attributes)
def initialize(attributes, callbacks, parent_class)
@parent_class = parent_class
@callbacks = callbacks
@attributes = attributes
attributes.each do |attribute|
define_attribute(attribute.name, attribute.to_proc)
end
end
def evaluator_class
@evaluator_class ||= Class.new(FactoryGirl::Evaluator)
@evaluator_class ||= Class.new(@parent_class).tap do |klass|
klass.callbacks ||= []
klass.callbacks += @callbacks
klass.attribute_lists ||= []
klass.attribute_lists += [@attributes]
end
end
private

View file

@ -40,8 +40,8 @@ module FactoryGirl
proxy = proxy_class.new
evaluator = evaluator_class_definer.evaluator_class.new(proxy, overrides.symbolize_keys, callbacks)
attribute_assigner = AttributeAssigner.new(build_class, evaluator, attributes)
evaluator = evaluator_class.new(proxy, overrides.symbolize_keys)
attribute_assigner = AttributeAssigner.new(build_class, evaluator)
proxy.result(attribute_assigner, to_create).tap(&block)
end
@ -104,6 +104,10 @@ module FactoryGirl
@class_name || parent.class_name || name
end
def evaluator_class
@evaluator_class ||= EvaluatorClassDefiner.new(attributes, callbacks, parent.evaluator_class).evaluator_class
end
def attributes
compile
AttributeList.new(@name).tap do |list|
@ -120,7 +124,7 @@ module FactoryGirl
private
def processing_order
[parent, traits.reverse, @definition].flatten
[traits.reverse, @definition].flatten
end
def assert_valid_options(options)
@ -145,9 +149,5 @@ module FactoryGirl
super
@definition = @definition.clone
end
def evaluator_class_definer
@evaluator_class_definer ||= EvaluatorClassDefiner.new(attributes)
end
end
end

View file

@ -11,5 +11,6 @@ module FactoryGirl
def compile; end
def class_name; end
def default_strategy; :create; end
def evaluator_class; FactoryGirl::Evaluator; end
end
end

View file

@ -4,9 +4,10 @@ describe FactoryGirl::EvaluatorClassDefiner do
let(:simple_attribute) { stub("simple attribute", :name => :simple, :to_proc => lambda { 1 }) }
let(:relative_attribute) { stub("relative attribute", :name => :relative, :to_proc => lambda { simple + 1 }) }
let(:attribute_that_raises_a_second_time) { stub("attribute that would raise without a cache", :name => :raises_without_proper_cache, :to_proc => lambda { raise "failed" if @run; @run = true; nil }) }
let(:callbacks) { [stub("callback 1"), stub("callback 2")] }
let(:attributes) { [simple_attribute, relative_attribute, attribute_that_raises_a_second_time] }
let(:class_definer) { FactoryGirl::EvaluatorClassDefiner.new(attributes) }
let(:class_definer) { FactoryGirl::EvaluatorClassDefiner.new(attributes, callbacks, FactoryGirl::Evaluator) }
let(:evaluator) { class_definer.evaluator_class.new(stub("build strategy", :add_observer => true)) }
it "returns an evaluator when accessing the evaluator class" do
@ -26,4 +27,28 @@ describe FactoryGirl::EvaluatorClassDefiner do
2.times { evaluator.raises_without_proper_cache }
}.to_not raise_error
end
it "sets attributes on the evaluator class" do
class_definer.evaluator_class.attribute_lists.should == [attributes]
end
it "sets callbacks on the evaluator class" do
class_definer.evaluator_class.callbacks.should == callbacks
end
context "with a custom evaluator as a parent class" do
let(:child_callbacks) { [stub("child callback 1"), stub("child callback 2")] }
let(:child_attributes) { [stub("child attribute", :name => :simple, :to_proc => lambda { 1 })] }
let(:child_definer) { FactoryGirl::EvaluatorClassDefiner.new(child_attributes, child_callbacks, class_definer.evaluator_class) }
subject { child_definer.evaluator_class }
it "bases its attribute lists on itself and its parent evaluator" do
subject.attribute_lists.should == [attributes, child_attributes]
end
it "bases its callbacks on itself and its parent evaluator" do
subject.callbacks.should == callbacks + child_callbacks
end
end
end

View file

@ -9,4 +9,5 @@ describe FactoryGirl::NullFactory do
its(:class_name) { should be_nil }
its(:default_strategy) { should == :create }
its(:attributes) { should be_an_instance_of(FactoryGirl::AttributeList) }
its(:evaluator_class) { should == FactoryGirl::Evaluator }
end