diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md index c928ed8..3396eab 100644 --- a/GETTING_STARTED.md +++ b/GETTING_STARTED.md @@ -207,7 +207,7 @@ factory :user do name { "John Doe#{" - Rockstar" if rockstar}" } email { "#{name.downcase}@example.com" } - after_create do |user, evaluator| + after(:create) do |user, evaluator| user.name.upcase! if evaluator.upcased end end @@ -299,11 +299,11 @@ FactoryGirl.define do posts_count 5 end - # the after_create yields two values; the user instance itself and the + # the after(:create) yields two values; the user instance itself and the # evaluator, which stores all values from the factory, including ignored # attributes; `create_list`'s second argument is the number of records # to create and we make sure the user is associated properly to the post - after_create do |user, evaluator| + after(:create) do |user, evaluator| FactoryGirl.create_list(:post, evaluator.posts_count, user: user) end end @@ -589,18 +589,19 @@ FactoryGirl.create_list(:user, 3, :admin, :male, name: "Jon Snow") Callbacks --------- -factory\_girl makes available three callbacks for injecting some code: +factory\_girl makes available four callbacks for injecting some code: -* after_build - called after a factory is built (via FactoryGirl.build) -* after_create - called after a factory is saved (via FactoryGirl.create) -* after_stub - called after a factory is stubbed (via FactoryGirl.build_stubbed) +* after(:build) - called after a factory is built (via `FactoryGirl.build`, `FactoryGirl.create`) +* before(:create) - called before a factory is saved (via `FactoryGirl.create`) +* after(:create) - called after a factory is saved (via `FactoryGirl.create`) +* after(:stub) - called after a factory is stubbed (via `FactoryGirl.build_stubbed`) Examples: ```ruby # Define a factory that calls the generate_hashed_password method after it is built factory :user do - after_build { |user| generate_hashed_password(user) } + after(:build) { |user| generate_hashed_password(user) } end ``` @@ -610,8 +611,8 @@ You can also define multiple types of callbacks on the same factory: ```ruby factory :user do - after_build { |user| do_something_to(user) } - after_create { |user| do_something_else_to(user) } + after(:build) { |user| do_something_to(user) } + after(:create) { |user| do_something_else_to(user) } end ``` @@ -619,12 +620,12 @@ Factories can also define any number of the same kind of callback. These callba ```ruby factory :user do - after_create { this_runs_first } - after_create { then_this } + after(:create) { this_runs_first } + after(:create) { then_this } end ``` -Calling FactoryGirl.create will invoke both after\_build and after\_create callbacks. +Calling FactoryGirl.create will invoke both `after_build` and `after_create` callbacks. Also, like standard attributes, child factories will inherit (and can also define) callbacks from their parent factory. @@ -677,7 +678,7 @@ When modifying a factory, you can change any of the attributes you want (aside f `FactoryGirl.modify` must be called outside of a `FactoryGirl.define` block as it operates on factories differently. A caveat: you can only modify factories (not sequences or traits) and callbacks *still compound as they normally would*. So, if -the factory you're modifying defines an `after_create` callback, you defining an `after_create` won't override it, it'll just get run after the first callback. +the factory you're modifying defines an `after(:create)` callback, you defining an `after(:create)` won't override it, it'll just get run after the first callback. Building or Creating Multiple Records ------------------------------------- @@ -810,6 +811,41 @@ FactoryGirl.json(:user) Finally, you can override factory\_girl's own strategies if you'd like by registering a new object in place of the strategies. +Custom Callbacks +---------------- + +Custom callbacks can be defined if you're using custom strategies: + +```ruby +class JsonStrategy + def initialize + @strategy = FactoryGirl.strategy_by_name(:create).new + end + + delegate :association, to: :@strategy + + def result(evaluation) + result = @strategy.result(evaluation) + evaluation.notify(:before_json, result) + + result.to_json.tap do |json| + evaluation.notify(:after_json, json) + evaluation.notify(:make_json_awesome, json) + end + end +end + +FactoryGirl.register_strategy(:json, JsonStrategy) + +FactoryGirl.define do + factory :user do + before(:json) {|user| do_something_to(user) } + after(:json) {|user_json| do_something_to(user_json) } + callback(:make_json_awesome) {|user_json| do_something_to(user_json) } + end +end +``` + Custom Methods to Persist Objects --------------------------------- diff --git a/lib/factory_girl.rb b/lib/factory_girl.rb index de3570a..7807c83 100644 --- a/lib/factory_girl.rb +++ b/lib/factory_girl.rb @@ -1,3 +1,4 @@ +require "set" require "active_support/core_ext/module/delegation" require "active_support/notifications" @@ -103,9 +104,26 @@ module FactoryGirl FactoryGirl.register_strategy(:null, FactoryGirl::Strategy::Null) end + def self.callbacks + @callbacks ||= Set.new + end + + def self.register_default_callbacks + register_callback(:after_build) + register_callback(:after_create) + register_callback(:after_stub) + register_callback(:before_create) + end + def self.callback_names - [:after_build, :after_create, :after_stub, :before_create].freeze + callbacks + end + + def self.register_callback(name) + name = name.to_sym + callbacks << name end end FactoryGirl.register_default_strategies +FactoryGirl.register_default_callbacks diff --git a/lib/factory_girl/definition_proxy.rb b/lib/factory_girl/definition_proxy.rb index f8ab78e..73da977 100644 --- a/lib/factory_girl/definition_proxy.rb +++ b/lib/factory_girl/definition_proxy.rb @@ -159,5 +159,18 @@ module FactoryGirl def initialize_with(&block) @definition.define_constructor(&block) end + + def before(name, &block) + callback("before_#{name}", &block) + end + + def after(name, &block) + callback("after_#{name}", &block) + end + + def callback(name, &block) + FactoryGirl.register_callback(name) + @definition.add_callback(Callback.new(name, block)) + end end end diff --git a/spec/acceptance/activesupport_instrumentation_spec.rb b/spec/acceptance/activesupport_instrumentation_spec.rb index 1999f31..64b2f31 100644 --- a/spec/acceptance/activesupport_instrumentation_spec.rb +++ b/spec/acceptance/activesupport_instrumentation_spec.rb @@ -21,7 +21,7 @@ describe "using ActiveSupport::Instrumentation to track factory interaction" do email "john@example.com" factory :slow_user do - after_build { Kernel.sleep(0.1) } + after(:build) { Kernel.sleep(0.1) } end end diff --git a/spec/acceptance/callbacks_spec.rb b/spec/acceptance/callbacks_spec.rb index d0f1d4a..e790217 100644 --- a/spec/acceptance/callbacks_spec.rb +++ b/spec/acceptance/callbacks_spec.rb @@ -6,35 +6,35 @@ describe "callbacks" do FactoryGirl.define do factory :user_with_callbacks, class: :user do - after_stub { |user| user.first_name = 'Stubby' } - after_build { |user| user.first_name = 'Buildy' } - after_create { |user| user.last_name = 'Createy' } + after(:stub) { |user| user.first_name = 'Stubby' } + after(:build) { |user| user.first_name = 'Buildy' } + after(:create) { |user| user.last_name = 'Createy' } end factory :user_with_inherited_callbacks, parent: :user_with_callbacks do - after_stub { |user| user.last_name = 'Double-Stubby' } - after_build { |user| user.first_name = 'Child-Buildy' } + after(:stub) { |user| user.last_name = 'Double-Stubby' } + after(:build) { |user| user.first_name = 'Child-Buildy' } end end end - it "runs the after_stub callback when stubbing" do + it "runs the after(:stub) callback when stubbing" do user = FactoryGirl.build_stubbed(:user_with_callbacks) user.first_name.should == 'Stubby' end - it "runs the after_build callback when building" do + it "runs the after(:build) callback when building" do user = FactoryGirl.build(:user_with_callbacks) user.first_name.should == 'Buildy' end - it "runs both the after_build and after_create callbacks when creating" do + it "runs both the after(:build) and after(:create) callbacks when creating" do user = FactoryGirl.create(:user_with_callbacks) user.first_name.should == 'Buildy' user.last_name.should == 'Createy' end - it "runs both the after_stub callback on the factory and the inherited after_stub callback" do + it "runs both the after(:stub) callback on the factory and the inherited after(:stub) callback" do user = FactoryGirl.build_stubbed(:user_with_inherited_callbacks) user.first_name.should == 'Stubby' user.last_name.should == 'Double-Stubby' @@ -56,9 +56,9 @@ describe "callbacks using syntax methods without referencing FactoryGirl explici sequence(:sequence_3) factory :user do - after_stub { generate(:sequence_3) } - after_build {|user| user.first_name = generate(:sequence_1) } - after_create {|user, evaluator| user.last_name = generate(:sequence_2) } + after(:stub) { generate(:sequence_3) } + after(:build) {|user| user.first_name = generate(:sequence_1) } + after(:create) {|user, evaluator| user.last_name = generate(:sequence_2) } end end end @@ -76,3 +76,76 @@ describe "callbacks using syntax methods without referencing FactoryGirl explici FactoryGirl.create(:user).last_name.should == 1 end end + +describe "custom callbacks" do + let(:custom_before) do + Class.new do + def result(evaluation) + evaluation.object.tap do |instance| + evaluation.notify(:before_custom, instance) + end + end + end + end + + let(:custom_after) do + Class.new do + def result(evaluation) + evaluation.object.tap do |instance| + evaluation.notify(:after_custom, instance) + end + end + end + end + + let(:totally_custom) do + Class.new do + def result(evaluation) + evaluation.object.tap do |instance| + evaluation.notify(:totally_custom, instance) + end + end + end + end + + before do + define_model("User", first_name: :string, last_name: :string) do + def name + [first_name, last_name].join(" ") + end + end + + FactoryGirl.register_strategy(:custom_before, custom_before) + FactoryGirl.register_strategy(:custom_after, custom_after) + FactoryGirl.register_strategy(:totally_custom, totally_custom) + + FactoryGirl.define do + factory :user do + first_name "John" + last_name "Doe" + + before(:custom) {|instance| instance.first_name = "Overridden First" } + after(:custom) {|instance| instance.last_name = "Overridden Last" } + callback(:totally_custom) do |instance| + instance.first_name = "Totally" + instance.last_name = "Custom" + end + end + end + end + + it "runs a custom before callback when the proper strategy executes" do + FactoryGirl.build(:user).name.should == "John Doe" + FactoryGirl.custom_before(:user).name.should == "Overridden First Doe" + end + + it "runs a custom after callback when the proper strategy executes" do + FactoryGirl.build(:user).name.should == "John Doe" + FactoryGirl.custom_after(:user).name.should == "John Overridden Last" + end + + it "runs a custom callback without prepending before or after when the proper strategy executes" do + FactoryGirl.build(:user).name.should == "John Doe" + FactoryGirl.totally_custom(:user).name.should == "Totally Custom" + end +end diff --git a/spec/acceptance/create_list_spec.rb b/spec/acceptance/create_list_spec.rb index 5686ae5..ea57fdd 100644 --- a/spec/acceptance/create_list_spec.rb +++ b/spec/acceptance/create_list_spec.rb @@ -64,7 +64,7 @@ describe "multiple creates and ignored attributes to dynamically build attribute posts_count 5 end - after_create do |user, evaluator| + after(:create) do |user, evaluator| FactoryGirl.create_list(:post, evaluator.posts_count, user: user) end end diff --git a/spec/acceptance/modify_factories_spec.rb b/spec/acceptance/modify_factories_spec.rb index f1c7786..3151aa3 100644 --- a/spec/acceptance/modify_factories_spec.rb +++ b/spec/acceptance/modify_factories_spec.rb @@ -12,7 +12,7 @@ describe "modifying factories" do factory :user do email - after_create do |user| + after(:create) do |user| user.login = user.name.upcase if user.name end @@ -58,7 +58,7 @@ describe "modifying factories" do FactoryGirl.modify do factory :user do name "Great User" - after_create do |user| + after(:create) do |user| user.name = user.name.downcase user.login = nil end diff --git a/spec/acceptance/parent_spec.rb b/spec/acceptance/parent_spec.rb index bd05287..9e9de00 100644 --- a/spec/acceptance/parent_spec.rb +++ b/spec/acceptance/parent_spec.rb @@ -78,7 +78,7 @@ describe "nested factories with different parents" do end factory :uppercase_male_user, parent: :male_user do - after_build {|user| user.name = user.name.upcase } + after(:build) {|user| user.name = user.name.upcase } end end end diff --git a/spec/acceptance/traits_spec.rb b/spec/acceptance/traits_spec.rb index 0e5a473..666168c 100644 --- a/spec/acceptance/traits_spec.rb +++ b/spec/acceptance/traits_spec.rb @@ -187,11 +187,11 @@ describe "traits with callbacks" do name "John" trait :great do - after_create {|user| user.name.upcase! } + after(:create) {|user| user.name.upcase! } end trait :awesome do - after_create {|user| user.name = "awesome" } + after(:create) {|user| user.name = "awesome" } end factory :caps_user, traits: [:great] @@ -232,7 +232,7 @@ describe "traits added via strategy" do end trait :great do - after_create {|user| user.name.upcase! } + after(:create) {|user| user.name.upcase! } end end end diff --git a/spec/acceptance/transient_attributes_spec.rb b/spec/acceptance/transient_attributes_spec.rb index 4f1c24e..4d0bc9f 100644 --- a/spec/acceptance/transient_attributes_spec.rb +++ b/spec/acceptance/transient_attributes_spec.rb @@ -17,7 +17,7 @@ describe "transient attributes" do name { "#{FactoryGirl.generate(:name)}#{" - Rockstar" if rockstar}" } email { "#{name.downcase}#{four}@example.com" } - after_create do |user, evaluator| + after(:create) do |user, evaluator| user.name.upcase! if evaluator.upcased end end diff --git a/spec/factory_girl/definition_proxy_spec.rb b/spec/factory_girl/definition_proxy_spec.rb index 94c44c9..3be6936 100644 --- a/spec/factory_girl/definition_proxy_spec.rb +++ b/spec/factory_girl/definition_proxy_spec.rb @@ -126,18 +126,18 @@ describe FactoryGirl::DefinitionProxy, "adding callbacks" do let(:proxy) { FactoryGirl::DefinitionProxy.new(subject) } let(:callback) { -> { "my awesome callback!" } } - context "#after_build" do - before { proxy.after_build(&callback) } + context "#after(:build)" do + before { proxy.after(:build, &callback) } it { should have_callback(:after_build).with_block(callback) } end - context "#after_create" do - before { proxy.after_create(&callback) } + context "#after(:create)" do + before { proxy.after(:create, &callback) } it { should have_callback(:after_create).with_block(callback) } end - context "#after_stub" do - before { proxy.after_stub(&callback) } + context "#after(:stub)" do + before { proxy.after(:stub, &callback) } it { should have_callback(:after_stub).with_block(callback) } end end