Other methods callable on Maps (#254)

Background:

When methods are attached on a map type like so:
type Foo map[string]string

func (f Foo) Bar() {
	fmt.Printf("Hello World");
}

vm := otto.New();

vm.Set("foo", Foo{});
vm.Run(`
    foo.Bar();
`);

You get:
Error in Run: TypeError: 'Bar' is not a function

The Fix:

I looked into how/why the same works for arrays. After
all array properties are tested (such as length, and any integer-based members),
the code then looks for any methods attached to that type.

This change literally copies that code over into
maps.

This is very useful when working with the http.Request object which
has the http.Header type that is a map[string][]string, with a lot
of useful methods attached to it.

Added unit test to support/guard the change (and map had no test before)

Responded to PR comments
This commit is contained in:
Archis
2017-06-02 02:05:44 -07:00
committed by Steven Hartland
parent bc4cd51799
commit e1afcee0b0
2 changed files with 48 additions and 0 deletions

View File

@@ -53,6 +53,14 @@ func goMapGetOwnProperty(self *_object, name string) *_property {
return &_property{self.runtime.toValue(value.Interface()), 0111}
}
// Other methods
if method := self.value.(*_goMapObject).value.MethodByName(name); (method != reflect.Value{}) {
return &_property{
value: self.runtime.toValue(method.Interface()),
mode: 0110,
}
}
return nil
}

40
type_go_map_test.go Normal file
View File

@@ -0,0 +1,40 @@
package otto
import (
"fmt"
"sort"
"testing"
)
type GoMapTest map[string]int
func (s GoMapTest) Join() string {
joinedStr := ""
// Ordering the map takes some effort
// because map iterators in golang are unordered by definition.
// So we need to extract keys, sort them, and then generate K/V pairs
// All of this is meant to ensure that the test is predictable.
keys := make([]string, len(s))
i := 0
for key, _ := range s {
keys[i] = key
i++
}
sort.Strings(keys)
for _, key := range keys {
joinedStr += key + ": " + fmt.Sprintf("%d", s[key]) + " "
}
return joinedStr
}
func TestGoMap(t *testing.T) {
tt(t, func() {
test, vm := test()
vm.Set("TestMap", GoMapTest{"one": 1, "two": 2, "three": 3})
is(test(`TestMap["one"]`).export(), 1)
is(test(`TestMap.Join()`).export(), "one: 1 three: 3 two: 2 ")
})
}