Skip to content

Commit 9c90a4f

Browse files
committed
Rename references to "join" or "union" to "choice type"
1 parent 61e814c commit 9c90a4f

File tree

3 files changed

+49
-43
lines changed

3 files changed

+49
-43
lines changed

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ to-many from being serialized.
187187
```
188188

189189
Polymorphic relations can be represented exactly as relations, except that
190-
an intermediate type is needed within your model struct that will be populated
191-
with exactly one value among all the fields in that struct.
190+
an intermediate type is needed within your model struct that provides a choice
191+
for the actual value to be populated within.
192192

193193
Example:
194194

@@ -220,14 +220,18 @@ type Post struct {
220220
```
221221

222222
During decoding, the `polyrelation` annotation instructs jsonapi to assign each relationship
223-
to either `Video` or `Image` within the value of the associated field. This value must be
224-
a pointer to a struct containing other pointer fields to jsonapi models. The actual field
225-
assignment depends on that type having a jsonapi "primary" annotation with a type matching
226-
the relationship type found in the response. All other fields will be remain nil.
223+
to either `Video` or `Image` within the value of the associated field, provided that the
224+
payload contains either a "videos" or "images" type. This field value must be
225+
a pointer to a special choice type struct (also known as a tagged union, or sum type) containing
226+
other pointer fields to jsonapi models. The actual field assignment depends on that type having
227+
a jsonapi "primary" annotation with a type matching the relationship type found in the response.
228+
All other fields will be remain empty. If no matching types are represented by the choice type,
229+
all fields will be empty.
227230

228231
During encoding, the very first non-nil field will be used to populate the payload. Others
229-
will be ignored. Therefore, it's critical to set the value of only one field within the join
230-
struct.
232+
will be ignored. Therefore, it's critical to set the value of only one field within the choice
233+
struct. When accepting input values on this type of choice type, it would a good idea to enforce
234+
and check that the value is set on only one field.
231235

232236
#### `links`
233237

request.go

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -177,25 +177,25 @@ type structFieldIndex struct {
177177
FieldNum int
178178
}
179179

