Skip to content

BoilingFactory is a CLI tool that generates factories for models generated by sqlboiler. https://github.com/volatiletech/sqlboiler.

License

Notifications You must be signed in to change notification settings

stephenafamo/boilingfactory

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BoilingFactory

This is a CLI tool that generates factories for models generated by sqlboiler.

Installation

  • Install sqlboiler
  • Install your database driver for sqlboiler.
  • Generate your models. Link
  • Install boilingfactory: go install github.com/stephenafamo/boilingfactory
  • Generate factory: go run github.com/stephenafamo/boilingfactory psql

A little taste

// 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 for each model on the factory, and pass additional mods when creating an individual object.

type PilotMod interface {
	Apply(*models.Pilot) error
}

There are generated mods for changing the column or relationship of a model, and some 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,

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.

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:

factory.PilotWithLicense(licenseModel) // adds the license relationship

As with the column mods, there is also a variant that takes a callback:

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.

You can pass some extra mods that would be applied to the created relations.

// 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:

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

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

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 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.

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 or CreateAndInsert 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.

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 and insertion in a single step. These functions also accept a variable number of Mods.

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 or Insert methods are called,
but we can also create a custom Factory object and call the methods on it.

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.

// 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.

About

BoilingFactory is a CLI tool that generates factories for models generated by sqlboiler. https://github.com/volatiletech/sqlboiler.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published