1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-07-17 01:32:22 +02:00

refactoring: .GetIn (#353)

* sync with MontFerret/ferret

* fix --param handling

When params is converted to map it uses strings.Split,
which slices a string into all substrings separated by :.

* implement GetIn for values.Object

* implement GetIn for values.Array

* rewrite GetIn because values.Object and values.Array implement core.Getter now

* fix bug when GetIn return nil instead of None

* add tests for Object and Array .GetIn

* add GetIn comment and remove 'len(byPath)' check

* fix GetIn comment
This commit is contained in:
3timeslazy
2019-08-21 05:00:15 +03:00
committed by Tim Voronov
parent 1c2096ae7e
commit f87fe1e669
5 changed files with 237 additions and 39 deletions

View File

@ -1,6 +1,7 @@
package values package values
import ( import (
"context"
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"hash/fnv" "hash/fnv"
@ -258,3 +259,29 @@ func (t *Array) SortWith(sorter ArraySorter) *Array {
return res return res
} }
func (t *Array) GetIn(ctx context.Context, path []core.Value) (core.Value, error) {
if len(path) == 0 {
return None, nil
}
if typ := path[0].Type(); typ != types.Int {
return None, core.TypeError(typ, types.Int)
}
first := t.Get(path[0].(Int))
if len(path) == 1 {
return first, nil
}
getter, ok := first.(core.Getter)
if !ok {
return None, core.TypeError(
first.Type(),
core.NewType("Getter"),
)
}
return getter.GetIn(ctx, path[1:])
}

View File

@ -1,12 +1,14 @@
package values_test package values_test
import ( import (
"context"
"encoding/json" "encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
"testing" "testing"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
) )
@ -561,4 +563,90 @@ func TestArray(t *testing.T) {
So(nestedInArr.Compare(nestedInClone), ShouldNotEqual, 0) So(nestedInArr.Compare(nestedInClone), ShouldNotEqual, 0)
}) })
}) })
Convey(".GetIn", t, func() {
ctx := context.Background()
Convey("Should return the same as .Get when input is correct", func() {
Convey("Should return item by key", func() {
key := values.NewInt(0)
arr := values.NewArrayWith(
values.NewInt(0),
)
el, err := arr.GetIn(ctx, []core.Value{key})
elGet := arr.Get(key)
So(err, ShouldBeNil)
So(el.Compare(elGet), ShouldEqual, 0)
})
Convey("Should return None when no items", func() {
key := values.NewInt(0)
arr := values.NewArray(0)
el, err := arr.GetIn(ctx, []core.Value{key})
elGet := arr.Get(key)
So(err, ShouldBeNil)
So(el.Compare(elGet), ShouldEqual, 0)
})
})
Convey("Should error when input is not correct", func() {
Convey("Should error when path[0] is not an int", func() {
arr := values.NewArray(0)
path := []core.Value{values.NewString("")}
el, err := arr.GetIn(ctx, path)
So(err, ShouldBeError)
So(el.Compare(values.None), ShouldEqual, 0)
})
Convey("Should error when first received item is not a Getter and len(path) > 1", func() {
key := values.NewInt(0)
arr := values.NewArrayWith(
values.NewInt(1),
)
path := []core.Value{key, key}
el, err := arr.GetIn(ctx, path)
So(err, ShouldBeError)
So(el.Compare(values.None), ShouldEqual, 0)
})
})
Convey("Should return None when len(path) == 0", func() {
arr := values.NewArrayWith(
values.NewInt(1),
)
el, err := arr.GetIn(ctx, nil)
So(err, ShouldBeNil)
So(el.Compare(values.None), ShouldEqual, 0)
})
Convey("Should call the nested Getter", func() {
key := values.NewInt(0)
arr := values.NewArrayWith(
values.NewObjectWith(
values.NewObjectProperty("foo", key),
),
)
el, err := arr.GetIn(ctx, []core.Value{
key, // obj[0]
values.NewString("foo"), // obj[0].foo
})
So(err, ShouldBeNil)
So(el.Compare(key), ShouldEqual, 0)
})
})
} }

View File

