Add global callbacks

This allows callbacks (after :build, :create, etc.) to be defined at the
FactoryGirl level; this means that the callback will be invoked for all
factories. This is primarily to maintain consistency and follow the
principle of least surprise.

As usual, callbacks are applied from the lowest component to the
highest, meaning that global callbacks will be run after factory and
trait callbacks are run.

    FactoryGirl.define do
      after(:build) {|object| puts "Built #{object}" }

      factory :user
      # ...
    end

Closes #481
Closes #486
This commit is contained in:
Joshua Clayton 2013-02-08 11:00:22 -05:00
parent 9bc0e39780
commit 36cb43e9b0
9 changed files with 95 additions and 19 deletions

View File

@ -748,6 +748,20 @@ factory :user do
end
```
To override callbacks for all factories, define them within the
`FactoryGirl.define` block:
```ruby
FactoryGirl.define do
after(:build) {|object| puts "Built #{object}" }
after(:create) {|object| AuditLog.create(attrs: object.attributes) }
factory :user do
name "John Doe"
end
end
```
Modifying factories
-------------------

View File

@ -1,5 +1,5 @@
PATH
remote: /Users/mjankowski/Development/OpenSource/factory_girl
remote: /Users/joshuaclayton/dev/gems/factory_girl
specs:
factory_girl (4.2.0)
activesupport (>= 3.0.0)

View File

@ -55,7 +55,7 @@ module FactoryGirl
end
class << self
delegate :factories, :sequences, :traits, :strategies, :callback_names,
delegate :factories, :sequences, :traits, :callbacks, :strategies, :callback_names,
:to_create, :skip_create, :initialize_with, :constructor, :duplicate_attribute_assignment_from_initialize_with,
:duplicate_attribute_assignment_from_initialize_with=, to: :configuration
end

View File

@ -15,7 +15,8 @@ module FactoryGirl
initialize_with { new }
end
delegate :to_create, :skip_create, :constructor, to: :@definition
delegate :to_create, :skip_create, :constructor, :before, :after,
:callback, :callbacks, to: :@definition
def initialize_with(&block)
@definition.define_constructor(&block)

View File

@ -84,6 +84,21 @@ module FactoryGirl
@constructor = block
end
def before(*names, &block)
callback(*names.map {|name| "before_#{name}" }, &block)
end
def after(*names, &block)
callback(*names.map {|name| "after_#{name}" }, &block)
end
def callback(*names, &block)
names.each do |name|
FactoryGirl.register_callback(name)
add_callback(Callback.new(name, block))
end
end
private
def base_traits

View File

@ -1,7 +1,7 @@
module FactoryGirl
class DefinitionHierarchy
def callbacks
[]
FactoryGirl.callbacks
end
def constructor

View File

@ -6,6 +6,8 @@ module FactoryGirl
undef_method(method) unless UNPROXIED_METHODS.include?(method.to_s)
end
delegate :before, :after, :callback, to: :@definition
attr_reader :child_factories
def initialize(definition, ignore = false)
@ -157,20 +159,5 @@ module FactoryGirl
def initialize_with(&block)
@definition.define_constructor(&block)
end
def before(*names, &block)
callback(*names.map {|name| "before_#{name}" }, &block)
end
def after(*names, &block)
callback(*names.map {|name| "after_#{name}" }, &block)
end
def callback(*names, &block)
names.each do |name|
FactoryGirl.register_callback(name)
@definition.add_callback(Callback.new(name, block))
end
end
end
end

View File

@ -48,6 +48,14 @@ module FactoryGirl
def self.run(block)
new.instance_eval(&block)
end
delegate :before, :after, :callback, to: :configuration
private
def configuration
FactoryGirl.configuration
end
end
class ModifyDSL

View File

@ -175,3 +175,54 @@ describe 'binding a callback to multiple callbacks' do
expect(FactoryGirl.build_stubbed(:user, name: 'John Doe').name).to eq 'JOHN DOE'
end
end
describe 'global callbacks' do
include FactoryGirl::Syntax::Methods
before do
define_model('User', name: :string)
define_model('Company', name: :string)
FactoryGirl.define do
after :build do |object|
object.name = case object.class.to_s
when 'User' then 'John Doe'
when 'Company' then 'Acme Suppliers'
end
end
after :create do |object|
object.name = "#{object.name}!!!"
end
trait :awesome do
after :build do |object|
object.name = "___#{object.name}___"
end
after :create do |object|
object.name = "A#{object.name}Z"
end
end
factory :user do
after :build do |user|
user.name = user.name.downcase
end
end
factory :company do
after :build do |company|
company.name = company.name.upcase
end
end
end
end
it 'triggers after build callbacks for all factories' do
expect(build(:user).name).to eq 'john doe'
expect(create(:user).name).to eq 'john doe!!!'
expect(create(:user, :awesome).name).to eq 'A___john doe___!!!Z'
expect(build(:company).name).to eq 'ACME SUPPLIERS'
end
end