Skip to content

Commit

Permalink
feat: support embedded struct fields (#691)
Browse files Browse the repository at this point in the history
  • Loading branch information
zepatrik committed Feb 22, 2022
1 parent f7abee9 commit 32f6994
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 19 deletions.
10 changes: 10 additions & 0 deletions associations/associations_for_struct.go
Expand Up @@ -72,6 +72,16 @@ func ForStruct(s interface{}, fields ...string) (Associations, error) {
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)

// inline embedded field
if f.Anonymous {
innerAssociations, err := ForStruct(v.Field(i).Interface(), fields...)
if err != nil {
return nil, err
}
associations = append(associations, innerAssociations...)
continue
}

// ignores those fields not included in fields list.
if len(fields) > 0 && fieldIgnoredIn(fields, f.Name) {
continue
Expand Down
50 changes: 32 additions & 18 deletions columns/columns_for_struct.go
Expand Up @@ -31,33 +31,47 @@ func ForStructWithAlias(s interface{}, tableName, tableAlias, idField string) (c
}
}

fieldCount := st.NumField()
// recursive functions to also find and add embedded struct fields
var findColumns func(st reflect.Type)
findColumns = func(t reflect.Type) {
if t.Kind() == reflect.Ptr {
t = t.Elem()
}

for i := 0; i < fieldCount; i++ {
field := st.Field(i)
fc := t.NumField()
for i := 0; i < fc; i++ {
field := t.Field(i)

popTags := TagsFor(field)
tag := popTags.Find("db")
if field.Anonymous {
findColumns(field.Type)
continue
}

if !tag.Ignored() && !tag.Empty() {
col := tag.Value
popTags := TagsFor(field)
tag := popTags.Find("db")

// add writable or readable.
tag := popTags.Find("rw")
if !tag.Empty() {
col = col + "," + tag.Value
}
if !tag.Ignored() && !tag.Empty() {
col := tag.Value

cs := columns.Add(col)
// add writable or readable.
tag := popTags.Find("rw")
if !tag.Empty() {
col = col + "," + tag.Value
}

// add select clause.
tag = popTags.Find("select")
if !tag.Empty() {
c := cs[0]
c.SetSelectSQL(tag.Value)
cs := columns.Add(col)

// add select clause.
tag = popTags.Find("select")
if !tag.Empty() {
c := cs[0]
c.SetSelectSQL(tag.Value)
}
}
}
}

findColumns(st)

return columns
}
27 changes: 27 additions & 0 deletions executors_test.go
Expand Up @@ -533,6 +533,33 @@ func Test_Create_Non_PK_ID(t *testing.T) {
})
}

func Test_Embedded_Struct(t *testing.T) {
if PDB == nil {
t.Skip("skipping integration tests")
}
transaction(func(tx *Connection) {
r := require.New(t)

entry := &EmbeddingStruct{
InnerStruct: InnerStruct{},
AdditionalField: "I am also important!",
}
r.NoError(tx.Create(entry))

var actual EmbeddingStruct
r.NoError(tx.Find(&actual, entry.ID))
r.Equal(entry.AdditionalField, actual.AdditionalField)

entry.AdditionalField = entry.AdditionalField + " updated"
r.NoError(tx.Update(entry))

r.NoError(tx.Find(&actual, entry.ID))
r.Equal(entry.AdditionalField, actual.AdditionalField)

r.NoError(tx.Destroy(entry))
})
}

func Test_Eager_Create_Has_Many(t *testing.T) {
if PDB == nil {
t.Skip("skipping integration tests")
Expand Down
11 changes: 11 additions & 0 deletions pop_test.go
Expand Up @@ -476,3 +476,14 @@ type NonStandardID struct {
ID int `db:"pk"`
OutfacingID string `db:"id"`
}

type InnerStruct struct {
ID int `db:"id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}

type EmbeddingStruct struct {
InnerStruct
AdditionalField string `db:"additional_field"`
}
2 changes: 1 addition & 1 deletion test.sh
Expand Up @@ -58,7 +58,7 @@ function test {
./tsoda create -e $SODA_DIALECT -c ./database.yml -p ./testdata/migrations
./tsoda migrate -e $SODA_DIALECT -c ./database.yml -p ./testdata/migrations
echo "Test..."
go test -race -tags sqlite $VERBOSE ./... -count=1
go test -race -tags sqlite $VERBOSE -count=1 ./...
}

function debug_test {
Expand Down
@@ -0,0 +1 @@
drop_table("embedding_structs")
7 changes: 7 additions & 0 deletions testdata/migrations/20220214113659_embedded_struct.up.fizz
@@ -0,0 +1,7 @@
create_table("embedding_structs") {
t.Column("id", "int", { "primary": true })

t.Column("additional_field", "string", {})

t.Timestamps()
}

0 comments on commit 32f6994

Please sign in to comment.