This is an automated email from the ASF dual-hosted git repository. Cole-Greer pushed a commit to branch GValueFollowupTP4 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 61407da00fde087ec41cc0bcb2805f8c265b517d Author: Cole Greer <[email protected]> AuthorDate: Wed Jun 10 16:26:24 2026 -0700 Replace Go GValue constructor with an exported-field struct Drops NewGValue and the unexported fields/getters in favor of a plain struct with exported Name and Value fields (construct via GValue{Name: ..., Value: ...}), which is more idiomatic Go. The String() and IsNil() helpers are retained; the Name()/Value() getters are removed as they are redundant with the exported fields. This removes the client-side nested-GValue guard that lived in the constructor, so Go no longer fails fast on a nested GValue (it surfaces as a downstream serialization error instead). Updates GremlinLang, the cucumber step, the unit tests, and the Go GValue doc example accordingly. Assisted-by: Kiro:claude-opus-4.8 --- docs/src/reference/gremlin-variants.asciidoc | 2 +- gremlin-go/driver/cucumber/cucumberSteps_test.go | 2 +- gremlin-go/driver/gValue.go | 33 +++--------- gremlin-go/driver/gValue_test.go | 65 +++++------------------- gremlin-go/driver/gremlinlang.go | 6 +-- 5 files changed, 27 insertions(+), 81 deletions(-) diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc index 521a7017bf..21a833b04f 100644 --- a/docs/src/reference/gremlin-variants.asciidoc +++ b/docs/src/reference/gremlin-variants.asciidoc @@ -303,7 +303,7 @@ the traversal to utilize a parameter in place of a literal. [source,go] ---- -g.V().Has("name", gremlingo.NewGValue("name", "marko")) +g.V().Has("name", gremlingo.GValue{Name: "name", Value: "marko"}) ---- [[gremlin-go-scripts]] diff --git a/gremlin-go/driver/cucumber/cucumberSteps_test.go b/gremlin-go/driver/cucumber/cucumberSteps_test.go index 41286b85a7..1b2f58a2d3 100644 --- a/gremlin-go/driver/cucumber/cucumberSteps_test.go +++ b/gremlin-go/driver/cucumber/cucumberSteps_test.go @@ -921,7 +921,7 @@ func (tg *tinkerPopGraph) usingTheParameterDefined(name string, params string) e } val := parseValue(strings.Replace(params, "\\\"", "\"", -1), tg.graphName) if parameterize { - tg.parameters[name] = gremlingo.NewGValue(name, val) + tg.parameters[name] = gremlingo.GValue{Name: name, Value: val} } else { tg.parameters[name] = val } diff --git a/gremlin-go/driver/gValue.go b/gremlin-go/driver/gValue.go index 90992cb780..3fece49b1f 100644 --- a/gremlin-go/driver/gValue.go +++ b/gremlin-go/driver/gValue.go @@ -23,38 +23,21 @@ import ( "fmt" ) -// GValue is a variable or literal value that is used in a Traversal. It is composed of a key-value pair where the key -// is the name given to the variable and the value is the object that the variable resolved to. +// GValue is a variable or literal value that is used in a Traversal. It is composed of a key-value +// pair where Name is the name given to the variable and Value is the object that the variable +// resolves to. Construct one directly with a struct literal, e.g. GValue{Name: "x", Value: 1}. +// A GValue's Value must not itself be a GValue (GValues cannot be nested). type GValue struct { - name string - value interface{} -} - -// NewGValue creates a new GValue to be used in traversals. GValues cannot be nested, which is -// the only restriction imposed on the name. -func NewGValue(name string, value interface{}) GValue { - if _, ok := value.(GValue); ok { - panic("GValues cannot be nested") - } - return GValue{name, value} -} - -// Name returns the name of the GValue. -func (gv GValue) Name() string { - return gv.name + Name string + Value interface{} } // IsNil determines if the value held is of a nil value. func (gv GValue) IsNil() bool { - return gv.value == nil -} - -// Value returns the value held by the GValue. -func (gv GValue) Value() interface{} { - return gv.value + return gv.Value == nil } // String returns the string representation of the GValue in the format "name=value". func (gv GValue) String() string { - return fmt.Sprintf("%v=%v", gv.name, gv.value) + return fmt.Sprintf("%v=%v", gv.Name, gv.Value) } diff --git a/gremlin-go/driver/gValue_test.go b/gremlin-go/driver/gValue_test.go index 90c74d4497..ffd766e46f 100644 --- a/gremlin-go/driver/gValue_test.go +++ b/gremlin-go/driver/gValue_test.go @@ -27,16 +27,16 @@ import ( func TestGValue(t *testing.T) { t.Run("test simple gValue", func(t *testing.T) { - gVal := NewGValue("intVal", 2) - assert.Equal(t, "intVal", gVal.Name()) - assert.Equal(t, 2, gVal.Value()) + gVal := GValue{Name: "intVal", Value: 2} + assert.Equal(t, "intVal", gVal.Name) + assert.Equal(t, 2, gVal.Value) assert.False(t, gVal.IsNil()) }) t.Run("test gValue allow parameter reuse with arrays", func(t *testing.T) { g := NewGraphTraversalSource(nil, nil) val := [3]int{1, 2, 3} - param := NewGValue("ids", val) + param := GValue{Name: "ids", Value: val} gl := g.Inject(param).V(param).GremlinLang assert.Equal(t, "g.inject(ids).V(ids)", gl.GetGremlin()) assert.Equal(t, val, gl.parameters["ids"]) @@ -45,7 +45,7 @@ func TestGValue(t *testing.T) { t.Run("test gValue allow parameter reuse with slices", func(t *testing.T) { g := NewGraphTraversalSource(nil, nil) val := []int{1, 2, 3} - param := NewGValue("ids", val) + param := GValue{Name: "ids", Value: val} gl := g.Inject(param).V(param).GremlinLang assert.Equal(t, "g.inject(ids).V(ids)", gl.GetGremlin()) assert.Equal(t, val, gl.parameters["ids"]) @@ -54,7 +54,7 @@ func TestGValue(t *testing.T) { t.Run("test gValue allow parameter reuse with maps", func(t *testing.T) { g := NewGraphTraversalSource(nil, nil) val := map[string]int{"foo": 1, "bar": 2} - param := NewGValue("ids", val) + param := GValue{Name: "ids", Value: val} gl := g.Inject(param).V(param).GremlinLang assert.Equal(t, "g.inject(ids).V(ids)", gl.GetGremlin()) assert.Equal(t, val, gl.parameters["ids"]) @@ -62,75 +62,38 @@ func TestGValue(t *testing.T) { t.Run("test gValue name not duplicated", func(t *testing.T) { g := NewGraphTraversalSource(nil, nil) - param1 := NewGValue("ids", [2]int{1, 2}) - param2 := NewGValue("ids", [2]int{2, 3}) + param1 := GValue{Name: "ids", Value: [2]int{1, 2}} + param2 := GValue{Name: "ids", Value: [2]int{2, 3}} assert.Panics(t, func() { g.Inject(param1).V(param2) }, "parameter with name ids already exists.") }) - t.Run("test name starting with _ accepted", func(t *testing.T) { - assert.NotPanics(t, func() { NewGValue("_ids", [2]int{1, 2}) }) - }) - - t.Run("test name starting with digit accepted", func(t *testing.T) { - assert.NotPanics(t, func() { NewGValue("1a", [2]int{1, 2}) }) - }) - - t.Run("test numeric name accepted", func(t *testing.T) { - assert.NotPanics(t, func() { NewGValue("1", [2]int{1, 2}) }) - }) - - t.Run("test mid-string underscore name accepted", func(t *testing.T) { - assert.NotPanics(t, func() { NewGValue("a_b", 1) }) - }) - - t.Run("test empty-string name accepted", func(t *testing.T) { - assert.NotPanics(t, func() { NewGValue("", 1) }) - }) - - t.Run("test mid-string dollar sign accepted", func(t *testing.T) { - assert.NotPanics(t, func() { NewGValue("a$b", 1) }) - }) - - t.Run("test unicode letter name accepted", func(t *testing.T) { - assert.NotPanics(t, func() { NewGValue("café", 1) }) - }) - t.Run("test IsNil returns true for nil value", func(t *testing.T) { - gv := NewGValue("x", nil) + gv := GValue{Name: "x", Value: nil} assert.True(t, gv.IsNil()) }) t.Run("test String representation", func(t *testing.T) { - gv := NewGValue("x", 1) + gv := GValue{Name: "x", Value: 1} assert.Equal(t, "x=1", gv.String()) }) - t.Run("test Go keyword name accepted", func(t *testing.T) { - assert.NotPanics(t, func() { NewGValue("for", 1) }) - }) - - t.Run("test nested GValue rejected", func(t *testing.T) { - assert.Panics(t, func() { NewGValue("x", NewGValue("y", 1)) }, - "GValues cannot be nested") - }) - t.Run("test distinct but equal slices allowed under same name", func(t *testing.T) { g := NewGraphTraversalSource(nil, nil) - param1 := NewGValue("ids", []int{1, 2, 3}) - param2 := NewGValue("ids", []int{1, 2, 3}) + param1 := GValue{Name: "ids", Value: []int{1, 2, 3}} + param2 := GValue{Name: "ids", Value: []int{1, 2, 3}} assert.NotPanics(t, func() { g.Inject(param1).V(param2) }) }) t.Run("test gValue nested in child traversal merges bindings", func(t *testing.T) { g := NewGraphTraversalSource(nil, nil) - gl := g.V().Where(T__.Is(NewGValue("xx1", 1))).GremlinLang + gl := g.V().Where(T__.Is(GValue{Name: "xx1", Value: 1})).GremlinLang assert.Equal(t, "g.V().where(__.is(xx1))", gl.GetGremlin()) assert.Equal(t, 1, gl.parameters["xx1"]) }) t.Run("test gValue nested across multiple child traversals merges bindings", func(t *testing.T) { g := NewGraphTraversalSource(nil, nil) - gl := g.V().Union(T__.V(NewGValue("vid1", 1)), T__.V(NewGValue("vid4", 4))).GremlinLang + gl := g.V().Union(T__.V(GValue{Name: "vid1", Value: 1}), T__.V(GValue{Name: "vid4", Value: 4})).GremlinLang assert.Equal(t, "g.V().union(__.V(vid1),__.V(vid4))", gl.GetGremlin()) assert.Equal(t, 1, gl.parameters["vid1"]) assert.Equal(t, 4, gl.parameters["vid4"]) diff --git a/gremlin-go/driver/gremlinlang.go b/gremlin-go/driver/gremlinlang.go index 8eb46509ab..4dc88d987f 100644 --- a/gremlin-go/driver/gremlinlang.go +++ b/gremlin-go/driver/gremlinlang.go @@ -244,14 +244,14 @@ func (gl *GremlinLang) argAsString(arg interface{}) (string, error) { } return v.GetGremlin("__"), nil case GValue: - key := v.Name() - value := v.Value() + key := v.Name + value := v.Value if val, ok := gl.parameters[key]; ok { if !reflect.DeepEqual(val, value) { panic(fmt.Sprintf("parameter with name '%v' already exists.", key)) } } else { - gl.parameters[key] = v.Value() + gl.parameters[key] = v.Value } return key, nil case uuid.UUID:
