1
0
mirror of https://github.com/MontFerret/ferret.git synced 2024-12-14 11:23:02 +02:00

Feature/#95 deepclone (#101)

* rename method Clone to Copy

* added Cloneable interface

* added Value to Cloneable interface

* implemented Cloneable intefrace by array

* implemented Cloneable interface by Object

* unit tests for Object.Clone

* move core.IsCloneable to value.go

* change Clone function

* move IsClonable to package values
This commit is contained in:
3timeslazy 2018-10-12 18:58:08 +03:00 committed by Tim Voronov
parent 88188cac2c
commit f91fbf6f8c
19 changed files with 190 additions and 29 deletions

View File

@ -176,7 +176,7 @@ func (doc *HTMLDocument) Hash() uint64 {
return h.Sum64()
}
func (doc *HTMLDocument) Clone() core.Value {
func (doc *HTMLDocument) Copy() core.Value {
return values.None
}

View File

@ -276,7 +276,7 @@ func (el *HTMLElement) Value() core.Value {
return val
}
func (el *HTMLElement) Clone() core.Value {
func (el *HTMLElement) Copy() core.Value {
return values.None
}
@ -300,7 +300,7 @@ func (el *HTMLElement) GetAttributes() core.Value {
}
// returning shallow copy
return val.Clone()
return val.Copy()
}
func (el *HTMLElement) GetAttribute(name values.String) core.Value {

View File

@ -3,14 +3,15 @@ package static
import (
"bytes"
"context"
"net/http"
"net/url"
"github.com/MontFerret/ferret/pkg/html/common"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/PuerkitoBio/goquery"
"github.com/corpix/uarand"
"github.com/pkg/errors"
"github.com/sethgrid/pester"
"net/http"
"net/url"
)
type Driver struct {

View File

@ -2,11 +2,12 @@ package static
import (
"encoding/json"
"hash/fnv"
"github.com/MontFerret/ferret/pkg/html/common"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/PuerkitoBio/goquery"
"hash/fnv"
)
type HTMLElement struct {
@ -69,7 +70,7 @@ func (el *HTMLElement) Hash() uint64 {
return h.Sum64()
}
func (el *HTMLElement) Clone() core.Value {
func (el *HTMLElement) Copy() core.Value {
c, _ := NewHTMLElement(el.selection.Clone())
return c

View File

@ -0,0 +1,6 @@
package core
type Cloneable interface {
Value
Clone() Cloneable
}

View File

@ -46,7 +46,7 @@ type Value interface {
Compare(other Value) int
Unwrap() interface{}
Hash() uint64
Clone() Value
Copy() Value
}
func IsTypeOf(value Value, check Type) bool {

View File

@ -110,7 +110,7 @@ func (t *Array) Hash() uint64 {
return h.Sum64()
}
func (t *Array) Clone() core.Value {
func (t *Array) Copy() core.Value {
c := NewArray(len(t.value))
for _, el := range t.value {
@ -206,3 +206,18 @@ func (t *Array) RemoveAt(idx Int) {
t.value = append(t.value[:i], t.value[i+1:]...)
}
func (t *Array) Clone() core.Cloneable {
cloned := NewArray(0)
var value core.Value
for idx := NewInt(0); idx < t.Length(); idx++ {
value = t.Get(idx)
if IsCloneable(value) {
value = value.(core.Cloneable).Clone()
}
cloned.Push(value)
}
return cloned
}

View File

@ -502,4 +502,62 @@ func TestArray(t *testing.T) {
So(arr.Get(0), ShouldEqual, 1)
})
})
Convey(".Clone", t, func() {
Convey("Cloned array should be equal to source array", func() {
arr := values.NewArrayWith(
values.NewInt(0),
values.NewObjectWith(
values.NewObjectProperty("one", values.NewInt(1)),
),
values.NewArrayWith(
values.NewInt(2),
),
)
clone := arr.Clone().(*values.Array)
So(arr.Length(), ShouldEqual, clone.Length())
So(arr.Compare(clone), ShouldEqual, 0)
})
Convey("Cloned array should be independent of the source array", func() {
arr := values.NewArrayWith(
values.NewInt(0),
values.NewInt(1),
values.NewInt(2),
values.NewInt(3),
values.NewInt(4),
values.NewInt(5),
)
clone := arr.Clone().(*values.Array)
arr.Push(values.NewInt(6))
So(arr.Length(), ShouldNotEqual, clone.Length())
So(arr.Compare(clone), ShouldNotEqual, 0)
})
Convey("Cloned array must contain copies of the nested objects", func() {
arr := values.NewArrayWith(
values.NewArrayWith(
values.NewInt(0),
values.NewInt(1),
values.NewInt(2),
values.NewInt(3),
values.NewInt(4),
),
)
clone := arr.Clone().(*values.Array)
nestedInArr := arr.Get(values.NewInt(0)).(*values.Array)
nestedInArr.Push(values.NewInt(5))
nestedInClone := clone.Get(values.NewInt(0)).(*values.Array)
So(nestedInArr.Compare(nestedInClone), ShouldNotEqual, 0)
})
})
}

View File

@ -2,10 +2,11 @@ package values
import (
"encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/core"
"hash/fnv"
"io"
"io/ioutil"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
type Binary struct {
@ -72,7 +73,7 @@ func (b *Binary) Hash() uint64 {
return h.Sum64()
}
func (b *Binary) Clone() core.Value {
func (b *Binary) Copy() core.Value {
c := make([]byte, len(b.values))
copy(c, b.values)

View File

@ -2,10 +2,11 @@ package values
import (
"encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/pkg/errors"
"hash/fnv"
"strings"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/pkg/errors"
)
type Boolean bool
@ -108,6 +109,6 @@ func (t Boolean) Hash() uint64 {
return h.Sum64()
}
func (t Boolean) Clone() core.Value {
func (t Boolean) Copy() core.Value {
return t
}

View File

@ -1,9 +1,10 @@
package values
import (
"github.com/MontFerret/ferret/pkg/runtime/core"
"hash/fnv"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
const defaultTimeLayout = time.RFC3339
@ -109,6 +110,6 @@ func (t DateTime) Hash() uint64 {
return h.Sum64()
}
func (t DateTime) Clone() core.Value {
func (t DateTime) Copy() core.Value {
return NewDateTime(t.Time)
}

View File

@ -4,11 +4,12 @@ import (
"encoding/binary"
"encoding/json"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/pkg/errors"
"hash/fnv"
"math"
"strconv"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/pkg/errors"
)
type Float float64
@ -127,6 +128,6 @@ func (t Float) Hash() uint64 {
return h.Sum64()
}
func (t Float) Clone() core.Value {
func (t Float) Copy() core.Value {
return t
}

View File

@ -2,9 +2,10 @@ package values
import (
"encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/core"
"reflect"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
)
func GetIn(from core.Value, byPath []core.Value) (core.Value, error) {
@ -294,3 +295,14 @@ func ToBoolean(input core.Value) core.Value {
return True
}
}
func IsCloneable(value core.Value) Boolean {
switch value.Type() {
case core.ArrayType:
return NewBoolean(true)
case core.ObjectType:
return NewBoolean(true)
default:
return NewBoolean(false)
}
}

View File

@ -3,10 +3,11 @@ package values
import (
"encoding/binary"
"encoding/json"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/pkg/errors"
"hash/fnv"
"strconv"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/pkg/errors"
)
type Int int
@ -125,6 +126,6 @@ func (t Int) Hash() uint64 {
return h.Sum64()
}
func (t Int) Clone() core.Value {
func (t Int) Copy() core.Value {
return t
}

View File

@ -37,6 +37,6 @@ func (t *none) Hash() uint64 {
return 0
}
func (t *none) Clone() core.Value {
func (t *none) Copy() core.Value {
return None
}

View File

@ -141,7 +141,7 @@ func (t *Object) Hash() uint64 {
return h.Sum64()
}
func (t *Object) Clone() core.Value {
func (t *Object) Copy() core.Value {
c := NewObject()
for k, v := range t.value {
@ -202,3 +202,20 @@ func (t *Object) Remove(key String) {
func (t *Object) SetIn(path []core.Value, value core.Value) error {
return SetIn(t, path, value)
}
func (t *Object) Clone() core.Cloneable {
cloned := NewObject()
var value core.Value
var keyString String
for key := range t.value {
keyString = NewString(key)
value, _ = t.Get(keyString)
if IsCloneable(value) {
value = value.(core.Cloneable).Clone()
}
cloned.Set(keyString, value)
}
return cloned
}

View File

@ -300,4 +300,49 @@ func TestObject(t *testing.T) {
So(v.Compare(values.NewInt(1)), ShouldEqual, 0)
})
})
Convey(".Clone", t, func() {
Convey("Cloned object should be equal to source object", func() {
obj := values.NewObjectWith(
values.NewObjectProperty("one", values.NewInt(1)),
values.NewObjectProperty("two", values.NewInt(2)),
)
clone := obj.Clone().(*values.Object)
So(obj.Compare(clone), ShouldEqual, 0)
})
Convey("Cloned object should be independent of the source object", func() {
obj := values.NewObjectWith(
values.NewObjectProperty("one", values.NewInt(1)),
values.NewObjectProperty("two", values.NewInt(2)),
)
clone := obj.Clone().(*values.Object)
obj.Remove(values.NewString("one"))
So(obj.Compare(clone), ShouldNotEqual, 0)
})
Convey("Cloned object must contain copies of the nested objects", func() {
obj := values.NewObjectWith(
values.NewObjectProperty(
"arr", values.NewArrayWith(values.NewInt(1)),
),
)
clone := obj.Clone().(*values.Object)
nestedInObj, _ := obj.Get(values.NewString("arr"))
nestedInObjArr := nestedInObj.(*values.Array)
nestedInObjArr.Push(values.NewInt(2))
nestedInClone, _ := clone.Get(values.NewString("arr"))
nestedInCloneArr := nestedInClone.(*values.Array)
So(nestedInObjArr.Compare(nestedInCloneArr), ShouldNotEqual, 0)
})
})
}

View File

@ -3,10 +3,11 @@ package values
import (
"encoding/json"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/pkg/errors"
"hash/fnv"
"strings"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/pkg/errors"
)
type String string
@ -103,7 +104,7 @@ func (t String) Hash() uint64 {
return h.Sum64()
}
func (t String) Clone() core.Value {
func (t String) Copy() core.Value {
return t
}

View File

@ -72,7 +72,7 @@ func Unshift(_ context.Context, args ...core.Value) (core.Value, error) {
if !ok {
// value is not unique, just return a new copy with same elements
return arr.Clone(), nil
return arr.Copy(), nil
}
}