Skip to content

Commit

Permalink
feat: Add django support
Browse files Browse the repository at this point in the history
  • Loading branch information
paveldedik committed Jun 28, 2024
1 parent 9ef8326 commit d31c755
Show file tree
Hide file tree
Showing 12 changed files with 104 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools setuptools_scm
python -m pip install .[full,test]
python -m pip install .[full,test,django]
- name: Test with pytest
run: |
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ repos:
additional_dependencies:
- types-cachetools
- typeguard
- django

- repo: https://github.com/compilerla/conventional-pre-commit
rev: v3.2.0
Expand Down
3 changes: 3 additions & 0 deletions ludic/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ def __init__(self, *children: Any, **attrs: Any) -> None:
def __str__(self) -> str:
return self.to_html()

def __bytes__(self) -> bytes:
return self.to_html().encode("utf-8")

def __format__(self, _: str) -> str:
return self.formatter.append(self)

Expand Down
3 changes: 2 additions & 1 deletion ludic/catalog/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ def render(self) -> div:
return div(
self.children[0],
div(*self.children[1:], classes=["content"]),
**self.attrs,
)
else:
return div(div(*self.children, classes=["content"]))
return div(div(*self.children, classes=["content"]), **self.attrs)


class MessageSuccess(Message):
Expand Down
Empty file added ludic/contrib/__init__.py
Empty file.
2 changes: 2 additions & 0 deletions ludic/contrib/django/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .middlewares import LudicMiddleware as LudicMiddleware
from .responses import LudicResponse as LudicResponse
28 changes: 28 additions & 0 deletions ludic/contrib/django/middlewares.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from collections.abc import Callable

from django.http import HttpRequest, HttpResponse

from ludic.base import BaseElement


class LudicMiddleware:
"""Ludic middleware for Django to clean up the cache for f-strings.
Usage:
# somewhere in django settings.py
MIDDLEWARES = [
"...",
"ludic.contrib.django.LudicMiddleware",
]
"""

def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]) -> None:
self.get_response = get_response

def __call__(self, request: HttpRequest) -> HttpResponse:
with BaseElement.formatter:
response: HttpResponse = self.get_response(request)

return response
24 changes: 24 additions & 0 deletions ludic/contrib/django/responses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import Any

from django.http import HttpResponse

from ludic.types import AnyChildren


class LudicResponse(HttpResponse):
"""Class representing Ludic response for Django View.
Usage:
from django.http import HttpRequest
from ludic.html import p
from ludic.contrib.django import LudicResponse
def index(request: HttpRequest) -> LudicResponse:
return LudicResponse(p("Hello, World!"))
"""

def __init__(self, content: AnyChildren = "", *args: Any, **kwargs: Any) -> None:
super().__init__(str(content), *args, **kwargs)
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ full = [
"typeguard>=4.1.5",
"pygments",
]
django = [
"django",
]
dev = [
"mypy",
"types-pygments",
"django-stubs",
]
test = [
"pytest",
Expand Down
Empty file added tests/contrib/__init__.py
Empty file.
32 changes: 32 additions & 0 deletions tests/contrib/test_django.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import os

from django.http import HttpRequest

from ludic.base import BaseElement
from ludic.contrib.django import LudicMiddleware, LudicResponse
from ludic.html import p

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.contrib")


def test_django_response() -> None:
assert LudicResponse(p("test")).content == b"<p>test</p>"


def test_django_middleware() -> None:
get_response_called = False

def get_response(_: HttpRequest) -> LudicResponse:
nonlocal get_response_called
get_response_called = True

assert len(BaseElement.formatter.get()) == 0
response = LudicResponse(f"{p("does not clean up cache")}")
assert len(BaseElement.formatter.get()) == 1
return response

middleware = LudicMiddleware(get_response)
middleware(HttpRequest())

assert get_response_called
assert len(BaseElement.formatter.get()) == 0
9 changes: 7 additions & 2 deletions tests/test_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
from ludic.styles import CSSProperties


def test_str_and_bytes() -> None:
assert str(html.a("str")) == "<a>str</a>"
assert bytes(html.p("str")) == b"<p>str</p>"


def test_empty_element() -> None:
dom = html.div()
assert dom.to_html() == "<div></div>"
Expand Down Expand Up @@ -155,7 +160,7 @@ def test_repr_and_str_and_to_string() -> None:


def test_data_attributes() -> None:
dom = html.div("content", data_foo="1", data_bar="test") # type: ignore
dom = html.div("content", data_foo="1", data_bar="test") # type: ignore[call-arg]

assert dom.attrs == {"data_foo": "1", "data_bar": "test"}
assert dom.to_html() == '<div data-foo="1" data-bar="test">content</div>'
Expand All @@ -165,7 +170,7 @@ def test_htmx_attributes() -> None:
assert html.button(
"Get Info!",
hx_get="/info", hx_on__before_request="alert('Making a request!')",
).to_html() == ( # type: ignore
).to_html() == ( # type: ignore[call-arg]
'<button hx-get="/info" hx-on--before-request="alert(\'Making a request!\')">'
"Get Info!"
"</button>"
Expand Down

0 comments on commit d31c755

Please sign in to comment.