perri.to: A mashup of things

Starting with reflection

  2016-02-26


Reflection

Some time ago, I decided that I wanted to learn a little about reflection in Go. Being a typed language, it is not common the need to discover details about a given type.

This said, sometimes it might be useful every now and then (and I am sure there are a lot of other aspects I cannot think of where reflection is the perfect fit) so what I did is get myself a fun exercise to learn some reflection. My learning exercise was goSQLMarshal but that would not be a clean sample to talk about, since it mixes a lot of SQL and, as usual when one is learning, I might have thrown a lot of useless code in the middle to try new things.

Additional information about reflection can be found here

The whole code for this example can be found in its own github repo

Practical sample

To make this tutorial easier to understand, I created a whole new sample project that should have the least amount of clutter possible without making it ugly. The samples will include testing, as this is something that most howtos miss and I feel adds some value.

The problem at hand is: An HTML form from Go structs creator It will include:

  • Creation of HTML5 form fields for very basic go types (string, int[64], float[64]).
  • Creation of HTML5 form with its fields filled with the values from the passed go struct.

The project

Among other tings, Reflection can help us know more about a struct fields and values. As you know, interfaces help us constraint variables to types that implement certain methods but there is nothing that can be done for Fields and this is where reflection can come in handy.

Getting The fields

The first thing we are going to do is determine the fields on the type we want to convert into a form, to make this example smaller and les convoluted we will only support strings, ints and floats.

// getFields will return a map of the field names and their HTML
// form input type.
// Since this function is only for educational purposes it handles
// only the most basic types and will return error when a more complex
// one is passed.
func getFields(goType POSTFillable) (map[string]string, error) {
	// Only valid for structs for the purposes of this example.
	t := reflect.TypeOf(goType)
	// We require a struct for this sample so if this is a ptr, lets get
	// the concrete type pointed to.
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		return nil, fmt.Errorf("interface must be a struct is %v", t.Kind())
	}
	fieldCount := t.NumField()
	// we know how many fields there are, we assume there are all valid.
	ret := make(map[string]string, fieldCount)
	for i := 0; i < fieldCount; i++ {
		f := t.Field(i)
		// Normally I could very well use the type and a function with
		// a switch going over the possible types here
		// but it would make this example a bit more complicated.
		fieldHTMLType, valid := GOHTML[f.Type.Name()]
		if !valid {
			return nil, fmt.Errorf("cannot find an HTML equivalent for %q of type ", f.Name)
		}

		// Adding tags to an attribute you can add extra metadata.
		tag := f.Tag.Get("html")
		if fieldHTMLType == HTMLFieldText {
			switch tag {
			case TagSecret:
				fieldHTMLType = HTMLFieldPassword
			case TagEmail:
				fieldHTMLType = HTMLFieldEmail
			case TagURL:
				fieldHTMLType = HTMLFieldURL
			case TagTel:
				fieldHTMLType = HTMLFieldTel
			}
		}

		ret[f.Name] = fieldHTMLType
	}
	return ret, nil
}

Lets break this down a bit.

	// Only valid for structs for the purposes of this example.
	t := reflect.TypeOf(goType)
	// We require a struct for this sample so if this is a ptr, lets get
	// the concrete type pointed to.
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		return nil, fmt.Errorf("interface must be a struct is %v", t.Kind())
	}

goType is a POSTFillable interface (ill explain this later) but this is mostly because i dont like to use interface{}. The first thing we want to get, is information about the type so, even though goType is an intance of its type, what we care now, its about the type itself. As you can see in the doc TypeOf returns a reflect.Type that is basically an struct that tells us a lot about the structure of our type.

This sample will only work with structs or pointer to structs, mostly because it makes no much sense to make a form from other types, for instance, an integer.

When you want to learn something about a given type, having a pointer to the type tells you very little, luckily reflect helps us solve this issue.

The reflect.Type struct knows about the kind of the underlying type, kind can be one of these if it is reflect.Ptr or reflect.Interface your can request the reflect.Type of the concrete type by using reflect.Type.Elem(), beware, this will break if the Kind() is not one of the aforementioned.

