Skip to content

Commit

Permalink
Merge pull request #13 from iFaceless/chaining-accessing-for-meth-result
Browse files Browse the repository at this point in the history
✨ feat: chanining accessing for method result
  • Loading branch information
iFaceless authored May 18, 2020
2 parents 17d07c9 + 54f3773 commit 7b288ce
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 20 deletions.
28 changes: 28 additions & 0 deletions USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,23 @@ func (u *UserModel) Fullname3(ctx context.Context) (string, error) {
return fmt.Sprintf("user:%d", u.ID)
}

type BadgeModel {
Name string
}

func (u *UserModel) Badge(ctx context.Context) (*BadgeModel, error) {
return &BadgeModel{
Name: "Cool"
}, nil
}

// Schema definition
type UserSchema struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty" portal:"attr:Fullname"`
// Chaining accessing is also supported.
// portal calls method `UserModel.Badge()`, then accesses Badge.Name field.
BadgeName string `json:"badge_name,omitempty" portal:"attr:Badge.Name"`
}
```

Expand All @@ -65,6 +78,9 @@ type UserSchema struct {
type TaskSchema struct {
Title string `json:"title,omitempty" portal:"meth:GetTitle"`
Description string `json:"description,omitempty" portal:"meth:GetDescription"`
// Chaining accessing is also supported for method result.
ScheduleAt *field.Timestamp `json:"schedule_at,omitempty" portal:"meth:FetchSchedule.At"`
ScheduleDescription *string `json:"schedule_description,omitempty" portal:"meth:FetchSchedule.Description"`
}

func (ts *TaskSchema) GetTitle(ctx context.Context, model *model.TaskModel) string {
Expand All @@ -78,6 +94,18 @@ func (ts *TaskSchema) GetDescription(model *model.TaskModel) (string, error) {
// If method returns an error, portal will ignore the result.
return "Custom description", nil
}

type Schedule struct {
At time.Time
Description string
}

func (ts *TaskSchema) FetchSchedule(model *model.TaskModel) *Schedule {
return &Schedule{
Description: "High priority",
At: time.Now(),
}
}
```

### Load Data Asynchronously: `async`
Expand Down
40 changes: 27 additions & 13 deletions _examples/todo/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,36 @@ type UserSchema struct {
}

type TaskSchema struct {
ID string `json:"id,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty" portal:"meth:GetDescription;async"`
Description1 string `json:"description1,omitempty" portal:"meth:GetDescription;async"`
Description2 string `json:"description2,omitempty" portal:"meth:GetDescription;async"`
Description3 string `json:"description3,omitempty" portal:"meth:GetDescription;async"`
User *UserSchema `json:"user,omitempty" portal:"nested"`
SimpleUser *UserSchema `json:"simple_user,omitempty" portal:"nested;only:Name;attr:User"`
Unknown string `json:"unknown"`
UsersWithDefault []*UserSchema `json:"users_with_default" portal:"nested;default:AUTO_INIT"`
UserWithDefault *UserSchema `json:"user_with_default" portal:"nested;default:AUTO_INIT"`
LowerString *field.LowerString `json:"lower_string,omitempty" portal:"meth:GetDescription"`
UpperString *field.UpperString `json:"upper_string,omitempty" portal:"meth:GetDescription"`
ID string `json:"id,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty" portal:"meth:GetDescription;async"`
Description1 string `json:"description1,omitempty" portal:"meth:GetDescription;async"`
Description2 string `json:"description2,omitempty" portal:"meth:GetDescription;async"`
Description3 string `json:"description3,omitempty" portal:"meth:GetDescription;async"`
User *UserSchema `json:"user,omitempty" portal:"nested"`
SimpleUser *UserSchema `json:"simple_user,omitempty" portal:"nested;only:Name;attr:User"`
Unknown string `json:"unknown"`
UsersWithDefault []*UserSchema `json:"users_with_default" portal:"nested;default:AUTO_INIT"`
UserWithDefault *UserSchema `json:"user_with_default" portal:"nested;default:AUTO_INIT"`
LowerString *field.LowerString `json:"lower_string,omitempty" portal:"meth:GetDescription"`
UpperString *field.UpperString `json:"upper_string,omitempty" portal:"meth:GetDescription"`
ScheduleAt *field.Timestamp `json:"schedule_at,omitempty" portal:"meth:FetchSchedule.At"`
ScheduleDescription *string `json:"schedule_description,omitempty" portal:"meth:FetchSchedule.Description"`
}

