From d918c1ddae0c2e0d181768934a82917303d10ca6 Mon Sep 17 00:00:00 2001 From: Joshua Clayton Date: Sat, 7 Jan 2012 22:13:38 -0500 Subject: [PATCH] Allow methods to be called from the instance in factory girl attributes This fixes a regression introduced with the introduction of the anonymous class. Closes #264 --- lib/factory_girl/attribute_assigner.rb | 11 ++++ lib/factory_girl/evaluator.rb | 6 ++- .../attributes_from_instance_spec.rb | 53 +++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 spec/acceptance/attributes_from_instance_spec.rb diff --git a/lib/factory_girl/attribute_assigner.rb b/lib/factory_girl/attribute_assigner.rb index b6e621929..c218df451 100644 --- a/lib/factory_girl/attribute_assigner.rb +++ b/lib/factory_girl/attribute_assigner.rb @@ -8,6 +8,7 @@ def initialize(build_class, evaluator, attribute_list) end def object + @evaluator.instance = build_class_instance build_class_instance.tap do |instance| attributes_to_set_on_instance.each do |attribute| instance.send("#{attribute}=", get(attribute)) @@ -17,6 +18,8 @@ def object end def hash + @evaluator.instance = null_object.new + attributes_to_set_on_hash.inject({}) do |result, attribute| result[attribute] = get(attribute) result @@ -29,6 +32,14 @@ def build_class_instance @build_class_instance ||= @build_class.new end + def null_object + Class.new(BasicObject) do + def method_missing(*args) + nil + end + end + end + def get(attribute_name) @evaluator.send(attribute_name) end diff --git a/lib/factory_girl/evaluator.rb b/lib/factory_girl/evaluator.rb index 2298ea75e..108360056 100644 --- a/lib/factory_girl/evaluator.rb +++ b/lib/factory_girl/evaluator.rb @@ -12,11 +12,15 @@ def initialize(build_strategy, overrides = {}, callbacks = []) delegate :association, :to => :@build_strategy + def instance=(object_instance) + @instance = object_instance + end + def method_missing(method_name, *args, &block) if @cached_attributes.key?(method_name) @cached_attributes[method_name] else - super + @instance.send(method_name, *args, &block) end end diff --git a/spec/acceptance/attributes_from_instance_spec.rb b/spec/acceptance/attributes_from_instance_spec.rb new file mode 100644 index 000000000..b1c46f82b --- /dev/null +++ b/spec/acceptance/attributes_from_instance_spec.rb @@ -0,0 +1,53 @@ +require "spec_helper" + +describe "calling methods on the model instance" do + before do + define_model('User', :age => :integer, :age_copy => :integer) do + def age + read_attribute(:age) || 18 + end + end + + FactoryGirl.define do + factory :user do + age_copy { age } + end + end + end + + context "without the attribute being overridden" do + it "returns the correct value from the instance" do + FactoryGirl.build(:user).age_copy.should == 18 + end + + it "returns nil during attributes_for" do + FactoryGirl.attributes_for(:user)[:age_copy].should be_nil + end + + it "doesn't instantiate a record with attributes_for" do + User.stubs(:new) + FactoryGirl.attributes_for(:user) + User.should have_received(:new).never + end + end + + context "with the attribute being overridden" do + it "uses the overridden value" do + FactoryGirl.build(:user, :age_copy => nil).age_copy.should be_nil + end + + it "uses the overridden value during attributes_for" do + FactoryGirl.attributes_for(:user, :age_copy => 25)[:age_copy].should == 25 + end + end + + context "with the referenced attribute being overridden" do + it "uses the overridden value" do + FactoryGirl.build(:user, :age => nil).age_copy.should be_nil + end + + it "uses the overridden value during attributes_for" do + FactoryGirl.attributes_for(:user, :age => 25)[:age_copy].should == 25 + end + end +end