From c63a98031dc8bbeb2653c337cc9aaac9d243f0de Mon Sep 17 00:00:00 2001 From: Johan Lindh Date: Fri, 28 Jun 2024 11:26:21 +0200 Subject: [PATCH] wip --- jaws.js | 5 ++- jsany.go | 43 +++++++++++++++++++++++ jsany_test.go | 73 +++++++++++++++++++++++++++++++++++++++ jsfunction.go | 45 ++++++++++++++++++++++++ jsfunction_test.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++ jsstring_test.go | 3 -- jsvariable.go | 8 +++-- wsmsg.go | 2 +- 8 files changed, 257 insertions(+), 7 deletions(-) create mode 100644 jsany.go create mode 100644 jsany_test.go create mode 100644 jsfunction_test.go diff --git a/jaws.js b/jaws.js index 6e7ec9d..2a284c0 100644 --- a/jaws.js +++ b/jaws.js @@ -332,7 +332,10 @@ function jawsVar(name, data, operation) { data = obj[lastkey](data); break; case 'Set': - return (obj[lastkey] = data); + if (typeof obj[lastkey] !== 'function') { + obj[lastkey] = data; + } + return data; default: throw "jaws: unknown operation: " + operation; } diff --git a/jsany.go b/jsany.go new file mode 100644 index 0000000..cfa3505 --- /dev/null +++ b/jsany.go @@ -0,0 +1,43 @@ +package jaws + +import ( + "encoding/json" + "io" + + "github.com/linkdata/jaws/what" +) + +type JsAny struct { + JsVariable + AnySetter +} + +func (ui *JsAny) JawsRender(e *Element, w io.Writer, params []any) error { + return ui.render(ui.AnySetter, ui.JawsGetAny(e), e, w, params) +} + +func (ui *JsAny) JawsUpdate(e *Element) { + _ = e.JsSet(ui.JawsGetAny(e)) +} + +func (ui *JsAny) JawsEvent(e *Element, wht what.What, val string) (err error) { + err = ErrEventUnhandled + if wht == what.Set { + var v any + if err = json.Unmarshal([]byte(val), &v); err == nil { + _, err = e.maybeDirty(ui.Tag, ui.JawsSetAny(e, v)) + } + } + return +} + +func NewJsAny(g AnySetter, name string) *JsAny { + return &JsAny{ + JsVariable: JsVariable{JsName{Name: name}}, + AnySetter: g, + } +} + +func (rq RequestWriter) JsAny(value any, name string, params ...any) error { + return rq.UI(NewJsAny(makeAnySetter(value), name), params...) +} diff --git a/jsany_test.go b/jsany_test.go new file mode 100644 index 0000000..0a8e364 --- /dev/null +++ b/jsany_test.go @@ -0,0 +1,73 @@ +package jaws + +import ( + "strconv" + "testing" + + "github.com/linkdata/jaws/what" +) + +func TestJsAny_JawsRender(t *testing.T) { + th := newTestHelper(t) + nextJid = 0 + rq := newTestRequest() + defer rq.Close() + + var val any + ts := newTestSetter(val) + th.NoErr(rq.JsAny(ts, "varname")) + wantHtml := "" + if gotHtml := rq.BodyString(); gotHtml != wantHtml { + t.Errorf("Request.JsAny() = %q, want %q", gotHtml, wantHtml) + } + + ts.Set(1.3) + rq.Dirty(ts) + + select { + case <-th.C: + th.Timeout() + case msg := <-rq.outCh: + s := msg.Format() + if s != "Set\tJid.1\t1.3\n" { + t.Error(strconv.Quote(s)) + } + } +} + +func TestJsAny_JawsEvent(t *testing.T) { + th := newTestHelper(t) + nextJid = 0 + rq := newTestRequest() + defer rq.Close() + + msgCh := make(chan string, 1) + defer close(msgCh) + + val := float64(1.2) + ts := newTestSetter(any(val)) + ui := NewJsAny(ts, "varname") + th.NoErr(rq.UI(ui)) + + th.Equal(ui.JawsGetTag(rq.Request), ts) + + select { + case <-th.C: + th.Timeout() + case rq.inCh <- wsMsg{Jid: 1, What: what.Set, Data: "1.3"}: + } + + select { + case <-th.C: + th.Timeout() + case <-ts.setCalled: + } + + th.Equal(ts.Get(), 1.3) + + select { + case <-th.C: + th.Timeout() + case rq.inCh <- wsMsg{Jid: 1, What: what.Set, Data: "1.3"}: + } +} diff --git a/jsfunction.go b/jsfunction.go index d56afc4..4526e9f 100644 --- a/jsfunction.go +++ b/jsfunction.go @@ -1,5 +1,50 @@ package jaws +import ( + "encoding/json" + "io" + + "github.com/linkdata/jaws/what" +) + type JsFunction struct { JsName + Param AnySetter + Result AnySetter + ResultTag any +} + +func (ui *JsFunction) JawsRender(e *Element, w io.Writer, params []any) error { + ui.ResultTag = e.ApplyGetter(ui.Result) + return ui.render(ui.Param, nil, e, w, params) +} + +func (ui *JsFunction) JawsUpdate(e *Element) { + _ = e.JsCall(ui.Param.JawsGetAny(e)) +} + +func (ui *JsFunction) JawsEvent(e *Element, wht what.What, val string) (err error) { + err = ErrEventUnhandled + if wht == what.Set && ui.ResultTag != nil { + var v any + if err = json.Unmarshal([]byte(val), &v); err == nil { + _, err = e.maybeDirty(ui.ResultTag, ui.Result.JawsSetAny(e, v)) + } + } + return +} + +func NewJsFunction(param, result AnySetter, name string) *JsFunction { + if param == nil { + panic("NewJsFunction param is nil") + } + return &JsFunction{ + JsName: JsName{Name: name}, + Param: param, + Result: result, + } +} + +func (rq RequestWriter) JsFunction(param, result any, name string, params ...any) error { + return rq.UI(NewJsFunction(makeAnySetter(param), makeAnySetter(result), name), params...) } diff --git a/jsfunction_test.go b/jsfunction_test.go new file mode 100644 index 0000000..a3f9117 --- /dev/null +++ b/jsfunction_test.go @@ -0,0 +1,85 @@ +package jaws + +import ( + "strconv" + "testing" + + "github.com/linkdata/jaws/what" +) + +func TestJsFunction_JawsRender(t *testing.T) { + th := newTestHelper(t) + nextJid = 0 + rq := newTestRequest() + defer rq.Close() + + param := any("meh") + tsparam := newTestSetter(param) + result := any("foo") + tsresult := newTestSetter(result) + ui := NewJsFunction(tsparam, tsresult, "fnname") + th.NoErr(rq.UI(ui)) + wantHtml := "" + if gotHtml := rq.BodyString(); gotHtml != wantHtml { + t.Errorf("Request.JsFunction() = %q, want %q", gotHtml, wantHtml) + } + + tsparam.Set(1.3) + rq.Dirty(tsparam) + + select { + case <-th.C: + th.Timeout() + case msg := <-rq.outCh: + s := msg.Format() + if s != "Call\tJid.1\t1.3\n" { + t.Error(strconv.Quote(s)) + } + } +} + +func TestJsFunction_PanicsIfParamNil(t *testing.T) { + is := newTestHelper(t) + rq := newTestRequest() + defer rq.Close() + defer func() { + if x := recover(); x == nil { + is.Fail() + } + }() + NewJsFunction(nil, nil, "fnname") + is.Fail() +} + +func TestJsFunction_JawsEvent(t *testing.T) { + th := newTestHelper(t) + nextJid = 0 + rq := newTestRequest() + defer rq.Close() + + param := any("foo") + tsparam := newTestSetter(param) + result := any(float64(1.2)) + tsresult := newTestSetter(result) + th.NoErr(rq.JsFunction(tsparam, tsresult, "fnname")) + + select { + case <-th.C: + th.Timeout() + case rq.inCh <- wsMsg{Jid: 1, What: what.Set, Data: "1.3"}: + } + + select { + case <-th.C: + th.Timeout() + case <-tsresult.setCalled: + } + + th.Equal(tsresult.Get(), 1.3) + + select { + case <-th.C: + th.Timeout() + case rq.inCh <- wsMsg{Jid: 1, What: what.Set, Data: "1.3"}: + } +} diff --git a/jsstring_test.go b/jsstring_test.go index c3fb02e..d2d2b7f 100644 --- a/jsstring_test.go +++ b/jsstring_test.go @@ -41,9 +41,6 @@ func TestJsString_JawsEvent(t *testing.T) { rq := newTestRequest() defer rq.Close() - msgCh := make(chan string, 1) - defer close(msgCh) - val := "text'1" ts := newTestSetter(val) th.NoErr(rq.JsString(ts, "varname")) diff --git a/jsvariable.go b/jsvariable.go index dedb2c9..16d73b7 100644 --- a/jsvariable.go +++ b/jsvariable.go @@ -12,8 +12,12 @@ type JsVariable struct { func (ui *JsVariable) render(getter any, val any, e *Element, w io.Writer, params []any) (err error) { var data []byte - if data, err = json.Marshal(val); err == nil { - data = bytes.ReplaceAll(data, []byte(`'`), []byte(`\u0027`)) + if val != nil { + if data, err = json.Marshal(val); err == nil { + data = bytes.ReplaceAll(data, []byte(`'`), []byte(`\u0027`)) + } + } + if err == nil { err = ui.JsName.render(getter, data, e, w, params) } return diff --git a/wsmsg.go b/wsmsg.go index f50a179..fd97bb7 100644 --- a/wsmsg.go +++ b/wsmsg.go @@ -53,7 +53,7 @@ func wsParse(txt []byte) (wsMsg, bool) { if wht := what.Parse(string(txt[0:nl1])); wht.IsValid() { if id := jid.ParseString(string(txt[nl1+1 : nl2])); id.IsValid() { data := string(txt[nl2+1 : len(txt)-1]) - if txt[nl2+1] == '"' { + if wht != what.Set && txt[nl2+1] == '"' { var err error if data, err = strconv.Unquote(data); err != nil { return wsMsg{}, false