mirror of
https://github.com/thoughtbot/factory_bot.git
synced 2022-11-09 11:43:51 -05:00
Overrides become methods defined on each instance of the evaluator
There were some big issues with trying to undefine specific methods on the Evaluator. After investigating maybe inheriting from BasicObject (or ActiveSupport::BasicObject since BasicObject is 1.9+), that turned out to be too much of a pain because it undefines almost everything, including class and a handful of other methods necessary for Evaluator to work properly. The second solution was to undefine all private methods. The problem is, when other libraries defining methods (private or otherwise) on Object are loaded *after* factory girl, those methods get added and Evaluator sees those methods on Object. So, that solution didn't directly work either. This commit removes undefining methods (the sole reason of which was to capture with method_missing and process ourselves, returning the override or cached value) and instead introduces a new concept - iterating over each override and defining it as a method on the evaluator INSTANCE. This means that overrides don't collide because they're on the instance and we don't have to worry about undefining methods so that method_missing kicks in. This is the most stable and guaranteed way to get this to work because the overrides are applied to each instance at runtime. Closes #279, #285
This commit is contained in:
parent
850116da11
commit
f50550c1f0
2 changed files with 56 additions and 1 deletions
|
@ -11,13 +11,21 @@ module FactoryGirl
|
|||
end
|
||||
end
|
||||
end
|
||||
undef_method(:id) if method_defined?(:id)
|
||||
|
||||
private_instance_methods.each do |method|
|
||||
undef_method(method) unless method =~ /^__|initialize/
|
||||
end
|
||||
|
||||
def initialize(build_strategy, overrides = {})
|
||||
@build_strategy = build_strategy
|
||||
@overrides = overrides
|
||||
@cached_attributes = overrides
|
||||
|
||||
singleton = class << self; self end
|
||||
@overrides.each do |name, value|
|
||||
singleton.send :define_method, name, lambda { value }
|
||||
end
|
||||
|
||||
@build_strategy.add_observer(CallbackRunner.new(self.class.callbacks, self))
|
||||
end
|
||||
|
||||
|
|
|
@ -19,3 +19,50 @@ describe "declaring attributes on a Factory that are private methods on Object"
|
|||
its(:link) { should == "http://example.com" }
|
||||
its(:sleep) { should == -5 }
|
||||
end
|
||||
|
||||
describe "assigning overrides that are also private methods on object" do
|
||||
before do
|
||||
define_model("Website", :format => :string, :y => :integer, :more_format => :string, :some_funky_method => :string)
|
||||
|
||||
Object.class_eval do
|
||||
private
|
||||
def some_funky_method(args)
|
||||
end
|
||||
end
|
||||
|
||||
FactoryGirl.define do
|
||||
factory :website do
|
||||
more_format { "format: #{format}" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Object.send(:undef_method, :some_funky_method)
|
||||
end
|
||||
|
||||
subject { FactoryGirl.build(:website, :format => "Great", :y => 12345, :some_funky_method => "foobar!") }
|
||||
its(:format) { should == "Great" }
|
||||
its(:y) { should == 12345 }
|
||||
its(:more_format) { should == "format: Great" }
|
||||
its(:some_funky_method) { should == "foobar!" }
|
||||
end
|
||||
|
||||
describe "accessing methods from the instance within a dynamic attribute that is also a private method on object" do
|
||||
before do
|
||||
define_model("Website", :more_format => :string) do
|
||||
def format
|
||||
"This is an awesome format"
|
||||
end
|
||||
end
|
||||
|
||||
FactoryGirl.define do
|
||||
factory :website do
|
||||
more_format { "format: #{format}" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
subject { FactoryGirl.build(:website) }
|
||||
its(:more_format) { should == "format: This is an awesome format" }
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue