-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 88de9a2
Showing
68 changed files
with
3,577 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# logs and outputs | ||
*.test | ||
*.out | ||
|
||
# IDEs | ||
.idea/ | ||
|
||
# misc | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2020 Randy Lough | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,291 @@ | ||
# GoGen | ||
|
||
## What is GoGen? | ||
Gogen, short for, Go Generate, is a simple file generation library written in Go. This library aims to provide an easy solution to modular file generation. Whether you want to generate your next projects scaffolding, or leverage conditional functions, make use of file bundling, or even create a word document, gogen is up for the task. | ||
|
||
## Features | ||
- Single file generation | ||
- Code generation (dyanmic imports, dynamic types, etc.) | ||
- File bundling (multiple files get put into one file) | ||
- Bulk generation (generate whole directories and subdirectories) | ||
|
||
## Installation | ||
```shell script | ||
go get github.com/randallmlough/gogen | ||
``` | ||
|
||
## Creating a single file | ||
### Creating a Go file | ||
[SIMPLE EXAMPLE](github.com/randallmlough/gogen/examples/simple) | ||
```go | ||
package main | ||
|
||
import ( | ||
"github.com/randallmlough/gogen" | ||
"log" | ||
) | ||
|
||
func main() { | ||
data := map[string]interface{}{ | ||
"Name": "john", | ||
"Greeting": "Hello to you too.", | ||
} | ||
contents, _ := gogen.LoadTemplate("path/to/templates/someGoFile.gotpl") | ||
gocode := &gogen.Go{ | ||
Template: contents, | ||
Filename: "relative/output/path/hello.go", | ||
PackageName: "testing", | ||
TemplateData: data, | ||
} | ||
|
||
gogen.Generate(gocode, &gogen.Config{ | ||
GeneratedHeader: true, | ||
Description: "// file generation is awesome"}) | ||
} | ||
``` | ||
|
||
#### Example template file | ||
```gotemplate | ||
{{ reserveImport "fmt" }} | ||
func talk() { | ||
name := "{{.Name}}" | ||
fmt.Println("Hello there", name) | ||
greeting("{{$.Greeting}}") | ||
} | ||
func greeting(say string) { | ||
fmt.Println(say) | ||
} | ||
``` | ||
`reserveImport` makes sure that the `fmt` package will be loaded and added to the generated file. | ||
|
||
#### Output | ||
`hello.go` | ||
```go | ||
// Code generated by github.com/randallmlough/gogen, DO NOT EDIT. | ||
|
||
// file generation is awesome | ||
package testing | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
func talk() { | ||
name := "john" | ||
fmt.Println("Hello there", name) | ||
greeting("Hello to you too.") | ||
} | ||
|
||
func greeting(say string) { | ||
fmt.Println(say) | ||
} | ||
``` | ||
|
||
### Creating another type of file | ||
For this example let's create a YAML file. | ||
```go | ||
package main | ||
|
||
import ( | ||
"github.com/randallmlough/gogen" | ||
"log" | ||
) | ||
|
||
func main() { | ||
type Data struct { | ||
Name string | ||
Location string | ||
Age *int | ||
FavoriteThings []string | ||
} | ||
data := Data{ | ||
Name: "john snow", | ||
Location: "the wall", | ||
Age: nil, | ||
FavoriteThings: []string{"dogs", "cold places", "sam"}, | ||
} | ||
contents, _ := gogen.LoadTemplate("examples/simple/file.gotpl") | ||
doc := &gogen.Document{ | ||
Template: contents, | ||
Filename: "examples/simple/output/file.yml", | ||
TemplateData: data, | ||
} | ||
gogen.Generate(doc) | ||
} | ||
``` | ||
|
||
**The template** | ||
```gotemplate | ||
name: {{.Name}} | ||
location: {{.Location}} | ||
{{- if .Age }} | ||
age: {{.Age}} | ||
{{ else }} | ||
# make sure to checkout the template. I was conditionally rendered | ||
{{ end -}} | ||
{{- with .FavoriteThings}} | ||
favorites: | ||
{{ range . -}} | ||
- {{.}} | ||
{{ end }} | ||
{{ end -}} | ||
``` | ||
|
||
**The output** | ||
```yaml | ||
name: john snow | ||
location: the wall | ||
# make sure to checkout the template. I was conditionally rendered | ||
|
||
favorites: | ||
- dogs | ||
- cold places | ||
- sam | ||
``` | ||
[SIMPLE EXAMPLE](github.com/randallmlough/gogen/examples/simple) | ||
## Generating files from a directory | ||
[DIRECTORY EXAMPLE](github.com/randallmlough/gogen/examples/directory) | ||
Let's say we have the following project structure... | ||
```shell script | ||
├── main.go | ||
├── output | ||
└── templates | ||
├── file.yml.gotpl | ||
├── gocode.go.gotpl | ||
└── types | ||
└── type.go.gotpl | ||
``` | ||
And we want to generate all the template files and put them into the output directory. It's crazy easy. | ||
|
||
```go | ||
func main() { | ||
type Data struct { | ||
Name string | ||
Location string | ||
Age *int | ||
FavoriteThings []string | ||
Greeting string | ||
PrimaryKey interface{} | ||
} | ||
data := Data{ | ||
Name: "john snow", | ||
Location: "the wall", | ||
Age: nil, | ||
FavoriteThings: []string{"dogs", "cold places", "sam"}, | ||
Greeting: "Hello to you too.", | ||
PrimaryKey: int(1), | ||
} | ||
dir := &gogen.Directory{ | ||
OutputDir: "examples/directory/output", | ||
TemplateDir: "examples/directory/templates", | ||
Data: data, | ||
} | ||
if err := gogen.Generate(dir, gogen.SkipChildren(false)); err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
``` | ||
|
||
Output structure | ||
```shell script | ||
├── output | ||
│ ├── file.yml | ||
│ ├── gocode.go | ||
│ └── types | ||
│ └── type.go | ||
|
||
``` | ||
|
||
The big difference between generating a single file and multiple files is the data you are passing into each template. When you generate a single file you can be selective on the data you pass in, but when you generate from a directory, the data will be shared across all the templates. However, that's rarely an issue. | ||
[DIRECTORY EXAMPLE](github.com/randallmlough/gogen/examples/directory) | ||
|
||
### Bundling files | ||
[BUNDLE EXAMPLE](github.com/randallmlough/gogen/examples/bundles) | ||
We can also bundle any number of files into one file. | ||
|
||
```shell script | ||
├── main.go | ||
├── output | ||
└── templates | ||
├── file | ||
│ ├── part-one.yml.gotpl | ||
│ └── part-two.yml.gotpl | ||
├── part-one.go.gotpl | ||
├── part-three.go.gotpl | ||
└── part-two.go.gotpl | ||
``` | ||
Let's bundle everything from the templates directory and put the result bundled file into the output directory. | ||
|
||
```go | ||
func main(){ | ||
data := map[string]interface{}{ | ||
"PrimaryKey": 1, | ||
"PartTwoAnswer": 2, | ||
"PartThreeCtxValue": "some-random-key", | ||
} | ||
gocode := &gogen.Go{ | ||
Bundle: "examples/bundles/templates", | ||
Filename: "examples/bundles/output/bundle.go", | ||
PackageName: "testing", | ||
TemplateData: data, | ||
} | ||
if err := gogen.Generate(gocode); err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
``` | ||
|
||
**Output** | ||
```shell script | ||
├── output | ||
│ ├── bundle.go | ||
``` | ||
Super cool! | ||
[BUNDLE EXAMPLE](github.com/randallmlough/gogen/examples/bundles) | ||
|
||
## Extending gogen | ||
By design, gogen accepts and returns interfaces for most of the heavy lifting. So as long as you fulfill the `Gen` and `File` interface you can extend gogen however you see fit. | ||
|
||
The three core interfaces are: | ||
The `Gen` interface builds and creates the data and returns the `File` interface | ||
```go | ||
type Gen interface { | ||
Generate(cfg *Config) (File, error) | ||
} | ||
``` | ||
|
||
The `File` interface is a simple interface that just tells gogen where to put this file, and the data to write into it. | ||
```go | ||
type File interface { | ||
Path() string | ||
Data | ||
} | ||
|
||
type Data interface { | ||
Bytes() []byte | ||
} | ||
``` | ||
|
||
The `FileWriter` interface is an optional interface. Gogen will attempt to write the file if your type hasn't implemented it, but if you need a custom writing procedure to be done, like our `Go` type does, then implement this interface as well. | ||
```go | ||
type FileWriter interface { | ||
Write(file File) error | ||
} | ||
``` | ||
|
||
## Roadmap | ||
- Base file templating (conditional file generation) | ||
- List generation (generate n number of files from a list, like contacts) | ||
- Plugin support | ||
- More documentation | ||
- More tests | ||
- Further cleanup | ||
|
||
## Credit | ||
This library has been heavily inspired by [gqlgen](https://github.com/99designs/gqlgen). They did an amazing job at setting the foundation for this library and showing what's possible. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package gogen | ||
|
||
var ( | ||
DefaultConfig = Config{ | ||
GeneratedHeader: false, | ||
Description: "", | ||
generatedText: "// Code generated by github.com/randallmlough/gogen, DO NOT EDIT.\n\n", | ||
FileNotice: false, | ||
FileNoticeText: "// this was a generated file", | ||
RegionTags: false, | ||
SkipChildren: true, | ||
} | ||
) | ||
|
||
type Config struct { | ||
GeneratedHeader bool | ||
generatedText string | ||
// Description is documentation written after the generated header and before any template data | ||
Description string | ||
FileNotice bool | ||
// FileNotice is notice written below the package line | ||
FileNoticeText string | ||
RegionTags bool | ||
|
||
SkipChildren bool | ||
} | ||
|
||
func (c *Config) apply(cfg *Config) { | ||
c.generatedText = cfg.generatedText | ||
*cfg = *c | ||
} |
Oops, something went wrong.