Skip to content

Commit

Permalink
v0.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
randallmlough committed May 29, 2020
0 parents commit 88de9a2
Show file tree
Hide file tree
Showing 68 changed files with 3,577 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# logs and outputs
*.test
*.out

# IDEs
.idea/

# misc
.DS_Store
21 changes: 21 additions & 0 deletions LICENSE
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.
291 changes: 291 additions & 0 deletions README.md
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.
31 changes: 31 additions & 0 deletions config.go
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
}
Loading

0 comments on commit 88de9a2

Please sign in to comment.