Skip to content

Commit 937da20

Browse files
committed
get test_app runserver to work
1 parent f8f094e commit 937da20

File tree

23 files changed

+144
-127
lines changed

23 files changed

+144
-127
lines changed

MANIFEST.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
include src/django_idom/static/js/idom.js
2+
include src/django_idom/templates/head_content.html
3+
include src/django_idom/templates/view.html

noxfile.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ def test_suite(session: Session) -> None:
5252
session.chdir(HERE / "tests")
5353
session.env["IDOM_DEBUG_MODE"] = "1"
5454
session.env["SELENIUM_HEADLESS"] = str(int("--headless" in session.posargs))
55-
session.run("python", "manage.py", "build_js")
5655
session.run("python", "manage.py", "test")
5756

5857

src/django_idom/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
from .websocket_consumer import IdomAsyncWebSocketConsumer
1+
from .websocket_consumer import (
2+
IdomAsyncWebSocketConsumer,
3+
django_idom_websocket_consumer_url,
4+
)
25

36

47
__version__ = "0.0.1"
5-
__all__ = ["IdomAsyncWebSocketConsumer"]
8+
__all__ = ["IdomAsyncWebSocketConsumer", "django_idom_websocket_consumer_url"]

src/django_idom/app_components.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import logging
2+
from importlib import import_module
3+
from typing import Dict
4+
5+
from django.conf import settings
6+
from idom.core.proto import ComponentConstructor
7+
8+
9+
logger = logging.getLogger(__name__)
10+
_LOADED_COMPONENTS: Dict[str, ComponentConstructor] = {}
11+
12+
13+
def get_component(name: str) -> ComponentConstructor:
14+
return _LOADED_COMPONENTS[name]
15+
16+
17+
def has_component(name: str) -> bool:
18+
return name in _LOADED_COMPONENTS
19+
20+
21+
for app_mod_name in settings.INSTALLED_APPS:
22+
idom_mod_name = f"{app_mod_name}.idom"
23+
24+
try:
25+
idom_mod = import_module(idom_mod_name)
26+
except ImportError:
27+
logger.debug(f"Skipping {idom_mod_name!r} - does not exist")
28+
continue
29+
30+
if not hasattr(idom_mod, "__all__"):
31+
logger.warning(
32+
f"'django_idom' expected module {idom_mod_name!r} to have an "
33+
"'__all__' attribute that lists its publically available components."
34+
)
35+
continue
36+
37+
for component_name in idom_mod.__all__:
38+
try:
39+
component_constructor = getattr(idom_mod, component_name)
40+
except AttributeError:
41+
logger.warning(
42+
f"Module {idom_mod_name!r} has no attribute {component_name!r}"
43+
)
44+
continue
45+
46+
if not callable(component_constructor):
47+
logger.warning(f"'{idom_mod_name}.{component_name}' is not a component")
48+
continue
49+
50+
_LOADED_COMPONENTS[f"{app_mod_name}.{component_name}"] = component_constructor

src/django_idom/app_settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
from django.conf import settings
22

3+
34
IDOM_WEBSOCKET_URL = getattr(settings, "IDOM_WEBSOCKET_URL", "_idom/")
5+
if not IDOM_WEBSOCKET_URL.endswith("/"):
6+
IDOM_WEBSOCKET_URL += "/"

src/django_idom/templates/idom/root.html

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
<div id="{{ idom_view_id }}"></div>
1+
{% load static %}
2+
<div id="{{ idom_mount_uuid }}"></div>
23
<script type="module" crossorigin="anonymous">
3-
import { mountToElement } from "{% static 'js/idom.js' %}";
4-
mountViewToElement("{{ idom_websocket_url }}", "{{ idom_view_id }}");
4+
import { mountViewToElement } from "{% static 'js/idom.js' %}";
5+
const mountPoint = document.getElementById("{{ idom_mount_uuid }}");
6+
mountViewToElement(
7+
mountPoint,
8+
"{{ idom_websocket_url }}",
9+
"{{ idom_view_id }}",
10+
"{{ idom_view_params }}"
11+
);
512
</script>

