mirror of
https://github.com/MontFerret/ferret.git
synced 2025-11-25 22:01:39 +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:
@@ -176,7 +176,7 @@ func (doc *HTMLDocument) Hash() uint64 {
|
|||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (doc *HTMLDocument) Clone() core.Value {
|
func (doc *HTMLDocument) Copy() core.Value {
|
||||||
return values.None
|
return values.None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ func (el *HTMLElement) Value() core.Value {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
func (el *HTMLElement) Clone() core.Value {
|
func (el *HTMLElement) Copy() core.Value {
|
||||||
return values.None
|
return values.None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +300,7 @@ func (el *HTMLElement) GetAttributes() core.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returning shallow copy
|
// returning shallow copy
|
||||||
return val.Clone()
|
return val.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (el *HTMLElement) GetAttribute(name values.String) core.Value {
|
func (el *HTMLElement) GetAttribute(name values.String) core.Value {
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ package static
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/html/common"
|
"github.com/MontFerret/ferret/pkg/html/common"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
"github.com/corpix/uarand"
|
"github.com/corpix/uarand"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sethgrid/pester"
|
"github.com/sethgrid/pester"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Driver struct {
|
type Driver struct {
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ package static
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"hash/fnv"
|
||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/html/common"
|
"github.com/MontFerret/ferret/pkg/html/common"
|
||||||
"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/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
"hash/fnv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTMLElement struct {
|
type HTMLElement struct {
|
||||||
@@ -69,7 +70,7 @@ func (el *HTMLElement) Hash() uint64 {
|
|||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (el *HTMLElement) Clone() core.Value {
|
func (el *HTMLElement) Copy() core.Value {
|
||||||
c, _ := NewHTMLElement(el.selection.Clone())
|
c, _ := NewHTMLElement(el.selection.Clone())
|
||||||
|
|
||||||
return c
|
return c
|
||||||
|
|||||||
6
pkg/runtime/core/cloneable.go
Normal file
6
pkg/runtime/core/cloneable.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
type Cloneable interface {
|
||||||
|
Value
|
||||||
|
Clone() Cloneable
|
||||||
|
}
|
||||||
@@ -46,7 +46,7 @@ type Value interface {
|
|||||||
Compare(other Value) int
|
Compare(other Value) int
|
||||||
Unwrap() interface{}
|
Unwrap() interface{}
|
||||||
Hash() uint64
|
Hash() uint64
|
||||||
Clone() Value
|
Copy() Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsTypeOf(value Value, check Type) bool {
|
func IsTypeOf(value Value, check Type) bool {
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ func (t *Array) Hash() uint64 {
|
|||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Array) Clone() core.Value {
|
func (t *Array) Copy() core.Value {
|
||||||
c := NewArray(len(t.value))
|
c := NewArray(len(t.value))
|
||||||
|
|
||||||
for _, el := range 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:]...)
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -502,4 +502,62 @@ func TestArray(t *testing.T) {
|
|||||||
So(arr.Get(0), ShouldEqual, 1)
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ package values
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Binary struct {
|
type Binary struct {
|
||||||
@@ -72,7 +73,7 @@ func (b *Binary) Hash() uint64 {
|
|||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Binary) Clone() core.Value {
|
func (b *Binary) Copy() core.Value {
|
||||||
c := make([]byte, len(b.values))
|
c := make([]byte, len(b.values))
|
||||||
|
|
||||||
copy(c, b.values)
|
copy(c, b.values)
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ package values
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Boolean bool
|
type Boolean bool
|
||||||
@@ -108,6 +109,6 @@ func (t Boolean) Hash() uint64 {
|
|||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Boolean) Clone() core.Value {
|
func (t Boolean) Copy() core.Value {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package values
|
package values
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultTimeLayout = time.RFC3339
|
const defaultTimeLayout = time.RFC3339
|
||||||
@@ -109,6 +110,6 @@ func (t DateTime) Hash() uint64 {
|
|||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t DateTime) Clone() core.Value {
|
func (t DateTime) Copy() core.Value {
|
||||||
return NewDateTime(t.Time)
|
return NewDateTime(t.Time)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Float float64
|
type Float float64
|
||||||
@@ -127,6 +128,6 @@ func (t Float) Hash() uint64 {
|
|||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Float) Clone() core.Value {
|
func (t Float) Copy() core.Value {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ package values
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetIn(from core.Value, byPath []core.Value) (core.Value, error) {
|
func GetIn(from core.Value, byPath []core.Value) (core.Value, error) {
|
||||||
@@ -294,3 +295,14 @@ func ToBoolean(input core.Value) core.Value {
|
|||||||
return True
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package values
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Int int
|
type Int int
|
||||||
@@ -125,6 +126,6 @@ func (t Int) Hash() uint64 {
|
|||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Int) Clone() core.Value {
|
func (t Int) Copy() core.Value {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,6 @@ func (t *none) Hash() uint64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *none) Clone() core.Value {
|
func (t *none) Copy() core.Value {
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ func (t *Object) Hash() uint64 {
|
|||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Object) Clone() core.Value {
|
func (t *Object) Copy() core.Value {
|
||||||
c := NewObject()
|
c := NewObject()
|
||||||
|
|
||||||
for k, v := range t.value {
|
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 {
|
func (t *Object) SetIn(path []core.Value, value core.Value) error {
|
||||||
return SetIn(t, path, value)
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -300,4 +300,49 @@ func TestObject(t *testing.T) {
|
|||||||
So(v.Compare(values.NewInt(1)), ShouldEqual, 0)
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package values
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type String string
|
type String string
|
||||||
@@ -103,7 +104,7 @@ func (t String) Hash() uint64 {
|
|||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t String) Clone() core.Value {
|
func (t String) Copy() core.Value {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func Unshift(_ context.Context, args ...core.Value) (core.Value, error) {
|
|||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
// value is not unique, just return a new copy with same elements
|
// value is not unique, just return a new copy with same elements
|
||||||
return arr.Clone(), nil
|
return arr.Copy(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user