Capture or assign golang template output to variable - go

Within a template, how can I achieve this?
{{$var := template "my-template"}}
I just get "unexpected <template> in operand".

There is no "builtin" action for getting the result of a template execution, but you may do it by registering a function which does that.
You can register functions with the Template.Funcs() function, you may execute a named template with Template.ExecuteTemplate() and you may use a bytes.Buffer as the target (direct template execution result into a buffer).
Here is a complete example:
var t *template.Template
func execTempl(name string) (string, error) {
buf := &bytes.Buffer{}
err := t.ExecuteTemplate(buf, name, nil)
return buf.String(), err
}
func main() {
t = template.Must(template.New("").Funcs(template.FuncMap{
"execTempl": execTempl,
}).Parse(tmpl))
if err := t.Execute(os.Stdout, nil); err != nil {
panic(err)
}
}
const tmpl = `{{define "my-template"}}my-template content{{end}}
See result:
{{$var := execTempl "my-template"}}
{{$var}}
`
Output (try it on the Go Playground):
See result:
my-template content
The "my-template" template is executed by the registered function execTempl(), and the result is returned as a string, which is stored in the $var template variable, which then is simply added to the output, but you may use it to pass to other functions if you want to.

Related

What type should result be in golang smart contract binding call

I am calling this code:
package multicall
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)
var multicallContractAddress = common.HexToAddress("0x5e227AD1969Ea493B43F840cfF78d08a6fc17796")
var multicallContractEthBalanceSelector = "4d2301cc"
func GetBalances(addresses []string, ETHProviderURL string) ([]string, error) {
ethProvider, err := ethclient.Dial(ETHProviderURL)
if err != nil {
panic(err)
}
multicallContract, err := NewMulticallCaller(multicallContractAddress, ethProvider)
if err != nil {
panic(err)
}
fmt.Println("multicallContract: ", multicallContract)
var calls = []MulticallCall{}
for _, address := range addresses {
hashAddress := common.HexToHash(address)
call := MulticallCall{multicallContractAddress, []byte("0x"+multicallContractEthBalanceSelector+hashAddress.String()[2:])}
calls = append(calls, call)
}
fmt.Println(string(calls[0].CallData))
var results []byte
err = multicallContract.contract.Call(&bind.CallOpts{}, results, "aggregate", common.Hex2Bytes(addresses[0]))
if err != nil {
panic(err)
}
fmt.Println("Result: ", &results)
return nil, nil
}
like this:
multicall.GetBalances([]string{"<MY_ADDRESS>"}, "<INFURA_API_KEY>")
And it returns this error:
# github.com/mteam88/keyswarm/multicall
multicall/multicall.go:37:58: cannot use results (variable of type []byte) as type *[]interface{} in argument to multicallContract.contract.Call
It seems like results should be a specific type/interface (maybe []uint8) but I can't seem to pass it like that to the Call function.
I have tried tons of things and I can't seem to get it. My Google diving has uncovered this Why am I getting a compile error 'cannot use ... as type uint8 in argument to ...' when the parameter is an int which led me to my previous assumption.
If you -1 please comment (:
Please provide links to relevant documentation in your answer.
NOTE: https://github.com/mteam88/keyswarm/blob/multicall/multicall/multicallContract.go is the location of the defenitions of MulticallCall and other Multicall prefixed defenitions.

Creating yaml files from template in golang

I want to create a yaml file from a current tmpl file. Basically I want to insert values in the sample.tmpl files stored in the /templates folder and create a new yaml file in the same folder sample.yml
My sample.tmpl looks like
url : {{ .host }}
namespace: {{ .namespace }}
I'm using the following function:
func ApplyTemplate(filePath string) (err error) {
// Variables - host, namespace
type Eingest struct {
host string
namespace string
}
ei := Eingest{host: "example.com", namespace: "finance"}
var templates *template.Template
var allFiles []string
files, err := ioutil.ReadDir(filePath)
if err != nil {
fmt.Println(err)
}
for _, file := range files {
filename := file.Name()
fullPath := filePath + "/" + filename
if strings.HasSuffix(filename, ".tmpl") {
allFiles = append(allFiles, fullPath)
}
}
fmt.Println("Files in path: ", allFiles)
// parses all .tmpl files in the 'templates' folder
templates, err = template.ParseFiles(allFiles...)
if err != nil {
fmt.Println(err)
}
s1 := templates.Lookup("sample.tmpl")
s1.ExecuteTemplate(os.Stdout, "sample.yml", ei)
fmt.Println()
return
}
s1.ExecuteTemplate() writes to stdout. How can I create a new file in the same folder? I believe something similar is used to build kubernetes yaml files. How do we achieve this using golang template package?
First: since you've already looked up the template, you should use template.Execute instead, but the same works with ExecuteTemplate.
text.Template.Execute takes a io.Writer as first argument. This is an interface with a single method: Write(p []byte) (n int, err error).
Any type that has that method implements the interface and can be used as a valid argument. One such type is a os.File. Simply create a new os.File object and pass it to Execute as follows:
// Build the path:
outputPath := filepath.Join(filepath, "sample.yml")
// Create the file:
f, err := os.Create(outputPath)
if err != nil {
panic(err)
}
defer f.Close() // don't forget to close the file when finished.
// Write template to file:
err = s1.Execute(f, ei)
if err != nil {
panic(err)
}
Note: don't forget to check whether s1 is nil, as documented in template.Lookup.

Golang - create an object of the same type as passed

I'm trying to build a generic function which will parse input (in JSON) into a specified structure. The structure may vary at run-time, based on parameters which are passed to the function. I'm currently trying to achieve this by passing an object of the right type and using reflect.New() to create a new output object of the same type.
I'm then parsing the JSON into this object, and scanning the fields.
If I create the object and specify the type in code, everything works. If I pass an object and try to create a replica, I get an "invalid indirect" error a few steps down (see code).
import (
"fmt"
"reflect"
"encoding/json"
"strings"
)
type Test struct {
FirstName *string `json:"FirstName"`
LastName *string `json:"LastName"`
}
func genericParser(incomingData *strings.Reader, inputStructure interface{}) (interface{}, error) {
//******* Use the line below and things work *******
//parsedInput := new(Test)
//******* Use vvv the line below and things don't work *******
parsedInput := reflect.New(reflect.TypeOf(inputStructure))
decoder := json.NewDecoder(incomingData)
err := decoder.Decode(&parsedInput)
if err != nil {
//parsing error
return nil, err
}
//******* This is the line that generates the error "invalid indirect of parsedInput (type reflect.Value)" *******
contentValues := reflect.ValueOf(*parsedInput)
for i := 0; i < contentValues.NumField(); i++ {
//do stuff with each field
fmt.Printf("Field name was: %s\n", reflect.TypeOf(parsedInput).Elem().Field(i).Name)
}
return parsedInput, nil
}
func main() {
inputData := strings.NewReader("{\"FirstName\":\"John\", \"LastName\":\"Smith\"}")
exampleObject := new(Test)
processedData, err := genericParser(inputData, exampleObject)
if err != nil {
fmt.Println("Parsing error")
} else {
fmt.Printf("Success: %v", processedData)
}
}
If I can't create a replica of the object, then a way of updating / returning the one supplied would be feasible. The key thing is that this function must be completely agnostic to the different structures available.
reflect.New isn't a direct analog to new, as it can't return a specific type, it only can return a reflect.Value. This means that you are attempting to unmarshal into a *reflect.Value, which obviously isn't going to work (even if it did, your code would have passed in **Type, which isn't what you want either).
Use parsedInput.Interface() to get the underlying value after creating the new value to unmarshal into. You then don't need to reflect on the same value a second time, as that would be a reflect.Value of a reflect.Value, which again isn't going to do anything useful.
Finally, you need to use parsedInput.Interface() before you return, otherwise you are returning the reflect.Value rather than the value of the input type.
For example:
func genericParser(incomingData io.Reader, inputStructure interface{}) (interface{}, error) {
parsedInput := reflect.New(reflect.TypeOf(inputStructure).Elem())
decoder := json.NewDecoder(incomingData)
err := decoder.Decode(parsedInput.Interface())
if err != nil {
return nil, err
}
for i := 0; i < parsedInput.Elem().NumField(); i++ {
fmt.Printf("Field name was: %s\n", parsedInput.Type().Elem().Field(i).Name)
}
return parsedInput.Interface(), nil
}
https://play.golang.org/p/CzDrj6sgQNt

How do I use olivere's elastic client.Update() service to change array fields, generic string fields and possibly nested struct fields?

https://github.com/olivere/elastic
Version 5.x
The wiki documentation isn't really clear on how client.Update() works. It's needed to completely change a field and to modify arrays. i.e. in the example in the wiki documentation, how would one go about appending and removing tags to a tweet or changing a tweet's content? Also if a tweet was represented in go as a struct and I added a nested struct called "echo" which contains a foo of type int, content of type string and another type string array, how would one go about changing any of these fields using client.Update() if it's even possible?
In my personal example I have this function:
func UpdateEntryContent(eclient *elastic.Client, entryID string, newContent []rune) error{
ctx:=context.Background()
exists, err := eclient.IndexExists(ENTRY_INDEX).Do(ctx)
if err != nil {return err}
if !exists {return errors.New("Index does not exist")}
_, err = eclient.Update().Index(ENTRY_INDEX).Type(ENTRY_TYPE).Id(entryID).
Script("ctx._source.Content = newCont").
ScriptParams(map[string]interface{}{"newCont": newContent}).
Do(ctx)
if err != nil {return err}
return nil
}
But I get this following error when I try to compile:
cannot use "ctx._source.Content = newCont" (type string) as type *elastic.Script in argument to eclient.Update().Index(ENTRY_INDEX).Type(ENTRY_TYPE).Id(entryID).Script
eclient.Update().Index(ENTRY_INDEX).Type(ENTRY_TYPE).Id(entryID).Script("ctx._source.Content = newCont").ScriptParams undefined (type *elastic.UpdateService has no field or method ScriptParams)
The Script method accepts a *elastic.Script, not a string. The ScriptParams method is also found on *elastic.Script as Params instead of being on *elastic.UpdateService.
func UpdateEntryContent(eclient *elastic.Client, entryID string, newContent []rune) error{
ctx:=context.Background()
exists, err := eclient.IndexExists(ENTRY_INDEX).Do(ctx)
if err != nil {return err}
if !exists {return errors.New("Index does not exist")}
script := elastic.NewScript("ctx._source.Content = newCont").Params(map[string]interface{}{"newCont": newContent})
_, err = eclient.Update().Index(ENTRY_INDEX).Type(ENTRY_TYPE).Id(entryID).
Script(script).
Do(ctx)
if err != nil {return err}
return nil
}
You can see more information about the package with GoDoc or by looking through the source code.
The following code should resolve the issue
_, err = eclient.Update().Index(INDEX).
Type(TYPE).
Id(ID).
Doc(map[string]interface{}{field: message}).
Do(ctx)
Credit where it's due Gavin's answer put me on the right track. This is for another .Index but the full function that acts as a generic single field update is as follows:
func UpdateUser(eclient *elastic.Client, userID string, field string, newContent interface{})error {
//CHANGES A SINGLE FIELD OF ES USER DOCUMENT(requires an elastic client pointer,
// the user DocID, the feild you wish to modify as a string,
// and what you want to change that field to as any type necessary)
//RETURN AN error IF SUCESSFUL error = nil
ctx := context.Background()
exists, err := eclient.IndexExists(USER_INDEX).Do(ctx)
if err != nil {return err}
if !exists {return errors.New("Index does not exist")}
_, err = eclient.Update().
Index(USER_INDEX).
Type(USER_TYPE).
Id(userID).
Doc(map[string]interface{}{field: newContent}).
Do(ctx)
return nil
}
You can change the .Index, .Type, and .Id and it works with all fields and types as far as I can tell