func (ts *TaskSchema) GetDescription(model *model.TaskModel) (string, error) {
time.Sleep(100 * time.Millisecond)
return "Custom description", nil
}

type Schedule struct {
At time.Time
Description string
}

func (ts *TaskSchema) FetchSchedule(model *model.TaskModel) *Schedule {
return &Schedule{
Description: "High priority",
At: time.Now(),
}
}
6 changes: 3 additions & 3 deletions chell.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,14 @@ func (c *Chell) dumpField(ctx context.Context, field *schemaField, value interfa
}

if !field.isNested() {
logger.Debugf("[portal.chell] dump normal field %s with value %v", field, value)
logger.Debugf("[portal.chell] dump normal field %s with value '%v'", field, value)
return field.setValue(value)
} else {
if field.hasMany() {
logger.Debugf("[portal.chell] dump nested slice field %s with value %v", field, value)
logger.Debugf("[portal.chell] dump nested slice field %s with value '%v'", field, value)
return c.dumpFieldNestedMany(ctx, field, value)
} else {
logger.Debugf("[portal.chell] dump nested field %s with value %v", field, value)
logger.Debugf("[portal.chell] dump nested field %s with value '%v'", field, value)
return c.dumpFieldNestedOne(ctx, field, value)
}
}
Expand Down
17 changes: 15 additions & 2 deletions field.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,21 @@ func (f *schemaField) hasMany() bool {
return f.Kind() == reflect.Slice
}

func (f *schemaField) method() string {
return f.settings["METH"]
func (f *schemaField) method() (meth string, attrs []string) {
result, ok := f.settings["METH"]
if !ok {
return "", nil
}

for _, r := range strings.Split(result, ".") {
attrs = append(attrs, strings.TrimSpace(r))
}

if len(attrs) > 0 {
meth = attrs[0]
attrs = attrs[1:]
}
return
}

func (f *schemaField) hasMethod() bool {
Expand Down
10 changes: 9 additions & 1 deletion field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,19 @@ func TestField_Method(t *testing.T) {
type FooSchema struct {
Name string
Bar string `portal:"meth:GetBar"`
Foo string `portal:"meth:GetFoo.Name"`
}

schema := newSchema(&FooSchema{})
f := newField(schema, schema.innerStruct().Field("Bar"))
assert.Equal(t, "GetBar", f.method())
m, attrs := f.method()
assert.Equal(t, "GetBar", m)
assert.Equal(t, 0, len(attrs))

f = newField(schema, schema.innerStruct().Field("Foo"))
m, attrs = f.method()
assert.Equal(t, "GetFoo", m)
assert.Equal(t, []string{"Name"}, attrs)
}

func TestField_HasMethod(t *testing.T) {
Expand Down
10 changes: 9 additions & 1 deletion schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,18 @@ func (s *schema) fieldValueFromSrc(ctx context.Context, field *schemaField, v in
if field.hasConstValue() {
val = field.constValue()
} else if field.hasMethod() {
ret, err := invokeMethodOfAnyType(ctx, s.rawValue, field.method(), v)
m, attrs := field.method()
if m == "" {
return nil, fmt.Errorf("empty method name")
}

ret, err := invokeMethodOfAnyType(ctx, s.rawValue, m, v)
if err != nil {
return nil, fmt.Errorf("failed to get value: %s", err)
}
if len(attrs) > 0 {
return nestedValue(ctx, ret, attrs)
}
return ret, nil
} else if field.hasChainingAttrs() {
return nestedValue(ctx, v, field.chainingAttrs())
Expand Down

0 comments on commit 7b288ce

Please sign in to comment.