Once you have the concrete element, we check that its kind is a Struct, which is the scope of our example.

	fieldCount := t.NumField()
	// we know how many fields there are, we assume there are all valid.
	ret := make(map[string]string, fieldCount)

After making sure we have the Kind of object we want, we check its fields, reflect.Type.NumFields() returns the quantity of fields in the object.

Since we are doing this for educational purposes, we assume that all of them are of valid types and just create the result set.

Then, for each of the fields, we will try to get the relevant information.

		f := t.Field(i)
		// Normally I could very well use the type and a function with
		// a switch going over the possible types here
		// but it would make this example a bit more complicated.
		fieldHTMLType, valid := GOHTML[f.Type.Name()]
		if !valid {
			return nil, fmt.Errorf("cannot find an HTML equivalent for %q of type ", f.Name)
		}
We can get fields by index, this will give us a reflect.Value this can only be done, once again, for structs. Having a handle of the fields, allows us to learn some metadata from its type, which we use to determine if it is one of the ones we support.

Then we proceed to extract a bit more useful data.

		// Adding tags to an attribute you can add extra metadata.
		tag := f.Tag.Get("html")
		if fieldHTMLType == HTMLFieldText {
			switch tag {
			case TagSecret:
				fieldHTMLType = HTMLFieldPassword
			case TagEmail:
				fieldHTMLType = HTMLFieldEmail
			case TagURL:
				fieldHTMLType = HTMLFieldURL
			case TagTel:
				fieldHTMLType = HTMLFieldTel
			}
		}

Before adding the name/type to our result map, we check if a tag was not used to add a bit more metadata that can be used by our generator.

Citing this doc:

By convention, tag strings are a concatenation of optionally space-separated key:"value" pairs. Each key is a non-empty string consisting of non-control characters other than space (U+0020 ' '), quote (U+0022 '"'), and colon (U+003A ':'). Each value is quoted using U+0022 '"' characters and Go string literal syntax.

Tags are quite useful, if we are creating a package that relies in reflection, the users of the package can tag their types as a way to give extra information, an example of this is gojson that relies on tags to determine the name of a map field in a json and if the field is included in the encoding when it is empty. In this sample we will support a few tags to let the user specify which variant of text field is a string going to be converted to.

The supported Tags are:

const (
	TagSecret = "secret"
	TagEmail  = "email"
	TagURL    = "url"
	TagTel    = "telephone"
)

They modify if a string is going to be converted into any of:

const (
	HTMLFieldText     = "text" // the default string
	HTMLFieldPassword = "password" // TagSecret
	HTMLFieldEmail    = "email" // TagEmail
	HTMLFieldURL      = "url" // TagURL
	HTMLFieldTel      = "tel" // TagTel This seems to be useful in phone browsers
)

The tests

Any code is broken unless there is a test that proves that wrong, so lets assert that what I said above, is true.

The aproach I like to take when testing is to go through all the possible failures that a given logic might have, a function test should have at least as many tests as exit points. Testing the succesful path is never very useful, it is the one you most likely put more effort into crafting and therefore the one less likely to fail so ideally we want to make sure our functionality behaves correctly under un-ideal conditions and perhaps we can even discover a few unexpected behavior and correct them while leaving behind a test as proof that the code is intended to handle that situation.

Another useful rule, if you find a breaking point, write it into a test, then fix it that way you avoid regressions.

type wrongFormValue int

func (w *wrongFormValue) ProcessPOST(map[string]string) error {
	return nil
}

func TestGetFieldsFailsWithoutStruct(t *testing.T) {
	var testValue wrongFormValue
	testValue = 1
	_, err := getFields(&testValue)
	expectedMessage := "interface must be a struct is int"
	errorMessage := err.Error()
	if errorMessage != expectedMessage {
		t.Logf("error should be %q but is %q", expectedMessage, errorMessage)
		t.Fail()
	}
}

One of the things I said most is that this works for structs and struct pointers only so the code should take care to reject any other kind of type even if it satisfies the requested interface.

In this case I create a type from int and take care to satisfy the interface, but I check that the function fails anyway because it knows the type is actually an int.

type rightForm struct {
}

func (r *rightForm) ProcessPOST(map[string]string) error {
	return nil
}