180-
// joinStructMapping reflects on a value that may be a slice
181-
// of join structs or a join struct. A join struct is a struct
182-
// comprising of pointers to other jsonapi models, only one of
183-
// which is populated with a value by the decoder. The join struct is
184-
// probed and a data structure is generated that maps the
185-
// underlying model type (its 'primary' type) to the field number
186-
// within the join struct.
180+
// choiceStructMapping reflects on a value that may be a slice
181+
// of choice type structs or a choice type struct. A choice type
182+
// struct is a struct comprising of pointers to other jsonapi models,
183+
// only one of which is populated with a value by the decoder.
187184
//
188-
// This data can then be used to correctly assign each data relationship
189-
// to the correct join struct field.
190-
func joinStructMapping(join reflect.Type) (result map[string]structFieldIndex, err error) {
185+
// The specified type is probed and a map is generated that maps the
186+
// underlying model type (its 'primary' type) to the field number
187+
// within the choice type struct. This data can then be used to correctly
188+
// assign each data relationship node to the correct choice type
189+
// struct field.
190+
func choiceStructMapping(choice reflect.Type) (result map[string]structFieldIndex, err error) {
191191
result = make(map[string]structFieldIndex)
192192

193-
for join.Kind() != reflect.Struct {
194-
join = join.Elem()
193+
for choice.Kind() != reflect.Struct {
194+
choice = choice.Elem()
195195
}
196196

197-
for i := 0; i < join.NumField(); i++ {
198-
fieldType := join.Field(i)
197+
for i := 0; i < choice.NumField(); i++ {
198+
fieldType := choice.Field(i)
199199

200200
if fieldType.Type.Kind() != reflect.Ptr {
201201
continue
@@ -234,22 +234,22 @@ func getStructTags(field reflect.StructField) ([]string, error) {
234234
return args, nil
235235
}
236236

237-
// unmarshalNodeMaybeJoin populates a model that may or may not be
238-
// a join struct that corresponds to a polyrelation or relation
239-
func unmarshalNodeMaybeJoin(m *reflect.Value, data *Node, annotation string, joinMapping map[string]structFieldIndex, included *map[string]*Node) error {
240-
// This will hold either the value of the join model or the actual
237+
// unmarshalNodeMaybeChoice populates a model that may or may not be
238+
// a choice type struct that corresponds to a polyrelation or relation
239+
func unmarshalNodeMaybeChoice(m *reflect.Value, data *Node, annotation string, choiceTypeMapping map[string]structFieldIndex, included *map[string]*Node) error {
240+
// This will hold either the value of the choice type model or the actual
241241
// model, depending on annotation
242242
var actualModel = *m
243-
var joinElem *structFieldIndex = nil
243+
var choiceElem *structFieldIndex = nil
244244

245245
if annotation == annotationPolyRelation {
246-
j, ok := joinMapping[data.Type]
246+
c, ok := choiceTypeMapping[data.Type]
247247
if !ok {
248248
// There is no valid join field to assign this type of relation.
249249
return ErrBadJSONAPIJoinStruct
250250
}
251-
joinElem = &j
252-
actualModel = reflect.New(joinElem.Type)
251+
choiceElem = &c
252+
actualModel = reflect.New(choiceElem.Type)
253253
}
254254

255255
if err := unmarshalNode(
@@ -260,11 +260,12 @@ func unmarshalNodeMaybeJoin(m *reflect.Value, data *Node, annotation string, joi
260260
return err
261261
}
262262

263-
if joinElem != nil {
263+
if choiceElem != nil {
264264
// actualModel is a pointer to the model type
265-
// m is a pointer to a struct that should hold the actualModel at joinElem.FieldNum
265+
// m is a pointer to a struct that should hold the actualModel
266+
// at choiceElem.FieldNum
266267
v := m.Elem()
267-
v.Field(joinElem.FieldNum).Set(actualModel)
268+
v.Field(choiceElem.FieldNum).Set(actualModel)
268269
}
269270
return nil
270271
}
@@ -384,10 +385,11 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
384385
}
385386

386387
// If this is a polymorphic relation, each data relationship needs to be assigned
387-
// to it's appropriate join field and fieldValue should be a join field.
388-
var joinMapping map[string]structFieldIndex = nil
388+
// to it's appropriate choice field and fieldValue should be a choice
389+
// struct type field.
390+
var choiceMapping map[string]structFieldIndex = nil
389391
if annotation == annotationPolyRelation {
390-
joinMapping, err = joinStructMapping(fieldValue.Type())
392+
choiceMapping, err = choiceStructMapping(fieldValue.Type())
391393
if err != nil {
392394
er = err
393395
break
@@ -406,16 +408,16 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
406408

407409
data := relationship.Data
408410

409-
// This will hold either the value of the slice of join models or
411+
// This will hold either the value of the slice of choice type models or
410412
// the slice of models, depending on the annotation
411413
models := reflect.New(sliceType).Elem()
412414

413415
for _, n := range data {
414-
// This will hold either the value of the join model or the actual
416+
// This will hold either the value of the choice type model or the actual
415417
// model, depending on annotation
416418
m := reflect.New(sliceType.Elem().Elem())
417419

418-
err = unmarshalNodeMaybeJoin(&m, n, annotation, joinMapping, included)
420+
err = unmarshalNodeMaybeChoice(&m, n, annotation, choiceMapping, included)
419421
if err != nil {
420422
er = err
421423
break
@@ -446,11 +448,11 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
446448
continue
447449
}
448450

449-
// This will hold either the value of the join model or the actual
451+
// This will hold either the value of the choice type model or the actual
450452
// model, depending on annotation
451453
m := reflect.New(fieldValue.Type().Elem())
452454

453-
err = unmarshalNodeMaybeJoin(&m, relationship.Data, annotation, joinMapping, included)
455+
err = unmarshalNodeMaybeChoice(&m, relationship.Data, annotation, choiceMapping, included)
454456
if err != nil {
455457
er = err
456458
break

request_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ func Test_UnmarshalPayload_polymorphicRelations(t *testing.T) {
697697
}
698698
}
699699

700-
func Test_joinStructMapping(t *testing.T) {
700+
func Test_choiceStructMapping(t *testing.T) {
701701
cases := []struct {
702702
val reflect.Type
703703
}{
@@ -706,7 +706,7 @@ func Test_joinStructMapping(t *testing.T) {
706706
}
707707

708708
for _, c := range cases {
709-
result, err := joinStructMapping(c.val)
709+
result, err := choiceStructMapping(c.val)
710710
if err != nil {
711711
t.Fatal(err)
712712
}

0 commit comments

Comments
 (0)