Skip to content

Commit

Permalink
Major docs overhaul: refs, guide, cookbook
Browse files Browse the repository at this point in the history
Split the docs into reference, guide, and cookbook.

The guide is mostly as it was before, with a lot of cleanup. Some
sections were combined and simplified.

Some guides were moved into a new cookbook section. This section
describes techniques that are helpful in specific situations of using
factory_bot but not, in itself, part of factory_bot. For example, using
factory_bot to simulate a has_many relation.

Other guides were moved into the wiki. The criteria for this was: does
it have to do with factory_bot, or with something external? Will we
change the docs alongside a change in this repo's code, or due to
another repo's code change?

A new reference section is added. It lists terse facts about methods you
are expected to use. The reason RubyDoc isn't the right solution here is
because these methods are defined all over the codebase. Due to
`instance_eval` and mixins, factory_bot is more of a DSL than a set of
Ruby classes. Thus, the reference section.

During the review, we updated the name of the second parameter for
hooks: instead of `evaluator`, we call it the `context`.

Co-authored-by: Sara Jackson <csara@thoughtbot.com>
Co-authored-by: Stefanni Brasil <stefannibrasil@gmail.com>
  • Loading branch information
3 people committed May 12, 2023
1 parent f870bbe commit fbea96b
Show file tree
Hide file tree
Showing 49 changed files with 805 additions and 140 deletions.
18 changes: 18 additions & 0 deletions docs/book.toml
Expand Up @@ -4,3 +4,21 @@ language = "en"
multilingual = false
src = "src"
title = "factory_bot"

[output.html.redirect]
"/setup/rspec.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-RSpec"
"/setup/cucumber.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-Cucumber"
"/setup/minitest-rails.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-minitest-rails"
"/setup/minitest-spec.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-Minitest::Spec"
"/setup/minitest.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-Minitest"
"/setup/spinach.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-Spinach"
"/setup/test-unit.html" = "https://github.com/thoughtbot/factory_bot/wiki/Integration:-Test::Unit"
"/setup/update-gemfile.html" = "summary.html"
"/setup/configure-test-suite.html" = "summary.html"
"/using-factories/build_stubbed-and-marshaldump.html" = "build-strategies.html"
"/associations/has_many-associations.html" = "../cookbook/has_many-associations.html"
"/associations/has_and_belongs_to_many-associations.html" = "../cookbook/has_and_belongs_to_many-associations.html"
"/associations/polymorphic-associations.html" = "../cookbook/polymorphic-associations.html"
"/associations/interconnected-associations.html" = "../cookbook/interconnected-associations.html"
"/traits/defining-traits.html" = "summary.html"
"/callbacks/default-callbacks.html" = "summary.html"
68 changes: 40 additions & 28 deletions docs/src/SUMMARY.md
@@ -1,28 +1,42 @@
# Summary

[Intro](../../README.md)
[Intro](summary.md)

# Reference

- [Build strategies](ref/build-strategies.md)
- [FactoryBot.find_definitions](ref/find_definitions.md)
- [FactoryBot.define](ref/define.md)
- [factory](ref/factory.md)
- [add_attribute](ref/add_attribute.md)
- [association](ref/association.md)
- [sequence](ref/sequence.md)
- [trait](ref/trait.md)
- [method_missing](ref/method_missing.md)
- [traits_for_enum](ref/traits_for_enum.md)
- [skip_create, to_create, and initialize_with](ref/build-and-create.md)
- [transient](ref/transient.md)
- [Hooks](ref/hooks.md)
- [FactoryBot.modify](ref/modify.md)
- [FactoryBot.lint](ref/lint.md)
- [FactoryBot.register_strategy](ref/register_strategy.md)

# Guide

