Skip to content

Commit 8e62514

Browse files
FEATURE: Before(:all) and after(:all) callbacks added (#1740)
* Before(:all) and after(:all) callbacks added * Update docs/src/ref/hooks.md Co-authored-by: Valerie Burzynski <[email protected]> * Update docs/src/callbacks/summary.md Co-authored-by: Valerie Burzynski <[email protected]> * Update docs/src/callbacks/summary.md Co-authored-by: Valerie Burzynski <[email protected]> * Update docs/src/callbacks/summary.md Co-authored-by: Valerie Burzynski <[email protected]> * small code tidy-up - moved `require` statemnet to top of file (containers/test_log.rb) - removed blank line (doc/src/callbacks/summary.md) --------- Co-authored-by: Valerie Burzynski <[email protected]>
1 parent cb7971a commit 8e62514

File tree

8 files changed

+416
-43
lines changed

8 files changed

+416
-43
lines changed

docs/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
- [Multiple callbacks](callbacks/multiple-callbacks.md)
8585
- [Global callbacks](callbacks/global-callbacks.md)
8686
- [Symbol#to_proc](callbacks/symbol-to_proc.md)
87+
- [Callback order](callbacks/callback_order.md)
8788
- [Modifying factories](modifying-factories/summary.md)
8889
- [Linting Factories](linting-factories/summary.md)
8990
- [Custom Construction](custom-construction/summary.md)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Callback Order
2+
3+
When a callback event like `after_build` or `before_all` is triggered, all callbacks for that event are excuted in the following order:
4+
5+
1. Global callbacks.
6+
2. Inherited callbacks.
7+
3. Factory callbacks.
8+
4. Trait callbacks (in the order requested).
9+
10+
## A simple factory example:
11+
12+
```ruby
13+
FactoryBot.define do
14+
before(:all) { puts "Global before(:all)" }
15+
after(:all) { puts "Global after(:all)" }
16+
17+
factory :user do
18+
before(:all) { puts "User before(:all)" }
19+
after(:all) { puts "User after(:all)" }
20+
after(:build) { puts "User after(:build)"
21+
22+
trait :trait_a do
23+
after(:build) { puts "Trait-A after(:build)"
24+
end
25+
26+
trait :trait_b do
27+
after(:build) { puts "Trait-B after(:build)" }
28+
end
29+
end
30+
end
31+
32+
build(:user, :trait_b, :trait_a)
33+
34+
# Result:
35+
#
36+
# 1. "Global before(:all)"
37+
# 2. "User before(:all)"
38+
# 3. "User after(:build)"
39+
# 4. "Trait-B after(:build)"
40+
# 5. "Trait-A after(:build)"
41+
# 6. "Global after(:all)"
42+
# 7. "User after(:all)"
43+
44+
```
45+
46+
47+
## An inherited factory example:
48+
49+
```ruby
50+
FactoryBot.define do
51+
before(:all) { puts "Global before(:all)" }
52+
after(:build) { puts "Global after(:build)" }
53+
after(:all) { puts "Global after(:all)" }
54+
55+
factory :parent do
56+
before(:all) { puts "Parent before(:all)" }
57+
after(:all) { puts "Parent after(:all)" }
58+
after(:build) { puts "Parent after(:build)" }
59+
60+
trait :trait_a do
61+
after(:build) { puts "Trait-A after(:build)" }
62+
end
63+
64+
factory :child do
65+
before(:all) { puts "Child before(:all)" }
66+
after(:build) { puts "Child after(:build)" }
67+
after(:all) { puts "Child after(:all)" }
68+
69+
trait :trait_b do
70+
after(:build) { puts "Trait-B after(:build)" }
71+
after(:all) { puts "Trait-B after(:all)" }
72+
end
73+
74+
trait :trait_c do
75+
after(:build) { puts "Trait-C after(:build)" }
76+
before(:all) { puts "Trait-C before(:all)" }
77+
end
78+
end
79+
end
80+
end
81+
82+
build(:child, :trait_c, :trait_a, :trait_b)
83+
84+
# Result:
85+
#
86+
# 1. "Global before(:all)"
87+
# 2. "Parent before(:all)"
88+
# 3. "Child before(:all)"
89+
# 4. "Trait-C before(:all)"
90+
# 5. "Global after(:build)"
91+
# 6. "Parent after(:build)"
92+
# 7. "Child after(:build)"
93+
# 8. "Trait-C after(:build)"
94+
# 9. "Trait-A after(:build)"
95+
# 10. "Trait-B after(:build)"
96+
# 11. "Global after(:all)"
97+
# 12. "Parent after(:all)"
98+
# 13. "Child after(:all)"
99+
# 14. "Trait-B after(:all)"
100+
101+
```

docs/src/callbacks/summary.md

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,42 @@
11
# Callbacks
22

3-
factory\_bot makes four callbacks available:
3+
factory\_bot makes six callbacks available:
44

5-
* after(:build) - called after a factory is built (via `FactoryBot.build`, `FactoryBot.create`)
6-
* before(:create) - called before a factory is saved (via `FactoryBot.create`)
7-
* after(:create) - called after a factory is saved (via `FactoryBot.create`)
8-
* after(:stub) - called after a factory is stubbed (via `FactoryBot.build_stubbed`)
5+
| Callback | Timing |
6+
| --------------- | ------------------------------------------------------------------------------------------------------------------------- |
7+
| before(:all) | called before a factory constructs an object (via `FactoryBot.build`, `FactoryBot.create`, or `FactoryBot.build_stubbed`) |
8+
| after(:build) | called after a factory builds an object (via `FactoryBot.build` or `FactoryBot.create`) |
9+
| before(:create) | called before a factory saves an object (via `FactoryBot.create`) |
10+
| after(:create) | called after a factory saves an object (via `FactoryBot.create`) |
11+
| after(:stub) | called after a factory stubs an object (via `FactoryBot.build_stubbed`) |
12+
| after(:all) | called after a factory constructs an object (via `FactoryBot.build`, `FactoryBot.create`, or `FactoryBot.build_stubbed`) |
913

10-
Examples:
14+
15+
## Examples
16+
17+
### Calling an object's own method after building
1118

1219
```ruby
13-
# Define a factory that calls the generate_hashed_password method after the user factory is built
20+
##
21+
# Define a factory that calls the generate_hashed_password method
22+
# after the user factory is built.
23+
#
24+
# Note that you'll have an instance of the object in the block
25+
#
1426
factory :user do
1527
after(:build) { |user, context| generate_hashed_password(user) }
1628
end
1729
```
1830

19-
Note that you'll have an instance of the object in the block.
31+
### Skipping an object's own :after_create callback
32+
33+
```ruby
34+
##
35+
# Disable a model's own :after_create callback that sends an email
36+
# on creation, then re-enable it afterwards
37+
#
38+
factory :user do
39+
before(:all){ User.skip_callback(:create, :after, :send_welcome_email) }
40+
after(:all){ User.set_callback(:create, :after, :send_welcome_email) }
41+
end
42+
```

docs/src/ref/hooks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ factory. Within a `FactoryBot.define` block, they are global to all factories.
1313

1414
The `callback` method allows you to hook into any factory\_bot callback by
1515
name. The pre-defined names, as seen in the [build strategies] reference, are
16-
`after_build`, `before_create`, `after_create`, and `after_stub`.
16+
`before_all`, `after_build`, `before_create`, `after_create`, `after_stub`, and `after_all`.
1717

1818
This method takes a splat of names, and a block. It invokes the block any time
1919
one of the names is activated. The block can be anything that responds to

lib/factory_bot/factory.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def build_class
3333

3434
def run(build_strategy, overrides, &block)
3535
block ||= ->(result) { result }
36+
3637
compile
3738

3839
strategy = StrategyCalculator.new(build_strategy).strategy.new
@@ -44,7 +45,11 @@ def run(build_strategy, overrides, &block)
4445
evaluation =
4546
Evaluation.new(evaluator, attribute_assigner, compiled_to_create, observer)
4647

47-
strategy.result(evaluation).tap(&block)
48+
evaluation.notify(:before_all, nil)
49+
instance = strategy.result(evaluation).tap(&block)
50+
evaluation.notify(:after_all, instance)
51+
52+
instance
4853
end
4954

5055
def human_names

0 commit comments

Comments
 (0)