<p align="center"><img src="https://github.com/gobuffalo/buffalo/blob/master/logo.svg" width="360"></p>
<p align="center">
<a href="https://godoc.org/github.com/gobuffalo/genny"><img src="https://godoc.org/github.com/gobuffalo/genny?status.svg" alt="GoDoc" /></a>
<a href="https://travis-ci.org/gobuffalo/genny"><img src="https://travis-ci.org/gobuffalo/genny.svg?branch=master" alt="Build Status" /></a>
<a href="https://goreportcard.com/report/github.com/gobuffalo/genny"><img src="https://goreportcard.com/badge/github.com/gobuffalo/genny" alt="Go Report Card" /></a>
</p>
# Genny
## What Is Genny?
Genny is a _framework_ for writing modular generators, it however, doesn't actually generate anything. It just makes it easier for you to. :)
## Core Concepts
### Generators
A [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) is used to build a blue print of what you want to generate.
A few of things that can be added to a `Generator` are:
* [`github.com/gobuffalo/genny#File`](https://godoc.org/github.com/gobuffalo/genny#File)
* [`os/exec#Cmd`](https://godoc.org/os/exec#Cmd)
* [`github.com/gobuffalo/packd#Box`](https://godoc.org/github.com/gobuffalo/packd#Box)
* [`net/http#Request`](https://godoc.org/net/http#Request)
* and more
A [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) does *not* actually generate anything; a [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner) is needed to run the generator.
```go
g := genny.New()
// add a file
g.File(genny.NewFileS("index.html", "Hello\n"))
// execute a command
g.Command(exec.Command("go", "env"))
// run a function at run time
g.RunFn(func(r *genny.Runner) error {
// look for the `genny` executable
if _, err := r.LookPath("genny"); err != nil {
// it wasn't found, so install it
c := gogen.Get("github.com/gobuffalo/genny/genny")
if err := r.Exec(c); err != nil {
return err
}
}
// call the `genny` executable with the `-h` flag.
return r.Exec(exec.Command("genny", "-h"))
})
```
When a [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) is run each item that was added to it will be run in FIFO order. In the above example this means the following will happen:
1. Create a new file `r.Root/index.html`
1. Run the command `go env`
1. Run a function that installs `genny`
#### Runtime Checks
Genny has two different components; the "generator" (or blueprint) and the "runner" which executes the generator. Often it is necessary to only run certain code when the generator is run, not built. For example, checking the existing of an executable and installing it if missing.
In these situations you will want to use a [`github.com/gobuffalo/genny#RunFn`](https://godoc.org/github.com/gobuffalo/genny#RunFn) function.
In this example at runtime the `RunFn` will be called given the `*Runner` that is calling it. When called the function will ask the [`github.com/gobuffalo/genny#Runner.LookPath`](https://godoc.org/github.com/gobuffalo/genny#Runner.LookPath) function to ask the location of the `genny` executable.
In [`github.com/gobuffalo/genny#DryRunner`](https://godoc.org/github.com/gobuffalo/genny#DryRunner) this will simply echo back the name of the executable that has been asked for, in this case `return "genny", nil`.
In [`github.com/gobuffalo/genny#WetRunner`](https://godoc.org/github.com/gobuffalo/genny#WetRunner) this will call the [`os/exec#LookPath`](https://godoc.org/os/exec#LookPath) and return its results.
If the `genny` binary is not found, it will attempt to install it. Should that succeed the method returns the execution of a call to `genny -h`.
```go
g.RunFn(func(r *genny.Runner) error {
// look for the `genny` executable
if _, err := r.LookPath("genny"); err != nil {
// it wasn't found, so install it
c := gogen.Get("github.com/gobuffalo/genny/genny")
if err := r.Exec(c); err != nil {
return err
}
}
// call the `genny` executable with the `-h` flag.
return r.Exec(exec.Command("genny", "-h"))
})
```
The flexibility of the `*Fn` functions, combined with [`github.com/gobuffalo/genny#RunFn`](https://godoc.org/github.com/gobuffalo/genny#RunFn) make for a powerful testing combination.
### Runners
A [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner) is used to run generators and control the environment in which those generators are run.
Genny ships with three implementations of `Runner` that cover _most_ situations. They can also provide good starting points for customized implementations of `Runner`.
* [`github.com/gobuffalo/genny#DryRunner`](https://godoc.org/github.com/gobuffalo/genny#DryRunner)
* [`github.com/gobuffalo/genny#WetRunner`](https://godoc.org/github.com/gobuffalo/genny#WetRunner)
* [`github.com/gobuffalo/genny/gentest#NewRunner`](https://godoc.org/github.com/gobuffalo/genny/gentest#NewRunner)
#### Adding Generators
To add a [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) to a [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner) the [`github.com/gobuffalo/genny#Runner.With`](https://godoc.org/github.com/gobuffalo/genny#Runner.With) function can be used.
```go
run := genny.DryRunner(context.Background())
// add a generator from the `simple` package
g := simple.New()
run.With(g)
// add a generator from the `notsimple` package
g := notsimple.New()
run.With(g)
```
Each [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) is run in FIFO order in which it was added to the [`github.com/gobuffalo/genny#Runner`](https://godoc.org/github.com/gobuffalo/genny#Runner).
It is common to have a function that builds a new [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) or returns an `error` if there was a problem.
```go
func New() (*genny.Generator, error) {
g := simple.New()
// do work which might error
return g, nil
}
```
The [`github.com/gobuffalo/genny#Runner.WithNew`](https://godoc.org/github.com/gobuffalo/genny#Runner.WithNew) function was designed to make adding a [`github.com/gobuffalo/genny#Generator`](https://godoc.org/github.com/gobuffalo/genny#Generator) with this return argument signature easier.
```go
if err := run.WithNew(New()); err != nil {
log.Fatal(err)
}
```
#### Dry Running (**NON-DESTRUCTIVE**)
The idea of "dry" running means that no commands are executed, no files are written to disk, no HTTP requests are made, etc... Instead these steps are run "dry", which in the case of [`github.com/gobuffalo/genny#DryRunner`](https://godoc.org/github.com/gobuffalo/genny#DryRunner) is the case.
```go
func main() {
run := genny.DryRunner(context.Background())
g := simple.New()
run.With(g)
if err := run.Run(); err != nil {
log.Fatal(err)
}
}
```
```plain
// output
DEBU[2018-12-06T15:13:47-05:00] Step: 4eac628c
DEBU[2018-12-06T15:13:47-05:00] Chdir: /go/src/github.com/gobuffalo/genny/internal/_examples/dry
DEBU[2018-12-06T15:13:47-05:00] File: /go/src/github.com/gobuffalo/genny/internal/_examples/dry/index.html
DEBU[2018-12-06T15:13:47-05:00] Exec: go env
DEBU[2018-12-06T15:13:47-05:00] LookPath: genny
DEBU[2018-12-06T15:13:47-05:00] Exec: genny -h
```
```bash
// file list
.
└── main.go
0 directories, 1 file
```
Using a "dry" runner can make testing easier when you don't have to worry about commands running, files being written, etc... It can also make it easy to provide a "dry-run" flag to your generators to let people see what will be generated when the generator is run for real.
#### Wet Running (**DESTRUCTIVE**)
While "dry" means to not execute commands or write files, "wet" running means the exact opposite; it will write files and execute commands.
Use the [`github.com/gobuffalo/genny#WetRu