user2312578
user2312578

Reputation:

Explain go generate in this example

I'm having difficulties understanding go generate. I also find barely any posts dealing with go generate.

please explain go generate in this following example:

package main

import (
        "gopkg.in/mgo.v2"
        "gopkg.in/mgo.v2/bson"
)

// --- Address

type Address struct {
        Id            bson.ObjectId `bson:"_id,omitempty"`
        AccountId     string        `bson:"account_id"`
        Name          string        `bson:"name"`
        StreetAddress string        `bson:"streetaddress"`
        Town          string        `bson:"town"`
        Country       string        `bson:"country"`
}

// --- AddressHandler

type AddressHandler struct {
        MS *mgo.Session
}

func NewAddressHandler(ms *mgo.Session) *AddressHandler {
        return &AddressHandler{MS: ms.Clone()}
}

func (h *AddressHandler) Close() {
        h.MS.Close()
}

// Add

type AddAddressInput struct {
        Address *Address
}

type AddAddressOutput struct {
        Error error
}

func (h *AddressHandler) AddAddress(in *AddAddressInput, out *AddAddressOutput) {
        ms := h.MS.Copy()
        defer ms.Close()
        c := ms.DB("").C("address")
        out.Error = c.Insert(in.Address)
}

// Remove

type RemoveAddressInput struct {
        AddressId string
}

type RemoveAddressOutput struct {
        Error error
}

func (h *AddressHandler) RemoveAddress(in *RemoveAddressInput, out *RemoveAddressOutput) {
        ms := h.MS.Copy()
        defer ms.Close()
        c := ms.DB("").C("address")
        out.Error = c.RemoveId(bson.ObjectIdHex(in.AddressId))
}

// Update

type UpdateAddressInput struct {
        Address *Address
}

type UpdateAddressOutput struct {
        Error error
}

func (h *AddressHandler) UpdateAddress(in *UpdateAddressInput, out *UpdateAddressOutput) {
        ms := h.MS.Copy()
        defer ms.Close()
        c := ms.DB("").C("address")
        out.Error = c.UpdateId(in.Address.AccountId)
}

// GetAllByAccount

type GetAddressInput struct {
        AccountId string
}

type GetAddressOutput struct {
        Address []*Address
        Error   error
}

func (h *AddressHandler) GetAddress(in *GetAddressInput, out *GetAddressOutput) {
        ms := h.MS.Copy()
        defer ms.Close()
        c := ms.DB("").C("address")
        out.Error = c.Find(bson.ObjectIdHex(in.AccountId)).All(&out.Address)
}

I would like to create almost carbon copies of this not yet template code.

the "template" code:

package main

import (
        "gopkg.in/mgo.v2"
        "gopkg.in/mgo.v2/bson"
)

// --- Address

type %Model% struct {
        Id            bson.ObjectId `bson:"_id,omitempty"`
}

// --- %Model%Handler

type %Model%Handler struct {
        MS *mgo.Session
}

func New%Model%Handler(ms *mgo.Session) *%Model%Handler {
        return &%Model%Handler{MS: ms.Clone()}
}

func (h *%Model%Handler) Close() {
        h.MS.Close()
}

// Add

type Add%Model%Input struct {
        %Model% *%Model%
}

type Add%Model%Output struct {
        Error error
}

func (h *%Model%Handler) Add%Model%(in *Add%Model%Input, out *Add%Model%Output) {
        ms := h.MS.Copy()
        defer ms.Close()
        c := ms.DB("").C("%Model%")
        out.Error = c.Insert(in.%Model%)
}

// Remove %Model%

type Remove%Model%Input struct {
        %Model%Id string
}

type Remove%Model%Output struct {
        Error error
}

func (h *%Model%Handler) Remove%Model%(in *Remove%Model%Input, out *Remove%Model%Output) {
        ms := h.MS.Copy()
        defer ms.Close()
        c := ms.DB("").C("%Model%")
        out.Error = c.RemoveId(bson.ObjectIdHex(in.%Model%Id))
}

// Update

type Update%Model%Input struct {
        %Model% *%Model%
}

type Update%Model%Output struct {
        Error error
}

func (h *%Model%Handler) Update%Model%(in *Update%Model%Input, out *Update%Model%Output) {
        ms := h.MS.Copy()
        defer ms.Close()
        c := ms.DB("").C("%Model%")
        out.Error = c.UpdateId(in.%Model%.AccountId)
}

// GetAllByAccount

type Get%Model%Input struct {
        AccountId string
}

type Get%Model%Output struct {
        %Model% []*%Model%
        Error   error
}

func (h *%Model%Handler) Get%Model%(in *Get%Model%Input, out *Get%Model%Output) {
        ms := h.MS.Copy()
        defer ms.Close()
        c := ms.DB("").C("%Model%")
        out.Error = c.Find(bson.ObjectIdHex(in.AccountId)).All(&out.%Model%)
}

What do I need to add or change so I can go generate output from this supposed template. As you can see everything Address is replaced with %Model%.

Upvotes: 11

Views: 13283

Answers (1)

AkiRoss
AkiRoss

Reputation: 12273

I am not expert with go generate, but AFAIK, go generate is called to perform commands specified in buildable go files, normally with the intent to produce something new.

Generate scans for files searching for a specific directive: //go:generate and, if found, it will execute the command following it.

To understand better what's happening, let's make a simple example: a template go file will have a string to be replaced.

Example 1

Let's make a command that replace the template string, NAME, with another string, AkiRoss:

repl.sh

#!/usr/bin/sh
sed "s/NAME/AkiRoss/g" $1 > $2

Here follows the go template, note the directive:

templ.go

package main

import "fmt"

//go:generate ./repl.sh $GOFILE aki_$GOFILE

func main() {
    fmt.Println("Hello,", NAME)
}

Both files are in the same directory, for convenience, and repl.sh is executable. If I run go generate in the directory, the go tool will call repl.sh templ.go aki_templ.go, being $GOFILE expanded to be the name of the file processed by generate.

Here's what I get:

aki_templ.go

package main

import "fmt"

//go:generate ./repl.sh $GOFILE aki_$GOFILE

func main() {
        fmt.Println("Hello,", AkiRoss)
}

Example 2

Regarding your example, you will need to place the //go:generate directive somewhere. It is likely, however, that the directive will be included in a different file, not the template file, that calls a replacement script, similar to the one I made, to produce a file which is needed for building.

Let me explain this better by changing my example:

repl.sh

#!/usr/bin/sh
sed "s/%NAME%/$3/g" $1 > $2

templ.txt

// This is a template for a go file
package main

import "fmt"

type %NAME% struct {
    foo string
    bar int
}

func (self *%NAME%) Perform() {
    fmt.Println(self.foo, self.bar)
}

main.go

package main

import "fmt"

//go:generate ./repl.sh templ.txt foobar.go FooBar

func main() {
    var fb = FooBar{"AkiRoss", -1}
    fmt.Println("Running!")
    fb.Perform()
}

Running go generate will produce a new file

foobar.go

// This is a template for a go file
package main

import "fmt"

type FooBar struct {
        foo string
        bar int
}

func (self *FooBar) Perform() {
        fmt.Println(self.foo, self.bar)
}

which allows now to compile correctly the main:

$ go build
$ ./program
Running!
AkiRoss -1

I hope this clarified.

References

More details here, and a better example is here.

Upvotes: 18

Related Questions