gogen

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 4, 2020 License: MIT Imports: 13 Imported by: 2

README

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)
  • List generation (generate n number of files based on inputs)

Installation

go get github.com/randallmlough/gogen

Creating a single file

Creating a Go file

SIMPLE EXAMPLE

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

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

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.Doc{
		Template:     contents,
		Filename:     "examples/simple/output/file.yml",
		TemplateData: data,
	}
	gogen.Generate(doc)
}

The template

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

name: john snow
location: the wall
# make sure to checkout the template. I was conditionally rendered

favorites:
- dogs
- cold places
- sam

SIMPLE EXAMPLE

Generating files from a directory

DIRECTORY EXAMPLE

Let's say we have the following project structure...

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

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.Dir{
		OutputDir:   "examples/directory/output",
		TemplateDir: "examples/directory/templates",
	}
	if err := gogen.Generate(dir, gogen.SkipChildren(false)); err != nil {
		log.Fatal(err)
	}
}

Output structure

├── 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

Bundling files

BUNDLE EXAMPLE

We can also bundle any number of files into one file.

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

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

├── output
│   ├── bundle.go

Super cool!

BUNDLE EXAMPLE

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 File interface builds and creates the data and returns the Document interface

type File interface {
	Generate(cfg *Config) (Document, error)
}

The File interface is a simple interface that just tells gogen where to put this file, and the data to write into it.

type Document interface {
	Path() string
	Data
}

type Data interface {
	Bytes() []byte
}

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

type DocWriter interface {
	Write(file Document) 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. They did an amazing job at setting the foundation for this library and showing what's possible.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	DefaultConfig = Config{
		GeneratedHeader: false,

		Description:             "",
		FileNotice:              false,
		FileNoticeText:          "// this was a generated file",
		RegionTags:              false,
		SkipChildren:            true,
		TemplateExtensionSuffix: defaultTemplateExtension,
		// contains filtered or unexported fields
	}
)
View Source
var ErrTemplateExtensionEmpty = errors.New("Template extension can not be empty")

Functions

func Generate

func Generate(file File, opts ...Option) error

func LoadTemplate

func LoadTemplate(pathToTemplate string) (string, error)

func MustLoadTemplate added in v0.1.0

func MustLoadTemplate(pathToTemplate string) string

func Write

func Write(file Document) error

Types

type Config

type Config struct {
	GeneratedHeader bool

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

	TemplateData            interface{}
	TemplateFuncs           template.FuncMap
	TemplateExtensionSuffix string
	// contains filtered or unexported fields
}

type Data

type Data interface {
	Bytes() []byte
}

type Dir added in v0.1.0

type Dir struct {
	OutputDir    string
	TemplateDir  string
	TemplateData interface{}
}

func (*Dir) Files added in v0.1.0

func (d *Dir) Files(cfg *Config) ([]fileable, error)

func (*Dir) Generate added in v0.1.0

func (d *Dir) Generate(cfg *Config) (Document, error)

walk directories and create templates from files

func (*Dir) Name added in v0.1.0

func (d *Dir) Name() string

type Doc added in v0.1.0

type Doc struct {
	// Template is a string of the entire template that
	// will be parsed and rendered. If it's empty,
	// the plugin processor will look for .gotpl files
	// in the same directory of where you wrote the plugin.
	Template string
	Bundle   string
	// Filename is the name of the file that will be
	// written to the system disk once the template is rendered.
	Filename string

	Funcs template.FuncMap

	TemplateData interface{}
	// contains filtered or unexported fields
}

func (*Doc) Bytes added in v0.1.0

func (doc *Doc) Bytes() []byte

func (*Doc) Generate added in v0.1.0

func (doc *Doc) Generate(cfg *Config) (Document, error)

func (*Doc) Path added in v0.1.0

func (doc *Doc) Path() string

type DocWriter added in v0.1.0

type DocWriter interface {
	Write(file Document) error
}

type Docs added in v0.1.0

type Docs []Document

func (Docs) Bytes added in v0.1.0

func (d Docs) Bytes() []byte

should never be called. Used to implement Document interface

func (Docs) Path added in v0.1.0

func (d Docs) Path() string

should never be called. Used to implement Document interface

func (Docs) String added in v0.1.0

func (d Docs) String() string

should never be called. Used to implement Document interface

type Document

type Document interface {
	Path() string
	Data
}

type File

type File interface {
	Generate(cfg *Config) (Document, error)
}

type Go

type Go struct {
	// Template is a string of the entire template that
	// will be parsed and rendered. If it's empty,
	// the plugin processor will look for .gotpl files
	// in the same directory of where you wrote the plugin.
	Template string
	Bundle   string
	// Filename is the name of the file that will be
	// written to the system disk once the template is rendered.
	Filename string
	Funcs    template.FuncMap
	// PackageName is a helper that specifies the package header declaration.
	// In other words, when you write the template you don't need to specify `package X`
	// at the top of the file. By providing PackageName in the Options, the Generate
	// function will do that for you.
	PackageName string
	Imports     []string

	TemplateData interface{}
	// contains filtered or unexported fields
}

func (*Go) Bytes

func (g *Go) Bytes() []byte

func (*Go) Generate

func (g *Go) Generate(cfg *Config) (Document, error)

func (*Go) Path

func (g *Go) Path() string

func (*Go) Write

func (g *Go) Write(file Document) error

type Option

type Option interface {
	// contains filtered or unexported methods
}

func SetDescription

func SetDescription(desc string) Option

func SetGlobalFuncMap added in v0.1.0

func SetGlobalFuncMap(funcs template.FuncMap) Option

func SetGlobalTemplateData added in v0.1.0

func SetGlobalTemplateData(templateData interface{}) Option

func SetTemplateExtension added in v0.1.0

func SetTemplateExtension(extension string) Option

func SkipChildren

func SkipChildren(skip bool) Option

Directories

Path Synopsis
examples
simple/output
file generation is awesome
file generation is awesome

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL