Skip to content

Commit

Permalink
Factory evaluators use inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuaclayton committed Jan 9, 2012
1 parent ac1df1d commit f2e4138
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 14 deletions.
4 changes: 2 additions & 2 deletions lib/factory_girl/attribute_assigner.rb
Expand Up @@ -2,10 +2,10 @@

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

Expand Down
15 changes: 13 additions & 2 deletions lib/factory_girl/evaluator.rb
@@ -1,13 +1,24 @@
require "active_support/core_ext/class/attribute"

This comment has been minimized.

Copy link
@ngan

ngan Feb 3, 2012

Rails 2.3 does not have this...

I'm getting the following error:

activesupport-2.3.2/lib/active_support/dependencies.rb:158:in `require': no such file to load -- active_support/core_ext/class/attribute (MissingSourceFile)

This comment has been minimized.

Copy link
@joshuaclayton

joshuaclayton Feb 4, 2012

Author Contributor

Ah, it was introduced in 2.3.9. I'll update the gemspec to require that as a minimum.


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
Expand Down
13 changes: 11 additions & 2 deletions lib/factory_girl/evaluator_class_definer.rb
@@ -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
Expand Down
14 changes: 7 additions & 7 deletions lib/factory_girl/factory.rb
Expand Up @@ -40,8 +40,8 @@ def run(proxy_class, overrides, &block) #:nodoc:

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
Expand Down Expand Up @@ -104,6 +104,10 @@ def class_name #:nodoc:
@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|
Expand All @@ -120,7 +124,7 @@ def callbacks
private

def processing_order
[parent, traits.reverse, @definition].flatten
[traits.reverse, @definition].flatten
end

def assert_valid_options(options)
Expand All @@ -145,9 +149,5 @@ def initialize_copy(source)
super
@definition = @definition.clone
end

def evaluator_class_definer
@evaluator_class_definer ||= EvaluatorClassDefiner.new(attributes)
end
end
end
1 change: 1 addition & 0 deletions lib/factory_girl/null_factory.rb
Expand Up @@ -11,5 +11,6 @@ def initialize
def compile; end
def class_name; end
def default_strategy; :create; end
def evaluator_class; FactoryGirl::Evaluator; end
end
end
27 changes: 26 additions & 1 deletion spec/factory_girl/evaluator_class_definer_spec.rb
Expand Up @@ -4,9 +4,10 @@
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
Expand All @@ -26,4 +27,28 @@
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
1 change: 1 addition & 0 deletions spec/factory_girl/null_factory_spec.rb
Expand Up @@ -9,4 +9,5 @@
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

0 comments on commit f2e4138

Please sign in to comment.