https://github.com/stephenafamo/boilingfactory
BoilingFactory is a CLI tool that generates factories for models generated by sqlboiler. https://github.com/volatiletech/sqlboiler.
https://github.com/stephenafamo/boilingfactory
database factorybot golang sqlboiler
Last synced: 11 months ago
JSON representation
BoilingFactory is a CLI tool that generates factories for models generated by sqlboiler. https://github.com/volatiletech/sqlboiler.
- Host: GitHub
- URL: https://github.com/stephenafamo/boilingfactory
- Owner: stephenafamo
- License: mit
- Created: 2022-01-15T21:18:00.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2022-01-29T09:38:33.000Z (about 4 years ago)
- Last Synced: 2025-03-31T06:41:19.581Z (about 1 year ago)
- Topics: database, factorybot, golang, sqlboiler
- Language: Smarty
- Homepage:
- Size: 85.9 KB
- Stars: 12
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# BoilingFactory
This is a CLI tool that generates factories for models generated by [`sqlboiler`](https://github.com/volatiletech/sqlboiler).
## Installation
* Install [`sqlboiler`](https://github.com/volatiletech/sqlboiler)
* Install your database driver for [`sqlboiler`](https://github.com/volatiletech/sqlboiler#supported-databases).
* Generate your models. [Link](https://github.com/volatiletech/sqlboiler#initial-generation)
* Install `boilingfactory`: `go install github.com/stephenafamo/boilingfactory`
* Generate factory: `go run github.com/stephenafamo/boilingfactory psql`
## A little taste
```go
// Create a new model
license, _ := factory.CreateLicense()
// create with options
pilot, _ := factory.CreatePilot(
// Set a Column with a value
factory.PilotID(120),
// Use a callback with the xxFunc variants
factory.PilotNameFunc(func() (string, error) {
var randomName string
// somehow generate a new name
return randomName, nil
}),
// RELATIONSHIPS
// Attach an existing model
factory.PilotWithLicense(license),
// Build models dynamically
factory.PilotWithNewJets(5),
)
// Insert can be used with any existing model
// Before Inserting: It checks if there are any foreign keys that are not
// set and do not have a default and creates models for it
// After Inserting: It saves any related models found in the relationship struct
_ = factory.InsertPilot(ctx, db, pilot)
// Set defaults on the factory
factory.SetBasePilotMod(factory.PilotMods{
// Attach an existing model
factory.PilotWithNewLicense(),
// Build models automatically
factory.PilotWithNewJets(5),
})
factory.SetBaseJetMod(factory.JetMods{
factory.JetColor("blue"),
})
// Now any new pilot will be created with a new license and 5 blue jets
pilot2, _ := factory.CreateAndInsertPilot(ctx, db)
```
## Configuration
The program accepts these flags to overwrite any configuration.
* `--sqlboiler-models`: The package of your generated models. Needed to import them properly in the factory. DEFAULT: `current/go/module/models`.
* `--config`: Configuration file path. DEFAULT: `sqlboiler.toml`
* `--output` or `-o`: The name of the folder to output to. DEFAULT: `factory`
* `--pkgname` or `-p`: The name you wish to assign to your generated package. DEFAULT: `factory`
* `--wipe`: Delete the output folder (rm -rf) before generation to ensure sanity. DEFAULT `false`
* `--version`: Print the version
* `debug` or `d`: Debug mode prints stack traces on error. DEFAULT `false`
They can also be set in the config file, or as environment variables
To attempt to match your generated model options, the defualt the sqlboiler configuration files are used: `sqlboiler.toml` or `json` or `yaml`.
**NOTE:** If you have customized the output folder or pkgname in your `sqlboiler` config file and you are passing the same file to `boilingfactory`, you should overwrite them using the `-o` and `p` flags respectively.
## Mods
Mods are central to the design of the factories. Each model has its own mod type, which is an interface that applies a change to the model.
You can set [base mods][base-mods] for each model on the factory, and pass additional mods when [creating][create] an individual object.
```go
type PilotMod interface {
Apply(*models.Pilot) error
}
```
There are generated mods for changing the [column][column-mods] or [relationship][relationship-mods] of a model, and some [helper types][mod-helper-types] to make it easier to implement custom mods.
### Column Mods
For every column in a model, several fuctions are created:
One take a value and returns a mod that sets the column's value,
```go
factory.PilotName("Stephen") // returns a PilotMod
```
The other takes a callback function that is called to generate new values. This plays very nicely with generating random data.
```go
factory.PilotNameFunc(func () (string, error) {
return randomdata.SillyName(), nil
})
```
### Relationship Mods
For every relationship in the model, several functions are created. Each of these generated functional mods do the following:
* They place the relationship in the `.R` struct of both the local model and the relation.
* If it is a to-one relationship, this will overwrite any currently set models
* For a to-many relationship, `AddXXX` methods are generated which will appended relations to the existing ones instead of overwriting.
* They set the value of the foreign key of the local model or related model as applicable.
One `Mod` is generated to set the relationship passing an already created model:
```go
factory.PilotWithLicense(licenseModel) // adds the license relationship
```
As with the [column mods][column-mods], there is also a variant that takes a callback:
```go
factory.PilotWithLicenseFunc(f func() (*models.License, error) {
var license *models.License // do some fancy generation
return license, nil
})
```
Finally, there is a variant that **creates** the relation on the fly using a supplied factory.
Pass `nil` as the factory to use the default global factory. See the section on [Multiple Factories][multiple-factories].
You can pass some extra mods that would be applied to the created relations.
```go
// Using the default factory
factory.PilotWithNewLicense(nil, factory.LicenseNumber("random"))
var myCustomFactory *factory.Factory
factory.PilotWithNewLicense(myCustomFactory, factory.LicenseNumber("random"))
```
**NOTE:** In addition, a To-Many relationship will also have `Add` relationship mods:
```go
factory.PilotWithJets(jetModelSlice) // Overwrites all current relationships with this
factory.PilotAddJets(jetModelSlice) // Adds the jets to the current ones
```
### Mod helper types
There are also helper types to easily create mods.
The `xxxModFunc` type helps convert a function like `func(*models.Pilot) error` to a `Mod`
```go
type PilotModFunc func(*models.Pilot) error
func (f PilotModFunc) Apply(n *models.Pilot) error {
return f(n)
}
```
The `xxxMods` type takes a list of mods and applies them as a single mod
```go
type PilotMods []PilotMod
func (mods PilotMods) Apply(n *models.Pilot) error {
for _, f := range mods {
err := f.Apply(n)
if err != nil {
return err
}
}
return nil
}
```
If you needed to, you could implement your own mods
## Usage
### Creating models
Models are created using the `Create` function.
These functions can take a variable number of [`Mods`][mods] which it would apply to a fresh object before returning. If there are any base mods defined on the factory, the base mods will be applied **first**.
**Multiple** models can be created with the **CreatePlural()** function. For example, if we have a `Jet` models, `CreateJet` and `CreateJets` will be generated for use.
```go
pilot, err := factory.CreatePilot()
pilotSlice, err := factory.CreatePilots(10)
```
**NOTE:** This does not save the model in the database. To do that you should use the [`Insert`][insert] or [`CreateAndInsert`][create-and-insert] functions.
### Inserting models
The **Insert()** function saves the model in the database.
* For every foreign key, if the value is not set and the field does not have a default value, a related model is built (using the default mods) and attached to the model **before** insertion.
* For dependent relations, models in the `.R` field are also inserted. So if you have a mod that builds and attaches related models, this will also save them.
**Multiple** models can be inserted with the **InsertPlural()** function. For example, if we have a `Jet` models, `InsertJet` and `InsertJets` will be generated for use.
```go
err := factory.InsertPilot(ctx, db, pilot)
err := factory.InsertPilots(ctx, db, pilotSlice)
```
### Create and Insert
For convenience, there are the `CreateAndInsert` functions that do both [creation][create] and [insertion][insert] in a single step. These functions also accept a variable number of [`Mods`][mods].
```go
pilot, err := factory.CreateAndInsertPilot(ctx, db)
pilotSlice, err := factory.CreateAndInsertPilots(ctx, db, 10)
```
### Multiple Factories
There is a global factory which is used when the [`Create`][create] or [`Insert`][insert] methods are called,
but we can also create a custom Factory object and call the methods on it.
```go
var myCustomFactory = &factory.Factory{}
pilot, _ := myCustomFactory.CreatePilot()
jet, _ := myCustomFactory.CreateAndInsertJet(ctx, db)
```
These methods works the same way as the package functions. Defaults can also be set on custom factories.
### Setting Defaults
You can set default values for models by setting a base mod to be applied to all created models in a factory.
```go
// For the global factory
factory.SetBasePilotMod(
factory.PilotName("Stephen"),
)
pilot, _ := factory.CreatePilot() // pilot.Name is "Stephen"
// For a custom factory
var myCustomFactory = &factory.Factory{}
myCustomFactory.SetBasePilotMod(
factory.PilotName("John"),
)
pilot2, _ := factory.CreatePilot() // pilot2.Name is "John"
```
## Contributing
PRs welcome.
[mods]: #mods
[create]: #creating-models
[insert]: #inserting-models
[create-and-insert]: #create-and-insert
[mod-helper-types]: #mod-helper-types
[column-mods]: #column-mods
[relationship-mods]: #relationship-mods
[base-mods]: #setting-defaults
[multiple-factories]: #multiple-factories