Differing behaviors for ParseFiles functions in html/template

I don't understand why the behaviors of func (t *Template) Parsefiles(... differs from func ParseFiles(.... Both functions are from the "html/template" package.
package example
import (
"html/template"
"io/ioutil"
"testing"
)
func MakeTemplate1(path string) *template.Template {
return template.Must(template.ParseFiles(path))
}
func MakeTemplate2(path string) *template.Template {
return template.Must(template.New("test").ParseFiles(path))
}
func TestExecute1(t *testing.T) {
tmpl := MakeTemplate1("template.html")
err := tmpl.Execute(ioutil.Discard, "content")
if err != nil {
t.Error(err)
}
}
func TestExecute2(t *testing.T) {
tmpl := MakeTemplate2("template.html")
err := tmpl.Execute(ioutil.Discard, "content")
if err != nil {
t.Error(err)
}
}
This exits with the error:
--- FAIL: TestExecute2 (0.00 seconds)
parse_test.go:34: html/template:test: "test" is an incomplete or empty template
FAIL
exit status 1
Note that TestExecute1 passed fine so this not a problem with template.html.
What's going on here?
What am I missing in MakeTemplate2?
It's because of the template names. Template objects can hold multiple teplates, each has a name. When using template.New("test"), and then Executing it, it will try to execute a template called "test" inside that template. However, tmpl.ParseFiles stores the template to the name of the file. That explains the error message.
How to fix it:
a) Give the template the correct name:
Use
return template.Must(template.New("template.html").ParseFiles(path))
instead of
return template.Must(template.New("test").ParseFiles(path))
b) Specify, which template you want to execute in you Template object:
Use
err := tmpl.ExecuteTemplate(ioutil.Discard, "template.html", "content")
instead of
err := tmpl.Execute(ioutil.Discard, "content")
Read more about this in http://golang.org/pkg/text/template/

Resources