Part 011 An Amazing EQ Function For Go Templates

My research on Go Templates lead me into the code for the templates and then to some new Google searches. This truly amazing eq function was the key for me understating that you could call a function from a template with more than one argument.

An example (ex11.go):

package main

// (C) Philip Schlump, 2013.
// MIT Licensed, see LICENSE.txt

// Example of calling a function that has an array of interface as a parameter.

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"os"
	"reflect"
)

// -----------------------------------------------------------------------------
//
// Test of "eq"
// I didn't write "eq" - I think that it is amazing!
//
// -----------------------------------------------------------------------------
//
// From:  https://groups.google.com/forum/#!topic/golang-nuts/OEdSDgEC7js
// By:Russ Cox
//
//		Comment by:
//		Rob 'Commander' Pike
//		Date: 4/4/12
//
//		I would like to point out the loveliness of Russ's eq function. The
//		beauty is concentrated in the range loop, where x==y is an interface
//		equality check that verifies type and value equality while avoiding
//		allocation. It is enabled by the "case string, int, ..." etc. line,
//		which does the type check but leaves x of interface type.
//		By the way, there could be more types on that case; if you borrow this
//		function, be sure to add the types you need.
//
//		-rob
//
// I have found this function to be useful in my templates.
// I install it as "eq".
//
// eq reports whether the first argument is equal to one of the suceeding arguments.
//
func eq(args ...interface{}) bool {
	if len(args) == 0 {
		return false
	}
	x := args[0]
	switch x := x.(type) {
	case string, int, int64, byte, float32, float64:
		for _, y := range args[1:] {
			if x == y {
				return true
			}
		}
		return false
	}

	for _, y := range args[1:] {
		if reflect.DeepEqual(x, y) {
			return true
		}
	}
	return false
}

func readInFile(path string) string {
	file, err := ioutil.ReadFile(path)
	if err != nil {
		return ""
	}
	return string(file)
}

func readInJson(path string) map[string]interface{} {
	var jsonData map[string]interface{}
	file, err := ioutil.ReadFile(path)
	if err != nil {
		return jsonData
	}
	json.Unmarshal(file, &jsonData)
	return jsonData
}

func main() {

	if len(os.Args) != 3 {
		fmt.Printf("Usage: ex11 TemplateFileName Data.JSON\n")
		os.Exit(1)
	}

	var tmpl_fn string = os.Args[1] // Look for *literal* in the template
	var data_fn string = os.Args[2]

	tmpl := readInFile(tmpl_fn)
	person := readInJson(data_fn)

	funcMap := template.FuncMap{
		"eq": eq,
	}

	t := template.New("file-template").Funcs(funcMap)
	t, err := t.Parse(tmpl)
	if err != nil {
		panic(err)
		os.Exit(1)
	}

	err = t.ExecuteTemplate(os.Stdout, "file-template", person)
	if err != nil {
		panic(err)
		os.Exit(1)
	}
}


In this example the function bob is called from inside the template.

To run this you can

$ go run ex11.go ex11.tmpl ex11.json


To compile it and run it

$ go build ex11.go
$ ./ex11 ex11.tmpl ex11.json

The output you should expect from running it is (ex11.out):


This shows that the "pipeline" is the last parameter to a function

output


Now let's try this with our function!

eq(1) = false : should be false
eq(2) = true : should be true
 


I can tell you that I don't (at least not yet) understand all the reflection and how it all works in the eq function. I am going to be doing a lot of reading and studying!

In any case - this is both useful and interesting. The original comment in the news group left me wondering just how you actually used the function. Remember that I was under the misconception that all functions called from the Go Template processor were one string parameter in one string out.

Hopefully having an entire program that uses and demonstrates how the eq works will help.

Code tested on: Ubuntu 12.04, Mac 11.8, Windows 7 in go 1.1.2


•       •       •       •       •       •

Summary: # of Words: 283
Author: Philip J. Schlump
Published On: 2013-10-23

Download code from this articles in .tar.gz for Mac/Linux/Unix or .zip with CR/LF for Windows format.

 

Before You Go....

Have you read "Unintend Consinsequences"?

"I laughed so hard it hurt..."
    Rod Brown

"Incredibly funny! Incredibly true!"
    Tad Stevens