func TestGetFieldsDereferencesPtr(t *testing.T) {
	testValue := rightForm{}
	_, err := getFields(&testValue)
	if err != nil {
		t.Logf("error should be nil, is: %v", err)
		t.Fail()
	}
}

Another important thing I said is that our function should be able to handle both structs and pointer to structs, so lets pass a pointer to a valid (yet empty) struct and make sure that the function does not fail.

And finally, the path of least resistence, and ideal case.

type formWithFields struct {
	FirstName string
	LastName  string
	Email     string `html:"email"`
	Website   string `html:"url"`
	Phone     string `html:"telephone"`
	Password  string `html:"secret"`
	Age       int
}

func (f formWithFields) ProcessPOST(map[string]string) error {
	return nil
}

func TestGetFieldsReturnsFields(t *testing.T) {
	testValue := formWithFields{}
	result, err := getFields(testValue)
	if err != nil {
		t.Logf("error should be nil, is: %v", err)
		t.Fail()
	}
	resultLenght := len(result)
	if resultLenght != 7 {
		t.Logf("result should contain 7 fields, contains %d", resultLenght)
		t.Fail()
	}
	expected := map[string]string{
		"Email":     "email",
		"Website":   "url",
		"Phone":     "tel",
		"Password":  "password",
		"Age":       "number",
		"FirstName": "text",
		"LastName":  "text",
	}
	for fieldName, fieldType := range expected {
		value, ok := result[fieldName]
		if !ok {
			t.Logf("key %q should be present in the map but is not", fieldName)
			t.Logf("obtained map is %#v", result)
			t.Logf("expected map is %#v", expected)
			t.Fail()
		}
		if value != fieldType {
			t.Logf("expected field type for field %q is %q but the obtained values is %q", fieldName, fieldType, value)
			t.Fail()
		}
	}
}

The test for the ideal case can be a bit more lax here, we write the ideal input (or even better a series of ideal inputs so you can properly exercise as much permutations as you think wise) and test that all the correct paths have ben triggered, in this case:

  • Struct is converted to a map of fields.
  • Error output is nil.
  • There are no more fields than expected.
  • The fields expected are there.
  • The fields expected have the expected types.

Bear in mind, that maps are not ordered in go (they might seem so in practice sometimes with older version of go, don’t fall into that trap) so you need to test with that in mind.

Also, when your success test is written, break expectancy to make sure all checks actually work before deeming it finished.

Making the HTML

This part is not related to reflection at all so you might want to skip it but I felt it was nicer to provide the full example.

Once we got the map with all the requierd properties, we need to create the HTML which is, after all, the goal of our toy project.

The creation is a simple template use.

const (
	inputTemplate          = `<label for="{{.Name}}">{{.Name}}</label><input name="{{.Name}}" type="{{.Type}}" />`
	formTemplate           = `<form name="goform" method="POST">%s</form>` // This is not really a template.
)

var (
	htmlLineTemplate          = template.Must(template.New("htmlInputLine").Parse(inputTemplate))
)
// htmlInput represents the data that can be used in htmlLoneTemplate.
type htmlInput struct {
	Name  string
	Type  string
	Value string
}

// doHTML returns a string containing the HTML form for the passed
// fields map.
func doHTML(fields map[string]string) string {
	htmlLines := make([]string, len(fields))
	fieldNames := make([]string, len(fields))
	fieldNo := 0
	for fieldName := range fields {
		fieldNames[fieldNo] = fieldName
		fieldNo++
	}
	sort.Sort(sort.StringSlice(fieldNames))
	for lineNo, fieldName := range fieldNames {
		fieldType := fields[fieldName]
		field := htmlInput{
			Name: fieldName,
			Type: fieldType,
		}
		var htmlLine bytes.Buffer
		htmlLineTemplate.Execute(&htmlLine, field)
		htmlLines[lineNo] = htmlLine.String()
	}
	return fmt.Sprintf(formTemplate, strings.Join(htmlLines, "\n"))
}

I believe there is not much to explain here, you can find a very complete documentation on text/template here. This use of template is very basic, the only things note worthy are htmlInput type that holds the variables the template will use. The other bit that might be interesting is the sorting, it is done mainly because maps dont have an order in go (even though older versions of go might make you think otherwise when tested empirically) and if we sort the fields we can afterwards obtain a consistent HTML (it is confusing to have a form that is different each time you see it)

