diff --git a/widgets/form/bind.go b/widgets/form/bind.go index 82a02a48..b4a65f1c 100644 --- a/widgets/form/bind.go +++ b/widgets/form/bind.go @@ -55,7 +55,7 @@ func (dsl *DSL) bindForm() error { // Load form if _, has := Forms[id]; !has { - if err := LoadID(id, dsl.Root); err != nil { + if err := LoadID(id); err != nil { return err } } diff --git a/widgets/form/form.go b/widgets/form/form.go index 7fbf6e9b..5a4e46d3 100644 --- a/widgets/form/form.go +++ b/widgets/form/form.go @@ -55,9 +55,11 @@ var Forms map[string]*DSL = map[string]*DSL{} var lock sync.Mutex // New create a new DSL -func New(id string) *DSL { +func New(id string, file string, source []byte) *DSL { return &DSL{ ID: id, + file: file, + source: source, Fields: &FieldsDSL{Form: field.Columns{}}, Layout: &LayoutDSL{}, CProps: field.CloudProps{}, @@ -96,6 +98,11 @@ func Load(cfg config.Config) error { return err } +// Unload unload the form +func Unload(id string) { + delete(Forms, id) +} + // LoadFileSync load form dsl by file func LoadFileSync(root string, file string) error { lock.Lock() @@ -112,23 +119,15 @@ func LoadFile(root string, file string) error { return err } - dsl := New(id) - err = application.Parse(file, data, dsl) - if err != nil { - return fmt.Errorf("[%s] %s", id, err.Error()) - } - - err = dsl.parse(id, root) + _, err = load(data, id, file) if err != nil { return err } - - Forms[id] = dsl return nil } // LoadID load via id -func LoadID(id string, root string) error { +func LoadID(id string) error { file := filepath.Join("forms", share.File(id, ".form.yao")) if exists, _ := application.App.Exists(file); exists { @@ -148,10 +147,39 @@ func LoadID(id string, root string) error { return fmt.Errorf("form %s not found", id) } -// LoadData load via data -func (dsl *DSL) parse(id string, root string) error { +// LoadSourceSync load form dsl by source +func LoadSourceSync(source []byte, id string) (*DSL, error) { + lock.Lock() + defer lock.Unlock() + return LoadSource(source, id) +} + +// LoadSource load form dsl by source +func LoadSource(source []byte, id string) (*DSL, error) { + file := filepath.Join("forms", share.File(id, ".form.yao")) + return load(source, id, file) +} + +// LoadSource load form dsl by source +func load(source []byte, id string, file string) (*DSL, error) { + dsl := New(id, file, source) + err := application.Parse(file, source, dsl) + if err != nil { + return nil, fmt.Errorf("[%s] %s", id, err.Error()) + } + + err = dsl.parse(id) + if err != nil { + return nil, err + } + + Forms[id] = dsl + return dsl, nil +} + +// LoadData load via data +func (dsl *DSL) parse(id string) error { - dsl.Root = root if dsl.Action == nil { dsl.Action = &ActionDSL{} } @@ -312,3 +340,19 @@ func (dsl *DSL) Actions() []component.ActionsExport { } return res } + +// Reload reload the form +func (dsl *DSL) Reload() (*DSL, error) { + return LoadSourceSync(dsl.source, dsl.ID) +} + +// Read read the source +func (dsl *DSL) Read() []byte { + return dsl.source +} + +// Exists check the form exists +func Exists(id string) bool { + _, has := Forms[id] + return has +} diff --git a/widgets/form/form_test.go b/widgets/form/form_test.go index 8c90ba53..445b9b6f 100644 --- a/widgets/form/form_test.go +++ b/widgets/form/form_test.go @@ -1,7 +1,6 @@ package form import ( - "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -32,12 +31,66 @@ func TestLoadID(t *testing.T) { defer test.Clean() prepare(t) - err := LoadID("pet", filepath.Join(config.Conf.Root)) + err := LoadID("pet") if err != nil { t.Fatal(err) } } +func TestLoadSourceSync(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + source := []byte(`{ + "name": "Pet Admin Form Bind Model", + "action": { + "bind": { "model": "pet" } + } + } + `) + + form, err := LoadSourceSync(source, `dynamic.pet`) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "Pet Admin Form Bind Model", form.Name) + assert.Equal(t, "pet", form.Action.Bind.Model) + assert.True(t, Exists("dynamic.pet")) + + // Reload + form, err = form.Reload() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "Pet Admin Form Bind Model", form.Name) + assert.Equal(t, "pet", form.Action.Bind.Model) + assert.True(t, Exists("dynamic.pet")) + + // Unload + Unload("dynamic.pet") + assert.False(t, Exists("dynamic.pet")) +} + +func TestRead(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + form := MustGet("pet") + assert.NotNil(t, form) + + // Read + source := form.Read() + if source == nil { + t.Fatal("Read Error") + } + + form, err := LoadSourceSync(source, `dynamic.pet`) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "::Pet Admin", form.Name) +} + func prepare(t *testing.T) { // load flows diff --git a/widgets/form/process.go b/widgets/form/process.go index 45d9dd11..8c094b6c 100644 --- a/widgets/form/process.go +++ b/widgets/form/process.go @@ -6,6 +6,7 @@ import ( "os" "strings" + "github.com/yaoapp/gou/application" "github.com/yaoapp/gou/fs" gouProcess "github.com/yaoapp/gou/process" "github.com/yaoapp/gou/types" @@ -28,6 +29,10 @@ func exportProcess() { gouProcess.Register("yao.form.update", processUpdate) gouProcess.Register("yao.form.delete", processDelete) gouProcess.Register("yao.form.load", processLoad) + gouProcess.Register("yao.form.reload", processReload) + gouProcess.Register("yao.form.unload", processUnload) + gouProcess.Register("yao.form.read", processRead) + gouProcess.Register("yao.form.exists", processExists) } func processXgen(process *gouProcess.Process) interface{} { @@ -182,9 +187,22 @@ func processDelete(process *gouProcess.Process) interface{} { return form.Action.Delete.MustExec(process) } -// processLoad yao.table.Load (:file) +// processLoad yao.form.Load form_name file func processLoad(process *gouProcess.Process) interface{} { process.ValidateArgNums(1) + + // Load from source + if process.NumOfArgs() >= 3 { + id := process.ArgsString(0) + source := process.ArgsString(2) + _, err := LoadSourceSync([]byte(source), id) + if err != nil { + exception.New(err.Error(), 500).Throw() + } + return nil + } + + // Load from file file := process.ArgsString(0) if file == "" { exception.New("file is required", 400).Throw() @@ -193,3 +211,39 @@ func processLoad(process *gouProcess.Process) interface{} { file = strings.TrimPrefix(file, string(os.PathSeparator)) return LoadFileSync("forms", file) } + +// processReload yao.form.Reload form_name +func processReload(process *gouProcess.Process) interface{} { + process.ValidateArgNums(1) + tab := MustGet(process) // 0 + _, err := tab.Reload() + if err != nil { + exception.New(err.Error(), 500).Throw() + } + return nil +} + +// processUnload yao.form.Unload form_name +func processUnload(process *gouProcess.Process) interface{} { + process.ValidateArgNums(1) + Unload(process.ArgsString(0)) + return nil +} + +// processRead yao.form.Read form_name +func processRead(process *gouProcess.Process) interface{} { + process.ValidateArgNums(1) + tab := MustGet(process) // 0 + source := map[string]interface{}{} + err := application.Parse(tab.file, tab.Read(), &source) + if err != nil { + exception.New(err.Error(), 500).Throw() + } + return source +} + +// processExists yao.form.Exists form_name +func processExists(process *gouProcess.Process) interface{} { + process.ValidateArgNums(1) + return Exists(process.ArgsString(0)) +} diff --git a/widgets/form/process_test.go b/widgets/form/process_test.go index 7e7e3fe9..e6d240a7 100644 --- a/widgets/form/process_test.go +++ b/widgets/form/process_test.go @@ -342,6 +342,59 @@ func TestProcessXgenWithPermissions(t *testing.T) { } +func TestProcessLoad(t *testing.T) { + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + + source := `{ + "name": "Pet Admin Form Bind Model", + "action": { + "bind": { "model": "pet" } + } + } + ` + args := []interface{}{"dynamic.pet", "/forms/dynamic/pet.form.yao", source} + + // Load + assert.NotPanics(t, func() { + process.New("yao.form.Load", args...).Run() + }) + form := MustGet("dynamic.pet") + assert.Equal(t, "Pet Admin Form Bind Model", form.Name) + assert.Equal(t, "pet", form.Action.Bind.Model) + + // Exist + res := process.New("yao.form.Exists", "dynamic.pet").Run() + assert.True(t, res.(bool)) + + // Reload + assert.NotPanics(t, func() { + process.New("yao.form.Reload", "dynamic.pet").Run() + }) + form = MustGet("dynamic.pet") + assert.Equal(t, "Pet Admin Form Bind Model", form.Name) + assert.Equal(t, "pet", form.Action.Bind.Model) + + // Unload + assert.NotPanics(t, func() { + process.New("yao.form.Unload", "dynamic.pet").Run() + }) + res = process.New("yao.form.Exists", "dynamic.pet").Run() + assert.False(t, res.(bool)) +} + +func TestProcessRead(t *testing.T) { + + test.Prepare(t, config.Conf) + defer test.Clean() + prepare(t) + + res := process.New("yao.form.Read", "pet").Run() + assert.NotNil(t, res) + assert.Equal(t, "::Pet Admin", res.(map[string]interface{})["name"]) +} + func testData(t *testing.T) { pet := model.Select("pet") err := pet.Insert( diff --git a/widgets/form/types.go b/widgets/form/types.go index c9d797dc..dd597db3 100644 --- a/widgets/form/types.go +++ b/widgets/form/types.go @@ -12,13 +12,14 @@ import ( // DSL the form DSL type DSL struct { ID string `json:"id,omitempty"` - Root string `json:"-"` Name string `json:"name,omitempty"` Action *ActionDSL `json:"action"` Layout *LayoutDSL `json:"layout"` Fields *FieldsDSL `json:"fields"` Config map[string]interface{} `json:"config,omitempty"` CProps field.CloudProps `json:"-"` + file string `json:"-"` + source []byte `json:"-"` compute.Computable *mapping.Mapping } diff --git a/widgets/table/fields_test.go b/widgets/table/fields_test.go index a7fcfd74..770cfcb6 100644 --- a/widgets/table/fields_test.go +++ b/widgets/table/fields_test.go @@ -18,7 +18,7 @@ func TestFiledsBindModel(t *testing.T) { testData(t) m := model.Select("pet") - tab := New("unit-test") + tab := New("unit-test", "unit-test.tab.yao", nil) err := tab.Fields.BindModel(m) if err != nil { t.Fatal(err) diff --git a/widgets/table/process.go b/widgets/table/process.go index ca5eca73..2b899fe3 100644 --- a/widgets/table/process.go +++ b/widgets/table/process.go @@ -347,6 +347,7 @@ func processReload(process *gouProcess.Process) interface{} { return nil } +// processUnload yao.table.Unload table_name func processUnload(process *gouProcess.Process) interface{} { process.ValidateArgNums(1) Unload(process.ArgsString(0)) diff --git a/widgets/table/table.go b/widgets/table/table.go index b74f22a6..a2250f68 100644 --- a/widgets/table/table.go +++ b/widgets/table/table.go @@ -85,9 +85,11 @@ var Tables map[string]*DSL = map[string]*DSL{} var lock sync.Mutex // New create a new DSL -func New(id string) *DSL { +func New(id string, file string, source []byte) *DSL { return &DSL{ ID: id, + file: file, + source: source, Fields: &FieldsDSL{Filter: field.Filters{}, Table: field.Columns{}}, CProps: field.CloudProps{}, Config: map[string]interface{}{}, @@ -189,7 +191,7 @@ func LoadSource(source []byte, id string) (*DSL, error) { // LoadSource load table dsl by source func load(source []byte, id string, file string) (*DSL, error) { - dsl := &DSL{ID: id, source: source, file: file} + dsl := New(id, file, source) err := application.Parse(file, source, dsl) if err != nil { return nil, fmt.Errorf("[%s] %s", id, err.Error())