From 0c17434b4a35256a20e5ce60559345e398f64721 Mon Sep 17 00:00:00 2001 From: Daniel Colson Date: Mon, 22 Oct 2018 22:29:02 -0400 Subject: [PATCH] Avoid recursive call to AttributeHash#attributes Fixes 1155 Defining an attribute called 'attributes', then referring to that attribute in `initialize_with` was causing `AttributeHash#attributes` to call itself recursively. --- lib/factory_bot/attribute_assigner.rb | 5 +++- lib/factory_bot/decorator/attribute_hash.rb | 2 +- spec/acceptance/initialize_with_spec.rb | 27 ++++++++++++++++--- .../decorator/attribute_hash_spec.rb | 23 ++++++++++++++++ 4 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 spec/factory_bot/decorator/attribute_hash_spec.rb diff --git a/lib/factory_bot/attribute_assigner.rb b/lib/factory_bot/attribute_assigner.rb index e96a64e..08490b7 100644 --- a/lib/factory_bot/attribute_assigner.rb +++ b/lib/factory_bot/attribute_assigner.rb @@ -31,7 +31,10 @@ module FactoryBot private def method_tracking_evaluator - @method_tracking_evaluator ||= Decorator::AttributeHash.new(decorated_evaluator, attribute_names_to_assign) + @method_tracking_evaluator ||= Decorator::AttributeHash.new( + decorated_evaluator, + attribute_names_to_assign, + ) end def decorated_evaluator diff --git a/lib/factory_bot/decorator/attribute_hash.rb b/lib/factory_bot/decorator/attribute_hash.rb index 62f9fed..3400ea9 100644 --- a/lib/factory_bot/decorator/attribute_hash.rb +++ b/lib/factory_bot/decorator/attribute_hash.rb @@ -8,7 +8,7 @@ module FactoryBot def attributes @attributes.each_with_object({}) do |attribute_name, result| - result[attribute_name] = send(attribute_name) + result[attribute_name] = @component.send(attribute_name) end end end diff --git a/spec/acceptance/initialize_with_spec.rb b/spec/acceptance/initialize_with_spec.rb index 36c583f..10d19a8 100644 --- a/spec/acceptance/initialize_with_spec.rb +++ b/spec/acceptance/initialize_with_spec.rb @@ -178,7 +178,7 @@ describe "initialize_with doesn't duplicate assignment on attributes accessed fr end describe "initialize_with has access to all attributes for construction" do - before do + it "assigns attributes correctly" do define_class("User") do attr_reader :name, :email, :ignored @@ -204,9 +204,7 @@ describe "initialize_with has access to all attributes for construction" do initialize_with { new(attributes) } end end - end - it "assigns attributes correctly" do user_with_attributes = FactoryBot.build(:user) expect(user_with_attributes.email).to eq "person1@example.com" expect(user_with_attributes.name).to eq "person1" @@ -214,6 +212,29 @@ describe "initialize_with has access to all attributes for construction" do end end +describe "initialize_with with an 'attributes' attribute" do + it "assigns attributes correctly" do + define_class("User") do + attr_reader :name + + def initialize(attributes:) + @name = attributes[:name] + end + end + + FactoryBot.define do + factory :user do + attributes { { name: "Daniel" } } + initialize_with { new(attributes) } + end + end + + user = FactoryBot.build(:user) + + expect(user.name).to eq("Daniel") + end +end + describe "initialize_with for a constructor that requires a block" do it "executes the block correctly" do define_class("Awesome") do diff --git a/spec/factory_bot/decorator/attribute_hash_spec.rb b/spec/factory_bot/decorator/attribute_hash_spec.rb new file mode 100644 index 0000000..5fe00dd --- /dev/null +++ b/spec/factory_bot/decorator/attribute_hash_spec.rb @@ -0,0 +1,23 @@ +describe FactoryBot::Decorator::AttributeHash do + describe "#attributes" do + it "returns a hash of attributes" do + attributes = { attribute_1: :value, attribute_2: :value } + component = double(:component, attributes) + + decorator = described_class.new(component, [:attribute_1, :attribute_2]) + + expect(decorator.attributes).to eq(attributes) + end + + context "with an attribute called 'attributes'" do + it "does not call itself recursively" do + attributes = { attributes: :value } + component = double(:component, attributes) + + decorator = described_class.new(component, [:attributes]) + + expect(decorator.attributes).to eq(attributes) + end + end + end +end