And now to make sure this does what we said:

func TestDoHTMLReturnsExpectedHTML(t *testing.T) {
	inputMap := map[string]string{
		"Email":     "email",
		"Website":   "url",
		"Phone":     "tel",
		"Password":  "password",
		"Age":       "number",
		"FirstName": "text",
		"LastName":  "text",
	}
	expectedHTML := `<form name="goform" method="POST"><label for="Age">Age</label><input name="Age" type="number" />
<label for="Email">Email</label><input name="Email" type="email" />
<label for="FirstName">FirstName</label><input name="FirstName" type="text" />
<label for="LastName">LastName</label><input name="LastName" type="text" />
<label for="Password">Password</label><input name="Password" type="password" />
<label for="Phone">Phone</label><input name="Phone" type="tel" />
<label for="Website">Website</label><input name="Website" type="url" /></form>`
	outputHTML := doHTML(inputMap)
	if outputHTML != expectedHTML {
		t.Logf("got: \n%q\nexpected: \n%q", outputHTML, expectedHTML)
		t.Fail()
	}
}

There is not much more to test here other than the fact that all the fields are in there and that the order is the desired one, luckily we can test all those with just using the expected HTML which is ordered and complete. This could be improved by using several different structs and perhaps each one several times to make sure order is respected everytime, but that would be pretty much testing go’s sort given the simplicity of the function.

The resulting HTML and Form:


<form name="goform" method="POST">
    <label for="Age">Age</label><input name="Age" type="number" />
    <label for="Email">Email</label><input name="Email" type="email" />
    <label for="FirstName">FirstName</label><input name="FirstName" type="text" />
    <label for="LastName">LastName</label><input name="LastName" type="text" />
    <label for="Password">Password</label><input name="Password" type="password" />
    <label for="Phone">Phone</label><input name="Phone" type="tel" />
    <label for="Website">Website</label><input name="Website" type="url" />
</form>


Getting the values

You did not think that it was over, did you? one important part of introspection of a type, is being able to obtain not only the type attributes, but their values. In small tools like marshalers the structure is half of the job, the values to fill it are also very important (or in the case of unmarshaling too)

To complete this part, I decided to join what we learned from the other two functions and make one that gives us a form with the fields filled by the struct attribute values, which makes much more sense.

// valueStringer tries to return a string representing the value
// of the passed reflect.Value and a boolean indicating if it
// was possible.
func valueStringer(value reflect.Value) (string, bool) {
	var stringValue string
	switch value.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		v := value.Int()
		stringValue = fmt.Sprintf("%d", v)
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		v := value.Uint()
		stringValue = fmt.Sprintf("%d", v)
	case reflect.Float32, reflect.Float64:
		v := value.Float()
		stringValue = fmt.Sprintf("%f", v)
	case reflect.String:
		stringValue = value.String()
	default:
		return "", false
	}
	return stringValue, true

}

// doFilledForm returns a string containing an HTML form with values
// extracted from the passed POSTFillable or error if this is not possible.
func doFilledForm(contents POSTFillable) (string, error) {
	fields, err := getFields(contents)
	if err != nil {
		return "", fmt.Errorf("cannot obtain fields: %v", err)
	}
	t := reflect.ValueOf(contents)
	// We require a struct for this sample so if this is a ptr, lets get
	// the concrete type pointed to.
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	htmlLines := make([]string, len(fields))
	fieldNames := make([]string, len(fields))
	fieldNo := 0
	for fieldName := range fields {
		fieldNames[fieldNo] = fieldName
		fieldNo++
	}
	for lineNo, fieldName := range fieldNames {
		fieldType := fields[fieldName]
		value, valid := valueStringer(t.FieldByName(fieldName))
		if !valid {
			return "", fmt.Errorf("cannot determine the value for %q", fieldName)
		}
		field := htmlInput{
			Name:  fieldName,
			Type:  fieldType,
			Value: value,
		}
		var htmlLine bytes.Buffer
		htmlLineWithValueTemplate.Execute(&htmlLine, field)
		htmlLines[lineNo] = htmlLine.String()
	}
	return fmt.Sprintf(formTemplate, strings.Join(htmlLines, "\n")), nil
}