@ -14,46 +14,18 @@ import (
"github.com/MontFerret/ferret/pkg/runtime/values/types" "github.com/MontFerret/ferret/pkg/runtime/values/types"
) )
// GetIn checks that from implements core.Getter interface. If it implements,
// GetIn call from.GetIn method, otherwise return error.
func GetIn(ctx context.Context, from core.Value, byPath []core.Value) (core.Value, error) { func GetIn(ctx context.Context, from core.Value, byPath []core.Value) (core.Value, error) {
if len(byPath) == 0 { getter, ok := from.(core.Getter)
return None, nil if !ok {
return None, core.TypeError(
from.Type(),
core.NewType("Getter"),
)
} }
var result = from return getter.GetIn(ctx, byPath)
for i, segment := range byPath {
if result == None || result == nil {
break
}
segType := segment.Type()
switch segVal := result.(type) {
case *Object:
if segType != types.String {
return nil, core.TypeError(segType, types.String)
}
result, _ = segVal.Get(segment.(String))
case *Array:
if segType != types.Int {
return nil, core.TypeError(segType, types.Int)
}
result = segVal.Get(segment.(Int))
case core.Getter:
return segVal.GetIn(ctx, byPath[i:])
default:
return None, core.TypeError(
from.Type(),
types.Array,
types.Object,
core.NewType("Getter"),
)
}
}
return result, nil
} }
func SetIn(ctx context.Context, to core.Value, byPath []core.Value, value core.Value) error { func SetIn(ctx context.Context, to core.Value, byPath []core.Value, value core.Value) error {

View File

@ -1,6 +1,7 @@
package values package values
import ( import (
"context"
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"hash/fnv" "hash/fnv"
@ -267,3 +268,29 @@ func (t *Object) Clone() core.Cloneable {
return cloned return cloned
} }
func (t *Object) GetIn(ctx context.Context, path []core.Value) (core.Value, error) {
if len(path) == 0 {
return None, nil
}
if typ := path[0].Type(); typ != types.String {
return None, core.TypeError(typ, types.String)
}
first, _ := t.Get(path[0].(String))
if len(path) == 1 {
return first, nil
}
getter, ok := first.(core.Getter)
if !ok {
return None, core.TypeError(
first.Type(),
core.NewType("Getter"),
)
}
return getter.GetIn(ctx, path[1:])
}

View File

@ -1,11 +1,13 @@
package values_test package values_test
import ( import (
"github.com/MontFerret/ferret/pkg/runtime/values/types" "context"
"testing" "testing"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
) )
@ -408,4 +410,86 @@ func TestObject(t *testing.T) {
So(nestedInObjArr.Compare(nestedInCloneArr), ShouldNotEqual, 0) So(nestedInObjArr.Compare(nestedInCloneArr), ShouldNotEqual, 0)
}) })
}) })
Convey(".GetIn", t, func() {
ctx := context.Background()
Convey("Should return the same as .Get when input is correct", func() {
Convey("Should return item by key", func() {
key := values.NewString("foo")
obj := values.NewObjectWith(
values.NewObjectProperty(key.String(), values.NewInt(1)),
)
el, err := obj.GetIn(ctx, []core.Value{key})
elGet, _ := obj.Get(key)
So(err, ShouldBeNil)
So(el.Compare(elGet), ShouldEqual, 0)
})
Convey("Should return None when no items", func() {
key := values.NewString("foo")
obj := values.NewObject()
el, err := obj.GetIn(ctx, []core.Value{key})
elGet, _ := obj.Get(key)
So(err, ShouldBeNil)
So(el.Compare(elGet), ShouldEqual, 0)
})
})
Convey("Should error when input is not correct", func() {
Convey("Should error when path[0] is not a string", func() {
obj := values.NewObject()
path := []core.Value{values.NewInt(0)}
el, err := obj.GetIn(ctx, path)
So(err, ShouldBeError)
So(el.Compare(values.None), ShouldEqual, 0)
})
Convey("Should error when first received item is not a Getter and len(path) > 1", func() {
key := values.NewString("foo")
obj := values.NewObjectWith(
values.NewObjectProperty(key.String(), values.NewInt(1)),
)
path := []core.Value{key, key}
el, err := obj.GetIn(ctx, path)
So(err, ShouldBeError)
So(el.Compare(values.None), ShouldEqual, 0)
})
})
Convey("Should return None when len(path) == 0", func() {
obj := values.NewObject()
el, err := obj.GetIn(ctx, nil)
So(err, ShouldBeNil)
So(el.Compare(values.None), ShouldEqual, 0)
})
Convey("Should call the nested Getter", func() {
key := values.NewString("foo")
obj := values.NewObjectWith(
values.NewObjectProperty(key.String(), values.NewArrayWith(key)),
)
el, err := obj.GetIn(ctx, []core.Value{
key, // obj.foo
values.NewInt(0), // obj.foo[0]
})
So(err, ShouldBeNil)
So(el.Compare(key), ShouldEqual, 0)
})
})
} }