Skip to content
This repository
Browse code

Factory evaluators use inheritance

  • Loading branch information...
commit f2e41389ea3f8fd455d7a14a0241c6a1ce8d801a 1 parent ac1df1d
Joshua Clayton joshuaclayton authored
4 lib/factory_girl/attribute_assigner.rb
@@ -2,10 +2,10 @@
2 2
3 3 module FactoryGirl
4 4 class AttributeAssigner
5   - def initialize(build_class, evaluator, attribute_list)
  5 + def initialize(build_class, evaluator)
6 6 @build_class = build_class
7 7 @evaluator = evaluator
8   - @attribute_list = attribute_list
  8 + @attribute_list = evaluator.class.attribute_list
9 9 @attribute_names_assigned = []
10 10 end
11 11
15 lib/factory_girl/evaluator.rb
... ... @@ -1,13 +1,24 @@
  1 +require "active_support/core_ext/class/attribute"
  2 +
1 3 module FactoryGirl
2 4 class Evaluator
  5 + class_attribute :callbacks, :attribute_lists
  6 +
  7 + def self.attribute_list
  8 + AttributeList.new.tap do |list|
  9 + attribute_lists.each do |attribute_list|
  10 + list.apply_attributes attribute_list.to_a
  11 + end
  12 + end
  13 + end
3 14 undef_method(:id) if method_defined?(:id)
4 15
5   - def initialize(build_strategy, overrides = {}, callbacks = [])
  16 + def initialize(build_strategy, overrides = {})
6 17 @build_strategy = build_strategy
7 18 @overrides = overrides
8 19 @cached_attributes = overrides
9 20
10   - @build_strategy.add_observer(CallbackRunner.new(callbacks, self))
  21 + @build_strategy.add_observer(CallbackRunner.new(self.class.callbacks, self))
11 22 end
12 23
13 24 delegate :association, :to => :@build_strategy
13 lib/factory_girl/evaluator_class_definer.rb
... ... @@ -1,13 +1,22 @@
1 1 module FactoryGirl
2 2 class EvaluatorClassDefiner
3   - def initialize(attributes)
  3 + def initialize(attributes, callbacks, parent_class)
  4 + @parent_class = parent_class
  5 + @callbacks = callbacks
  6 + @attributes = attributes
  7 +
4 8 attributes.each do |attribute|
5 9 define_attribute(attribute.name, attribute.to_proc)
6 10 end
7 11 end
8 12
9 13 def evaluator_class
10   - @evaluator_class ||= Class.new(FactoryGirl::Evaluator)
  14 + @evaluator_class ||= Class.new(@parent_class).tap do |klass|
  15 + klass.callbacks ||= []
  16 + klass.callbacks += @callbacks
  17 + klass.attribute_lists ||= []
  18 + klass.attribute_lists += [@attributes]
  19 + end
11 20 end
12 21
13 22 private
14 lib/factory_girl/factory.rb
@@ -40,8 +40,8 @@ def run(proxy_class, overrides, &block) #:nodoc:
40 40
41 41 proxy = proxy_class.new
42 42
43   - evaluator = evaluator_class_definer.evaluator_class.new(proxy, overrides.symbolize_keys, callbacks)
44   - attribute_assigner = AttributeAssigner.new(build_class, evaluator, attributes)
  43 + evaluator = evaluator_class.new(proxy, overrides.symbolize_keys)
  44 + attribute_assigner = AttributeAssigner.new(build_class, evaluator)
45 45
46 46 proxy.result(attribute_assigner, to_create).tap(&block)
47 47 end
@@ -104,6 +104,10 @@ def class_name #:nodoc:
104 104 @class_name || parent.class_name || name
105 105 end
106 106
  107 + def evaluator_class
  108 + @evaluator_class ||= EvaluatorClassDefiner.new(attributes, callbacks, parent.evaluator_class).evaluator_class
  109 + end
  110 +