Now this works much more like a finished job and provides exactly what one would need. There is a bit of code repetition but I made some concessions in that aspect for the sake of a clearer example and to avoid creating useless abstracions that end up taking a lot of parameters and returning a lot more information than needed.

The relevant differences from its simpler predecesors are:

	t := reflect.ValueOf(contents)
	// We require a struct for this sample so if this is a ptr, lets get
	// the concrete type pointed to.
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

We use reflect.ValueOf instead of reflect.TypeOf

		value, valid := valueStringer(t.FieldByName(fieldName))
		if !valid {
			return "", fmt.Errorf("cannot determine the value for %q", fieldName)
		}

From the reflect.Value item obtained earlier, we can ask FieldByName and that in time will allow us to obtain the final value.

As we can see, there is a rudimentary type conversion (the call to valueStringer that will handle the known types:

	switch value.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		v := value.Int()
		stringValue = fmt.Sprintf("%d", v)
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		v := value.Uint()
		stringValue = fmt.Sprintf("%d", v)
	case reflect.Float32, reflect.Float64:
		v := value.Float()
		stringValue = fmt.Sprintf("%f", v)
	case reflect.String:
		stringValue = value.String()
	default:
		return "", false
	}

The simple statement takes in the ouput of FieldByName and based on its kind it obtains the actual type element and converts that to a string (since we need strings to fill in the HTML form)

The remaning code only varies on the intantiated htmlInput

		field := htmlInput{
			Name:  fieldName,
			Type:  fieldType,
			Value: value,
		}

It contains the extra struct field Value that will be used in the template for this function.

And to make sure this function work, we test that which differs from its predecessors.

func TestDoFillHTMLReturnsExpectedHTML(t *testing.T) {
	inputStruct := formWithFields{
		FirstName: "Horacio",
		LastName:  "Duran",
		Email:     "horacio.duran@gmail.com",
		Website:   "http://perri.to",
		Phone:     "+555555555",
		Password:  "a big secret",
		Age:       32,
	}
	expectedHTML := `<form name="goform" method="POST"><label for="FirstName">FirstName</label><input name="FirstName" type="text" value="Horacio" />
<label for="LastName">LastName</label><input name="LastName" type="text" value="Duran" />
<label for="Email">Email</label><input name="Email" type="email" value="horacio.duran@gmail.com" />
<label for="Website">Website</label><input name="Website" type="url" value="http://perri.to" />
<label for="Phone">Phone</label><input name="Phone" type="tel" value="+555555555" />
<label for="Password">Password</label><input name="Password" type="password" value="a big secret" />
<label for="Age">Age</label><input name="Age" type="number" value="32" /></form>`
	outputHTML, err := doFilledForm(inputStruct)
	if err != nil {
		t.Logf("no error expected, obtained %v", err)
		t.Fail()
	}
	fmt.Println(outputHTML)
	if outputHTML != expectedHTML {
		t.Logf("got: \n%q\nexpected: \n%q", outputHTML, expectedHTML)
		t.Fail()
	}
}

For this particular example, the test will check that the produced HTML form has the right fields in the right order and populated with the right values, but in real life you should also test the error exit points.

This is the resulting form:

<form name="goform" method="POST"><label for="FirstName">FirstName</label>
    <input name="FirstName" type="text" value="Horacio" />
    <label for="LastName">LastName</label><input name="LastName" type="text" value="Duran" />
    <label for="Email">Email</label><input name="Email" type="email" value="horacio.duran@gmail.com" />
    <label for="Website">Website</label><input name="Website" type="url" value="http://perri.to" />
    <label for="Phone">Phone</label><input name="Phone" type="tel" value="+555555555" />
    <label for="Password">Password</label><input name="Password" type="password" value="a big secret" />
    <label for="Age">Age</label><input name="Age" type="number" value="32" />
</form>


I hope this was somehow useful to understand the very basic of reflection and that the whole wrapping was not more distracting than useful.

The whole code for this example can be found in its own github repo

comments powered by Disqus