Skip to content

Commit 981fd43

Browse files
authored
remove json patch (#881)
* remove json patch * fix tests * use sequence instead of list * fix more tests
1 parent d1a69f6 commit 981fd43

21 files changed

+244
-278
lines changed

requirements/test-env.txt

+3
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ playwright
1010

1111
# I'm not quite sure why this needs to be installed for tests with Sanic to pass
1212
sanic-testing
13+
14+
# Used to generate model changes from layout update messages
15+
jsonpointer

src/client/package-lock.json

+29-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/client/packages/idom-client-react/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"author": "Ryan Morshead",
33
"dependencies": {
4-
"fast-json-patch": "^3.1.1",
5-
"htm": "^3.0.3"
4+
"htm": "^3.0.3",
5+
"json-pointer": "^0.6.2"
66
},
77
"description": "A client for IDOM implemented in React",
88
"devDependencies": {

src/client/packages/idom-client-react/src/components.js

+22-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from "react";
22
import ReactDOM from "react-dom";
33
import htm from "htm";
4+
import { set as setJsonPointer } from "json-pointer";
45

5-
import { useJsonPatchCallback } from "./json-patch.js";
66
import { useImportSource } from "./import-source.js";
77
import { LayoutContext } from "./contexts.js";
88

@@ -14,17 +14,30 @@ import {
1414
const html = htm.bind(React.createElement);
1515

1616
export function Layout({ saveUpdateHook, sendEvent, loadImportSource }) {
17-
const [model, patchModel] = useJsonPatchCallback({});
17+
const currentModel = React.useState({})[0];
18+
const forceUpdate = useForceUpdate();
19+
20+
const patchModel = React.useCallback(
21+
({ path, model }) => {
22+
if (!path) {
23+
Object.assign(currentModel, model);
24+
} else {
25+
setJsonPointer(currentModel, path, model);
26+
}
27+
forceUpdate();
28+
},
29+
[currentModel]
30+
);
1831

1932
React.useEffect(() => saveUpdateHook(patchModel), [patchModel]);
2033

21-
if (!Object.keys(model).length) {
34+
if (!Object.keys(currentModel).length) {
2235
return html`<${React.Fragment} />`;
2336
}
2437

2538
return html`
2639
<${LayoutContext.Provider} value=${{ sendEvent, loadImportSource }}>
27-
<${Element} model=${model} />
40+
<${Element} model=${currentModel} />
2841
<//>
2942
`;
3043
}
@@ -200,3 +213,8 @@ function _ImportedElement({ model, importSource }) {
200213

201214
return html`<div ref=${mountPoint} />`;
202215
}
216+
217+
function useForceUpdate() {
218+
const [, updateState] = React.useState();
219+
return React.useCallback(() => updateState({}), []);
220+
}

src/client/packages/idom-client-react/src/element-utils.js

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ function createEventHandler(eventName, sendEvent, eventSpec) {
5151
sendEvent({
5252
data: data,
5353
target: eventSpec["target"],
54+
type: "layout-event",
5455
});
5556
};
5657
}

src/client/packages/idom-client-react/src/json-patch.js

-60
This file was deleted.

src/client/packages/idom-client-react/src/mount.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ function mountLayoutWithReconnectingWebSocket(
5151
};
5252

5353
socket.onmessage = (event) => {
54-
const [pathPrefix, patch] = JSON.parse(event.data);
55-
updateHookPromise.promise.then((update) => update(pathPrefix, patch));
54+
const message = JSON.parse(event.data);
55+
updateHookPromise.promise.then((update) => update(message));
5656
};
5757

5858
socket.onclose = (event) => {

src/idom/backend/default.py

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

33
import asyncio
4+
from logging import getLogger
5+
from sys import exc_info
46
from typing import Any, NoReturn
57

68
from idom.types import RootComponentConstructor
@@ -9,6 +11,9 @@
911
from .utils import all_implementations
1012

1113

14+
logger = getLogger(__name__)
15+
16+
1217
def configure(
1318
app: Any, component: RootComponentConstructor, options: None = None
1419
) -> None:
@@ -53,6 +58,7 @@ def _default_implementation() -> BackendImplementation[Any]:
5358
try:
5459
implementation = next(all_implementations())
5560
except StopIteration: # pragma: no cover
61+
logger.debug("Backend implementation import failed", exc_info=exc_info())
5662
raise RuntimeError("No built-in server implementation installed.")
5763
else:
5864
_DEFAULT_IMPLEMENTATION = implementation

src/idom/backend/flask.py

+11-15
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
3838
from idom.backend.hooks import ConnectionContext
3939
from idom.backend.hooks import use_connection as _use_connection
4040
from idom.backend.types import Connection, Location
41-
from idom.core.layout import LayoutEvent, LayoutUpdate
42-
from idom.core.serve import serve_json_patch
41+
from idom.core.serve import serve_layout
4342
from idom.core.types import ComponentType, RootComponentConstructor
4443
from idom.utils import Ref
4544

@@ -182,8 +181,8 @@ def model_stream(ws: WebSocket, path: str = "") -> None:
182181
def send(value: Any) -> None:
183182
ws.send(json.dumps(value))
184183

185-
def recv() -> LayoutEvent:
186-
return LayoutEvent(**json.loads(ws.receive()))
184+
def recv() -> Any:
185+
return json.loads(ws.receive())
187186

188187
_dispatch_in_thread(
189188
ws,
@@ -203,7 +202,7 @@ def _dispatch_in_thread(
203202
path: str,
204203
component: ComponentType,
205204
send: Callable[[Any], None],
206-
recv: Callable[[], Optional[LayoutEvent]],
205+
recv: Callable[[], Optional[Any]],
207206
) -> NoReturn:
208207
dispatch_thread_info_created = ThreadEvent()
209208
dispatch_thread_info_ref: idom.Ref[Optional[_DispatcherThreadInfo]] = idom.Ref(None)
@@ -213,18 +212,15 @@ def run_dispatcher() -> None:
213212
loop = asyncio.new_event_loop()
214213
asyncio.set_event_loop(loop)
215214

216-
thread_send_queue: "ThreadQueue[LayoutUpdate]" = ThreadQueue()
217-
async_recv_queue: "AsyncQueue[LayoutEvent]" = AsyncQueue()
215+
thread_send_queue: "ThreadQueue[Any]" = ThreadQueue()
216+
async_recv_queue: "AsyncQueue[Any]" = AsyncQueue()
218217

219218
async def send_coro(value: Any) -> None:
220219
thread_send_queue.put(value)
221220

222-
async def recv_coro() -> Any:
223-
return await async_recv_queue.get()
224-
225221
async def main() -> None:
226222
search = request.query_string.decode()
227-
await serve_json_patch(
223+
await serve_layout(
228224
idom.Layout(
229225
ConnectionContext(
230226
component,
@@ -239,7 +235,7 @@ async def main() -> None:
239235
),
240236
),
241237
send_coro,
242-
recv_coro,
238+
async_recv_queue.get,
243239
)
244240

245241
main_future = asyncio.ensure_future(main(), loop=loop)
@@ -282,9 +278,9 @@ def run_send() -> None:
282278

283279
class _DispatcherThreadInfo(NamedTuple):
284280
dispatch_loop: asyncio.AbstractEventLoop
285-
dispatch_future: "asyncio.Future[Any]"
286-
thread_send_queue: "ThreadQueue[LayoutUpdate]"
287-
async_recv_queue: "AsyncQueue[LayoutEvent]"
281+
dispatch_future: asyncio.Future[Any]
282+
thread_send_queue: ThreadQueue[Any]
283+
async_recv_queue: AsyncQueue[Any]
288284

289285

290286
@dataclass

src/idom/backend/sanic.py

+6-12
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,8 @@
1414
from sanic_cors import CORS
1515

1616
from idom.backend.types import Connection, Location
17-
from idom.core.layout import Layout, LayoutEvent
18-
from idom.core.serve import (
19-
RecvCoroutine,
20-
SendCoroutine,
21-
Stop,
22-
VdomJsonPatch,
23-
serve_json_patch,
24-
)
17+
from idom.core.layout import Layout
18+
from idom.core.serve import RecvCoroutine, SendCoroutine, Stop, serve_layout
2519
from idom.core.types import RootComponentConstructor
2620

2721
from ._common import (
@@ -169,7 +163,7 @@ async def model_stream(
169163
scope = asgi_app.transport.scope
170164

171165
send, recv = _make_send_recv_callbacks(socket)
172-
await serve_json_patch(
166+
await serve_layout(
173167
Layout(
174168
ConnectionContext(
175169
constructor(),
@@ -198,14 +192,14 @@ async def model_stream(
198192
def _make_send_recv_callbacks(
199193
socket: WebSocketConnection,
200194
) -> Tuple[SendCoroutine, RecvCoroutine]:
201-
async def sock_send(value: VdomJsonPatch) -> None:
195+
async def sock_send(value: Any) -> None:
202196
await socket.send(json.dumps(value))
203197

204-
async def sock_recv() -> LayoutEvent:
198+
async def sock_recv() -> Any:
205199
data = await socket.recv()
206200
if data is None:
207201
raise Stop()
208-
return LayoutEvent(**json.loads(data))
202+
return json.loads(data)
209203

210204
return sock_send, sock_recv
211205

0 commit comments

Comments
 (0)