src/django_idom/templatetags/idom.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from uuid import uuid4
2+
13
from django import template
24

35
from django_idom.app_settings import IDOM_WEBSOCKET_URL
@@ -8,10 +10,15 @@
810

911
# Template tag that renders the IDOM scripts
1012
@register.inclusion_tag("idom/head_content.html")
11-
def idom_scripts():
13+
def idom_head():
1214
pass
1315

1416

1517
@register.inclusion_tag("idom/view.html")
16-
def idom_view(view_id):
17-
return {"idom_websocket_url": IDOM_WEBSOCKET_URL, "view_id": view_id}
18+
def idom_view(view_id, view_params=""):
19+
return {
20+
"idom_websocket_url": IDOM_WEBSOCKET_URL,
21+
"idom_mount_uuid": uuid4().hex,
22+
"idom_view_id": view_id,
23+
"idom_view_params": view_params,
24+
}

src/django_idom/view_loader.py

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/django_idom/websocket_consumer.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,35 @@
22
import asyncio
33
import logging
44
from typing import Any
5+
from urllib.parse import parse_qsl
56

67
from channels.generic.websocket import AsyncJsonWebsocketConsumer
8+
from django.urls import path
79
from idom.core.dispatcher import dispatch_single_view
810
from idom.core.layout import Layout, LayoutEvent
911

10-
from .view_loader import ALL_VIEWS
12+
from .app_components import get_component, has_component
13+
from .app_settings import IDOM_WEBSOCKET_URL
1114

1215

1316
logger = logging.getLogger(__name__)
1417

1518

19+
def django_idom_websocket_consumer_url(*args, **kwargs):
20+
"""Return a URL resolver for :class:`IdomAsyncWebSocketConsumer`
21+
22+
While this is relatively uncommon in most Django apps, because the URL of the
23+
websocket must be defined by the setting ``IDOM_WEBSOCKET_URL``. There's no need
24+
to allow users to configure the URL themselves
25+
"""
26+
return path(
27+
IDOM_WEBSOCKET_URL + "<view_id>/",
28+
IdomAsyncWebSocketConsumer.as_asgi(),
29+
*args,
30+
**kwargs,
31+
)
32+
33+
1634
class IdomAsyncWebSocketConsumer(AsyncJsonWebsocketConsumer):
1735
"""Communicates with the browser to perform actions on-demand."""
1836

@@ -34,23 +52,21 @@ async def receive_json(self, content: Any, **kwargs: Any) -> None:
3452
await self._idom_recv_queue.put(LayoutEvent(**content))
3553

3654
async def _run_dispatch_loop(self):
37-
# get the URL parameters and grab the view ID
38-
view_id = ...
39-
# get component ags from the URL params too
40-
component_args = ...
55+
view_id = self.scope["url_route"]["kwargs"]["view_id"]
4156

42-
if view_id not in ALL_VIEWS:
43-
logger.warning(f"Uknown IDOM view ID {view_id}")
57+
if not has_component(view_id):
58+
logger.warning(f"Uknown IDOM view ID {view_id!r}")
4459
return
4560

46-
component_constructor = ALL_VIEWS[view_id]
61+
component_constructor = get_component(view_id)
62+
component_kwargs = dict(parse_qsl(self.scope["query_string"]))
4763

4864
try:
49-
component_instance = component_constructor(*component_args)
65+
component_instance = component_constructor(**component_kwargs)
5066
except Exception:
5167
logger.exception(
5268
f"Failed to construct component {component_constructor} "
53-
f"with parameters {component_args}"
69+
f"with parameters {component_kwargs}"
5470
)
5571
return
5672

0 commit comments

Comments
 (0)