- [Setup](setup/summary.md)
- [Update Your Gemfile](setup/update-gemfile.md)
- [Configure your test suite](setup/configure-test-suite.md)
- [RSpec](setup/rspec.md)
- [Test::Unit](setup/test-unit.md)
- [Cucumber](setup/cucumber.md)
- [Spinach](setup/spinach.md)
- [Minitest](setup/minitest.md)
- [Minitest::Spec](setup/minitest-spec.md)
- [minitest-rails](setup/minitest-rails.md)
- [Defining factories](defining/summary.md)
- [Using Without Bundler](using-without-bundler/summary.md)
- [Rails Preloaders and RSpec](rails-preloaders-and-rspec/summary.md)
- [Defining factories]()
- [Factory name and attributes](defining/name-attributes.md)
- [Specifying the class explicitly](defining/explicit-class.md)
- [Definition file paths](defining/file-paths.md)
- [Hash attributes](defining/hash-attributes.md)
- [Best practices](defining/best-practices.md)
- [Definition file paths](defining/file-paths.md)
- [Static Attributes (deprecated)](defining/static-attributes.md)
- [Using factories](using-factories/summary.md)
- [Using factories]()
- [Build strategies](using-factories/build-strategies.md)
- [Building or Creating Multiple Records](building-or-creating-multiple-records/summary.md)
- [Attribute overrides](using-factories/attribute-overrides.md)
- [`build_stubbed` and `Marshal.dump`](using-factories/build_stubbed-and-marshaldump.md)
- [Aliases](aliases/summary.md)
- [Dependent Attributes](dependent-attributes/summary.md)
- [Transient Attributes](transient-attributes/summary.md)
Expand All @@ -31,23 +45,19 @@
- [With callbacks](transient-attributes/with-callbacks.md)
- [With associations](transient-attributes/with-associations.md)
- [Method Name / Reserved Word Attributes](method-name-reserved-word-attributes/summary.md)
- [Inheritance](inheritance/summary.md)
- [Inheritance]()
- [Nested factories](inheritance/nested-factories.md)
- [Assigning parent explicitly](inheritance/assigning-parent-explicitly.md)
- [Best practices](inheritance/best-practices.md)
- [Associations](associations/summary.md)
- [Associations]()
- [Implicit definition](associations/implicit-definition.md)
- [Explicit definition](associations/explicit-definition.md)
- [Inline definition](associations/inline-definition.md)
- [Specifying the factory](associations/specifying-the-factory.md)
- [Overriding attributes](associations/overriding-attributes.md)
- [Association overrides](associations/association-overrides.md)
- [Build strategies](associations/build-strategies.md)
- [`has_many` associations](associations/has_many-associations.md)
- [`has_and_belongs_to_many` associations](associations/has_and_belongs_to_many-associations.md)
- [Polymorphic associations](associations/polymorphic-associations.md)
- [Interconnected associations](associations/interconnected-associations.md)
- [Sequences](sequences/summary.md)
- [Sequences]()
- [Global sequences](sequences/global-sequences.md)
- [With dynamic attributes](sequences/with-dynamic-attributes.md)
- [As implicit attributes](sequences/as-implicit-attributes.md)
Expand All @@ -58,28 +68,30 @@
- [Rewinding](sequences/rewinding.md)
- [Uniqueness](sequences/uniqueness.md)
- [Traits](traits/summary.md)
- [Defining traits](traits/defining-traits.md)
- [As implicit attributes](traits/as-implicit-attributes.md)
- [Using traits](traits/using.md)
- [Enum traits](traits/enum.md)
- [Attribute precedence](traits/attribute-precedence.md)
- [In child factories](traits/in-child-factories.md)
- [As mixins](traits/mixins.md)
- [Using traits](traits/using.md)
- [With associations](traits/with-associations.md)
- [Traits within traits](traits/traits-within-traits.md)
- [With transient attributes](traits/with-transient-attributes.md)
- [Enum traits](traits/enum.md)
- [Callbacks](callbacks/summary.md)
- [Default callbacks](callbacks/default-callbacks.md)
- [Multiple callbacks](callbacks/multiple-callbacks.md)
- [Global callbacks](callbacks/global-callbacks.md)
- [Symbol#to_proc](callbacks/symbol-to_proc.md)
- [Modifying factories](modifying-factories/summary.md)
- [Building or Creating Multiple Records](building-or-creating-multiple-records/summary.md)
- [Linting Factories](linting-factories/summary.md)
- [Custom Construction](custom-construction/summary.md)
- [Custom Strategies](custom-strategies/summary.md)
- [Custom Callbacks](custom-callbacks/summary.md)
- [Custom Methods to Persist Objects](custom-methods-to-persist-objects/summary.md)
- [ActiveSupport Instrumentation](activesupport-instrumentation/summary.md)
- [Rails Preloaders and RSpec](rails-preloaders-and-rspec/summary.md)
- [Using Without Bundler](using-without-bundler/summary.md)

# Cookbook

- [`has_many` associations](cookbook/has_many-associations.md)
- [`has_and_belongs_to_many` associations](cookbook/has_and_belongs_to_many-associations.md)
- [Polymorphic associations](cookbook/polymorphic-associations.md)
- [Interconnected associations](cookbook/interconnected-associations.md)
3 changes: 1 addition & 2 deletions docs/src/associations/build-strategies.md
@@ -1,7 +1,6 @@
# Build strategies

In factory\_bot 5, associations default to using the same build strategy as
their parent object:
Associations default to using the same build strategy as their parent object:

```ruby
FactoryBot.define do
Expand Down
2 changes: 1 addition & 1 deletion docs/src/associations/overriding-attributes.md
@@ -1,6 +1,6 @@
# Overriding attributes

You can also override attributes.
You can also override attributes on associations.

Implicitly:

Expand Down
18 changes: 18 additions & 0 deletions docs/src/callbacks/summary.md
@@ -1 +1,19 @@
# Callbacks

factory\_bot makes four callbacks available:

* after(:build) - called after a factory is built (via `FactoryBot.build`, `FactoryBot.create`)
* before(:create) - called before a factory is saved (via `FactoryBot.create`)
* after(:create) - called after a factory is saved (via `FactoryBot.create`)
* after(:stub) - called after a factory is stubbed (via `FactoryBot.build_stubbed`)

Examples:

```ruby
# Define a factory that calls the generate_hashed_password method after the user factory is built
factory :user do
after(:build) { |user, context| generate_hashed_password(user) }
end
```

Note that you'll have an instance of the object in the block.
Expand Up @@ -22,8 +22,8 @@ factory :profile_with_languages do
languages_count { 2 }
end

after(:create) do |profile, evaluator|
create_list(:language, evaluator.languages_count, profiles: [profile])
after(:create) do |profile, context|
create_list(:language, context.languages_count, profiles: [profile])
profile.reload
end
end
Expand Down
Expand Up @@ -44,17 +44,17 @@ FactoryBot.define do
# user_with_posts will create post data after the user has been created
factory :user_with_posts do
# posts_count is declared as a transient attribute available in the
# callback via the evaluator
# callback via the context
transient do
posts_count { 5 }
end

# the after(:create) yields two values; the user instance itself and the
# evaluator, which stores all values from the factory, including transient
# context, which stores all values from the factory, including transient
# 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|
create_list(:post, evaluator.posts_count, user: user)
after(:create) do |user, context|
create_list(:post, context.posts_count, user: user)

# You may need to reload the record here, depending on your application
user.reload
Expand Down
39 changes: 39 additions & 0 deletions docs/src/defining/hash-attributes.md
Expand Up @@ -8,3 +8,42 @@ factory :program do
configuration { { auto_resolve: false, auto_define: true } }
end
```

Alternatively you may prefer `do`/`end` syntax:

```ruby
factory :program do
configuration do
{ auto_resolve: false, auto_define: true }
end
end
```

---

However, defining a value as a hash makes it complicated to set values within
the hash when constructing an object. Instead, prefer to use factory\_bot
itself:

```ruby
factory :program do
configuration { attributes_for(:configuration) }
end

factory :configuration do
auto_resolve { false }
auto_define { true }
end
```

This way you can more easily set value when building:

```ruby
create(
:program,
configuration: attributes_for(
:configuration,
auto_resolve: true,
)
)
```
2 changes: 1 addition & 1 deletion docs/src/dependent-attributes/summary.md
@@ -1,6 +1,6 @@
# Dependent Attributes

Attributes can be based on the values of other attributes using the evaluator
Attributes can be based on the values of other attributes using the context
that is yielded to dynamic attribute blocks:

```ruby
Expand Down
3 changes: 1 addition & 2 deletions docs/src/inheritance/best-practices.md
Expand Up @@ -2,5 +2,4 @@

As mentioned above, it's good practice to define a basic factory for each class
with only the attributes required to create it. Then, create more specific
factories that inherit from this basic parent. Factory definitions are still
code, so keep them DRY.
factories that inherit from this basic parent.
4 changes: 2 additions & 2 deletions docs/src/inheritance/nested-factories.md
@@ -1,7 +1,7 @@
# Nested factories

You can easily create multiple factories for the same class without repeating
common attributes by nesting factories:
You can create multiple factories for the same class without repeating common
attributes by nesting factories:

```ruby
factory :post do
Expand Down
27 changes: 27 additions & 0 deletions docs/src/ref/add_attribute.md
@@ -0,0 +1,27 @@
# add_attribute

Within a factory definition, the `add_attribute` method defines a key/value
pair that will be set when the object is built.

The `add_attribute` method takes two arguments: a name (Symbol or String) and a
block. This block is called each time this object is constructed. The block is
not called when the attribute is overriden by a build strategy.

Assignment is done by calling the Ruby attribute setter. For example, given

```ruby
FactoryBot.define do
factory :user do
add_attribute(:name) { "Acid Burn" }
end
end
```

This will use the `#name=` setter:

```ruby
user = User.new
user.name = "Acid Burn"
```

Also see [method_missing](method_missing.html) for a shorthand.
15 changes: 15 additions & 0 deletions docs/src/ref/association.md
@@ -0,0 +1,15 @@
# association

Within a factory block, use the `association` method to always make an
additional object alongside this one. This name best makes sense within the
context of ActiveRecord.

The `association` method takes a mandatory name and optional options.

The options are zero or more trait names (Symbols), followed by a hash
of attribute overrides. When constructing this association, factory\_bot uses
the trait and attribute overrides given.

See [method_missing](method_missing.html) for a shorthand. See [build
strategies](build-strategies.html) for an explanation of how each build
strategy handles associations.
39 changes: 39 additions & 0 deletions docs/src/ref/build-and-create.md
@@ -0,0 +1,39 @@
# skip_create, to_create, and initialize_with

The `skip_create`, `to_create`, and `initialize_with` methods control how
factory\_bot interacts with the [build strategies](build-strategies.html).

These methods can be called within a `factory` definition block, to scope their
effects to just that factory; or within `FactoryBot.define`, to affect global
change.

## initialize_with

The `initialize_with` method takes a block and returns an instance of the
factory's class. It has access to the `attributes` method, which is a hash of
all the fields and values for the object.

The default definition is:

```ruby
initialize_with { new }
```

## to_create

The `to_create` method lets you control the `FactoryBot.create` strategy. This
method takes a block which takes the object as constructed by
`initialize_with`, and the factory\_bot context. The context has additional
data from any [`transient`] blocks.

[`transient`]: transient.html

The default definition is:

```ruby
to_create { |obj, context| obj.save! }
```

The `skip_create` method is a shorthand for turning `to_create` into a no-op.
This allows you to use the `create` strategy as a synonym for `build`, except
you additionally get any `create` hooks.

0 comments on commit fbea96b

Please sign in to comment.