From edf9faeb28db6165f6e86df78493aacfc745f08a Mon Sep 17 00:00:00 2001 From: Mohammed Nasser <135416851+mohammednasser-32@users.noreply.github.com> Date: Sun, 28 Apr 2024 22:44:04 +0300 Subject: [PATCH 1/3] add_before_build_callbcak --- lib/factory_bot/strategy/build.rb | 1 + lib/factory_bot/strategy/create.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/factory_bot/strategy/build.rb b/lib/factory_bot/strategy/build.rb index d18bb8ff..45720acb 100644 --- a/lib/factory_bot/strategy/build.rb +++ b/lib/factory_bot/strategy/build.rb @@ -7,6 +7,7 @@ def association(runner) def result(evaluation) evaluation.object.tap do |instance| + evaluation.notify(:before_build, instance) evaluation.notify(:after_build, instance) end end diff --git a/lib/factory_bot/strategy/create.rb b/lib/factory_bot/strategy/create.rb index c9371f42..a0ea8586 100644 --- a/lib/factory_bot/strategy/create.rb +++ b/lib/factory_bot/strategy/create.rb @@ -7,6 +7,7 @@ def association(runner) def result(evaluation) evaluation.object.tap do |instance| + evaluation.notify(:before_build, instance) evaluation.notify(:after_build, instance) evaluation.notify(:before_create, instance) evaluation.create(instance) From 6a63eeae3ce486e86f4ef9585d662cfa18fe8af7 Mon Sep 17 00:00:00 2001 From: Mohammed Nasser <135416851+mohammednasser-32@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:55:56 +0300 Subject: [PATCH 2/3] fix before_build_callback change the approach for calling the before_build_callback, now it is called before the object is built, and thus called without any attributes --- lib/factory_bot/callback.rb | 8 ++++++++ lib/factory_bot/factory.rb | 7 +++++++ lib/factory_bot/strategy/build.rb | 1 - lib/factory_bot/strategy/create.rb | 1 - spec/acceptance/callbacks_spec.rb | 30 ++++++++++++++++++++++++++++++ spec/factory_bot/callback_spec.rb | 11 +++++++++++ 6 files changed, 56 insertions(+), 2 deletions(-) diff --git a/lib/factory_bot/callback.rb b/lib/factory_bot/callback.rb index 1766cc80..c11f0716 100644 --- a/lib/factory_bot/callback.rb +++ b/lib/factory_bot/callback.rb @@ -7,6 +7,10 @@ def initialize(name, block) @block = block end + def run_before_build + syntax_runner.instance_exec(&block) + end + def run(instance, evaluator) case block.arity when 1, -1, -2 then syntax_runner.instance_exec(instance, &block) @@ -20,6 +24,10 @@ def ==(other) block == other.block end + def before_build? + name == :before_build + end + protected attr_reader :block diff --git a/lib/factory_bot/factory.rb b/lib/factory_bot/factory.rb index 7f445c88..fd94e5d0 100644 --- a/lib/factory_bot/factory.rb +++ b/lib/factory_bot/factory.rb @@ -32,6 +32,7 @@ def build_class def run(build_strategy, overrides, &block) block ||= ->(result) { result } compile + run_before_build_callbacks strategy = StrategyCalculator.new(build_strategy).strategy.new @@ -139,6 +140,12 @@ def compiled_constructor hierarchy_instance.constructor end + def run_before_build_callbacks + callbacks.each do |callback| + callback.run_before_build if callback.before_build? + end + end + private def assert_valid_options(options) diff --git a/lib/factory_bot/strategy/build.rb b/lib/factory_bot/strategy/build.rb index 45720acb..d18bb8ff 100644 --- a/lib/factory_bot/strategy/build.rb +++ b/lib/factory_bot/strategy/build.rb @@ -7,7 +7,6 @@ def association(runner) def result(evaluation) evaluation.object.tap do |instance| - evaluation.notify(:before_build, instance) evaluation.notify(:after_build, instance) end end diff --git a/lib/factory_bot/strategy/create.rb b/lib/factory_bot/strategy/create.rb index a0ea8586..c9371f42 100644 --- a/lib/factory_bot/strategy/create.rb +++ b/lib/factory_bot/strategy/create.rb @@ -7,7 +7,6 @@ def association(runner) def result(evaluation) evaluation.object.tap do |instance| - evaluation.notify(:before_build, instance) evaluation.notify(:after_build, instance) evaluation.notify(:before_create, instance) evaluation.create(instance) diff --git a/spec/acceptance/callbacks_spec.rb b/spec/acceptance/callbacks_spec.rb index 57d2471a..2dc3e0b0 100644 --- a/spec/acceptance/callbacks_spec.rb +++ b/spec/acceptance/callbacks_spec.rb @@ -249,3 +249,33 @@ def name expect(build(:company).name).to eq "ACME SUPPLIERS" end end + +describe "before build callback" do + class TitleSetter + def self.title=(new_title) + @@title = new_title + end + + def self.title + @@title + end + end + + before do + define_model("Article", title: :string) + + FactoryBot.define do + factory :article_with_before_callbacks, class: :article do + before(:build) { TitleSetter.title = "title from before build" } + after(:build) { TitleSetter.title = "title from after build" } + + title { TitleSetter.title } + end + end + end + + it "runs the before callback" do + article = FactoryBot.build(:article_with_before_callbacks) + expect(article.title).to eq("title from before build") + end +end diff --git a/spec/factory_bot/callback_spec.rb b/spec/factory_bot/callback_spec.rb index 238d7d8a..2ccbb934 100644 --- a/spec/factory_bot/callback_spec.rb +++ b/spec/factory_bot/callback_spec.rb @@ -24,4 +24,15 @@ FactoryBot::Callback.new(:after_create, ->(one, two) { ran_with = [one, two] }).run(:one, :two) expect(ran_with).to eq [:one, :two] end + + it "runs run_before_build callback without attributes" do + ran_with = nil + FactoryBot::Callback.new(:before_build, -> { ran_with = "before build" }).run_before_build + expect(ran_with).to eq "before build" + end + + it "#before_build?" do + expect(FactoryBot::Callback.new(:before_build, -> {}).before_build?).to be true + expect(FactoryBot::Callback.new(:after_create, -> {}).before_build?).to be false + end end From 35db09fde3a473c09f36c06ab46c66e3b4c4ae01 Mon Sep 17 00:00:00 2001 From: mohammednasser-32 Date: Fri, 30 Aug 2024 23:28:05 +0300 Subject: [PATCH 3/3] fix rubocop offenses --- spec/acceptance/callbacks_spec.rb | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/spec/acceptance/callbacks_spec.rb b/spec/acceptance/callbacks_spec.rb index 2dc3e0b0..bd3a9973 100644 --- a/spec/acceptance/callbacks_spec.rb +++ b/spec/acceptance/callbacks_spec.rb @@ -251,29 +251,29 @@ def name end describe "before build callback" do - class TitleSetter - def self.title=(new_title) - @@title = new_title - end - - def self.title - @@title - end - end - before do + define_class("TitleSetter") do + def self.title=(new_title) + class_variable_set(:@@title, new_title) + end + + def self.title + class_variable_get(:@@title) + end + end + define_model("Article", title: :string) - + FactoryBot.define do factory :article_with_before_callbacks, class: :article do before(:build) { TitleSetter.title = "title from before build" } after(:build) { TitleSetter.title = "title from after build" } - + title { TitleSetter.title } end end end - + it "runs the before callback" do article = FactoryBot.build(:article_with_before_callbacks) expect(article.title).to eq("title from before build")