Skip to content

Commit

Permalink
added MERGE object function (#111)
Browse files Browse the repository at this point in the history
* add pkg/stdlib/objects Length function

* rename lenght.go -> length.go

* fix tests according to other tests

* add new tests to length tests

* delete objects method Length

* add objects method Has

* add objects function Keys

* small fixes in Keys and Has functions

* change Has function

* unit tests for Keys function

* add unit tests for merge. also little change in lib.go

* add doc to Keys function

* Merge function prototype

* add unit tests for KEEP function

* added KEEP function

* added doc for KEYS function

* update lib.go

* update lib.go

* upd merge prototype

* addded isEqualObjects function to objects tests

* change object method Compare

* added unit tests for Compare method

* changed Compare method

* fix Compare method

* rename method Clone to Copy

* added Cloneable interface

* added Value to Cloneable interface

* implemented Cloneable intefrace by array

* added some more unit tests for values.Array

* fix values.Array.Compare method

* added one more unit test

* implemented Cloneable interface by Object

* unit tests for Object.Clone

* move core.IsCloneable to value.go

* change Clone function

* move IsClonable to package values

* updated MERGE unit tests

* added MERGE function

* added MERGE to lib

* added one more test

* changed MERGE function
  • Loading branch information
3timeslazy authored and ziflex committed Oct 13, 2018
1 parent 42757a2 commit 446ce3e
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 3 deletions.
7 changes: 4 additions & 3 deletions pkg/stdlib/objects/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import "github.com/MontFerret/ferret/pkg/runtime/core"

func NewLib() map[string]core.Function {
return map[string]core.Function{
"HAS": Has,
"KEYS": Keys,
"KEEP": Keep,
"HAS": Has,
"KEYS": Keys,
"KEEP": Keep,
"MERGE": Merge,
}
}
63 changes: 63 additions & 0 deletions pkg/stdlib/objects/merge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package objects

import (
"context"

"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)

/*
* Merge the given objects into a single object.
* @params objs (Array Of Object OR Objects) - objects to merge.
* @returns (Object) - Object created by merging.
*/
func Merge(_ context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, core.MaxArgs)

if err != nil {
return values.None, err
}

objs := values.NewArrayWith(args...)

if len(args) == 1 && args[0].Type() == core.ArrayType {
objs = args[0].(*values.Array)
}

err = validateArrayOf(core.ObjectType, objs)

if err != nil {
return values.None, err
}

return mergeArray(objs), nil
}

func mergeArray(arr *values.Array) *values.Object {
merged, obj := values.NewObject(), values.NewObject()

arr.ForEach(func(arrValue core.Value, arrIdx int) bool {
obj = arrValue.(*values.Object)
obj.ForEach(func(objValue core.Value, objKey string) bool {
if values.IsCloneable(objValue) {
objValue = objValue.(core.Cloneable).Clone()
}
merged.Set(values.NewString(objKey), objValue)
return true
})
return true
})

return merged
}

func validateArrayOf(typ core.Type, arr *values.Array) (err error) {
for idx := values.NewInt(0); idx < arr.Length(); idx++ {
if err != nil {
break
}
err = core.ValidateType(arr.Get(idx), typ)
}
return
}
165 changes: 165 additions & 0 deletions pkg/stdlib/objects/merge_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package objects_test

import (
"context"
"testing"

"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/stdlib/objects"

. "github.com/smartystreets/goconvey/convey"
)

func TestMerge(t *testing.T) {
Convey("When not enought arguments", t, func() {
obj, err := objects.Merge(context.Background())

So(err, ShouldBeError)
So(obj.Compare(values.None), ShouldEqual, 0)
})

Convey("When wrong type of arguments", t, func() {
obj, err := objects.Merge(context.Background(), values.NewInt(0))

So(err, ShouldBeError)
So(obj.Compare(values.None), ShouldEqual, 0)

obj, err = objects.Merge(context.Background(), values.NewObject(), values.NewInt(0))

So(err, ShouldBeError)
So(obj.Compare(values.None), ShouldEqual, 0)
})

Convey("When too many arrays", t, func() {
obj, err := objects.Merge(context.Background(), values.NewArray(0), values.NewArray(0))

So(err, ShouldBeError)
So(obj.Compare(values.None), ShouldEqual, 0)
})

Convey("Merged object should be independent of source objects", t, func() {
obj1 := values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(1)),
values.NewObjectProperty("prop2", values.NewString("str")),
)
obj2 := values.NewObjectWith(
values.NewObjectProperty("prop3", values.NewInt(3)),
)

result := values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(1)),
values.NewObjectProperty("prop2", values.NewString("str")),
values.NewObjectProperty("prop3", values.NewInt(3)),
)

merged, err := objects.Merge(context.Background(), obj1, obj2)

So(err, ShouldBeNil)

obj1.Remove(values.NewString("prop1"))

So(merged.Compare(result), ShouldEqual, 0)
})
}

func TestMergeObjects(t *testing.T) {
Convey("Merge single object", t, func() {
obj1 := values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(1)),
values.NewObjectProperty("prop2", values.NewString("str")),
)
result := values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(1)),
values.NewObjectProperty("prop2", values.NewString("str")),
)

merged, err := objects.Merge(context.Background(), obj1)

So(err, ShouldBeNil)
So(merged.Compare(result), ShouldEqual, 0)
})

Convey("Merge two objects", t, func() {
obj1 := values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(1)),
values.NewObjectProperty("prop2", values.NewString("str")),
)
obj2 := values.NewObjectWith(
values.NewObjectProperty("prop3", values.NewInt(3)),
)

result := values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(1)),
values.NewObjectProperty("prop2", values.NewString("str")),
values.NewObjectProperty("prop3", values.NewInt(3)),
)

merged, err := objects.Merge(context.Background(), obj1, obj2)

So(err, ShouldBeNil)
So(merged.Compare(result), ShouldEqual, 0)
})

Convey("When keys are repeated", t, func() {
obj1 := values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(1)),
values.NewObjectProperty("prop2", values.NewString("str")),
)
obj2 := values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(3)),
)
result := values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(3)),
values.NewObjectProperty("prop2", values.NewString("str")),
)

merged, err := objects.Merge(context.Background(), obj1, obj2)

So(err, ShouldBeNil)
So(merged.Compare(result), ShouldEqual, 0)
})
}

func TestMergeArray(t *testing.T) {
Convey("Merge array", t, func() {
objArr := values.NewArrayWith(
values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(1)),
),
values.NewObjectWith(
values.NewObjectProperty("prop2", values.NewInt(2)),
),
)
result := values.NewObjectWith(
values.NewObjectProperty("prop1", values.NewInt(1)),
values.NewObjectProperty("prop2", values.NewInt(2)),
)

merged, err := objects.Merge(context.Background(), objArr)

So(err, ShouldBeNil)
So(merged.Compare(result), ShouldEqual, 0)
})

Convey("Merge empty array", t, func() {
objArr := values.NewArray(0)
result := values.NewObject()

merged, err := objects.Merge(context.Background(), objArr)

So(err, ShouldBeNil)
So(merged.Compare(result), ShouldEqual, 0)
})

Convey("When there is not object element inside the array", t, func() {
objArr := values.NewArrayWith(
values.NewObject(),
values.NewInt(0),
)

obj, err := objects.Merge(context.Background(), objArr)

So(err, ShouldBeError)
So(obj.Compare(values.None), ShouldEqual, 0)
})
}

0 comments on commit 446ce3e

Please sign in to comment.