107 111 def attributes
108 112 compile
109 113 AttributeList.new(@name).tap do |list|
@@ -120,7 +124,7 @@ def callbacks
120 124 private
121 125
122 126 def processing_order
123   - [parent, traits.reverse, @definition].flatten
  127 + [traits.reverse, @definition].flatten
124 128 end
125 129
126 130 def assert_valid_options(options)
@@ -145,9 +149,5 @@ def initialize_copy(source)
145 149 super
146 150 @definition = @definition.clone
147 151 end
148   -
149   - def evaluator_class_definer
150   - @evaluator_class_definer ||= EvaluatorClassDefiner.new(attributes)
151   - end
152 152 end
153 153 end
1  lib/factory_girl/null_factory.rb
@@ -11,5 +11,6 @@ def initialize
11 11 def compile; end
12 12 def class_name; end
13 13 def default_strategy; :create; end
  14 + def evaluator_class; FactoryGirl::Evaluator; end
14 15 end
15 16 end
27 spec/factory_girl/evaluator_class_definer_spec.rb
@@ -4,9 +4,10 @@
4 4 let(:simple_attribute) { stub("simple attribute", :name => :simple, :to_proc => lambda { 1 }) }
5 5 let(:relative_attribute) { stub("relative attribute", :name => :relative, :to_proc => lambda { simple + 1 }) }
6 6 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 }) }
  7 + let(:callbacks) { [stub("callback 1"), stub("callback 2")] }
7 8
8 9 let(:attributes) { [simple_attribute, relative_attribute, attribute_that_raises_a_second_time] }
9   - let(:class_definer) { FactoryGirl::EvaluatorClassDefiner.new(attributes) }
  10 + let(:class_definer) { FactoryGirl::EvaluatorClassDefiner.new(attributes, callbacks, FactoryGirl::Evaluator) }
10 11 let(:evaluator) { class_definer.evaluator_class.new(stub("build strategy", :add_observer => true)) }
11 12
12 13 it "returns an evaluator when accessing the evaluator class" do
@@ -26,4 +27,28 @@
26 27 2.times { evaluator.raises_without_proper_cache }
27 28 }.to_not raise_error
28 29 end
  30 +
  31 + it "sets attributes on the evaluator class" do
  32 + class_definer.evaluator_class.attribute_lists.should == [attributes]
  33 + end
  34 +
  35 + it "sets callbacks on the evaluator class" do
  36 + class_definer.evaluator_class.callbacks.should == callbacks
  37 + end
  38 +
  39 + context "with a custom evaluator as a parent class" do
  40 + let(:child_callbacks) { [stub("child callback 1"), stub("child callback 2")] }
  41 + let(:child_attributes) { [stub("child attribute", :name => :simple, :to_proc => lambda { 1 })] }
  42 + let(:child_definer) { FactoryGirl::EvaluatorClassDefiner.new(child_attributes, child_callbacks, class_definer.evaluator_class) }
  43 +
  44 + subject { child_definer.evaluator_class }
  45 +
  46 + it "bases its attribute lists on itself and its parent evaluator" do
  47 + subject.attribute_lists.should == [attributes, child_attributes]
  48 + end
  49 +
  50 + it "bases its callbacks on itself and its parent evaluator" do
  51 + subject.callbacks.should == callbacks + child_callbacks
  52 + end
  53 + end
29 54 end
1  spec/factory_girl/null_factory_spec.rb
@@ -9,4 +9,5 @@
9 9 its(:class_name) { should be_nil }
10 10 its(:default_strategy) { should == :create }
11 11 its(:attributes) { should be_an_instance_of(FactoryGirl::AttributeList) }
  12 + its(:evaluator_class) { should == FactoryGirl::Evaluator }
12 13 end

0 comments on commit f2e4138

Ngan Pham

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)
Joshua Clayton

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

Please sign in to comment.
Something went wrong with that request. Please try again.