Skip to content

Commit

Permalink
chore
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-yin committed Aug 26, 2024
1 parent f1b1e97 commit 2c0202a
Show file tree
Hide file tree
Showing 3 changed files with 6 additions and 134 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Changelog = "https://github.com/rails-inspire-django/django-turbo-helper/release
python = ">=3.8"
django = ">=3.0"
django-actioncable = ">=1.0.4"
django-template-simplify = ">=1.0.2"

[tool.poetry.dev-dependencies]

Expand Down
3 changes: 2 additions & 1 deletion src/turbo_helper/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from template_simplify import dom_id

from .middleware import get_current_request
from .response import HttpResponseSeeOther, TurboStreamResponse
from .shortcuts import redirect_303, respond_to
from .signals import after_create_commit, after_delete_commit, after_update_commit
from .stream import register_turbo_stream_action, turbo_stream
from .templatetags.turbo_helper import dom_id

# extend turbo_stream actions, inspired by https://github.com/marcoroth/turbo_power
from .turbo_power import * # noqa
Expand Down
136 changes: 3 additions & 133 deletions src/turbo_helper/templatetags/turbo_helper.py
Original file line number Diff line number Diff line change
@@ -1,145 +1,15 @@
import re
from typing import Any, Optional

from django import template
from django.db.models.base import Model
from django.template import Node, TemplateSyntaxError
from django.template.base import token_kwargs
from template_simplify.templatetags.template_simplify import class_names, dom_id

from turbo_helper.renderers import render_turbo_frame, render_turbo_stream_from
from turbo_helper.stream import action_proxy

register = template.Library()


@register.simple_tag
def dom_id(instance: Any, prefix: Optional[str] = "") -> str:
"""
Generate a unique identifier for a Django model instance, class, or even Python object.
Args:
instance (Any): The instance or class for which the identifier is generated.
prefix (Optional[str]): An optional prefix to prepend to the identifier. Defaults to an empty string.
Returns:
str: The generated identifier.
Raises:
Exception: If the model instance does not have either the `to_key` or `pk` attribute.
Note:
- If `instance` is a Django model instance, the identifier is generated based on the `to_key` or `pk` attribute.
- If `instance` is a Django model class, the identifier is generated as `new_<class_name>`.
- If `instance` is neither a model instance nor a model class, the identifier is generated based on the `to_key`
attribute if available, otherwise it uses the string representation of the instance.
- The `prefix` argument can be used to prepend a prefix to the generated identifier.
"""
if not isinstance(instance, type) and isinstance(instance, Model):
# Django model instance
if hasattr(instance, "to_key") and getattr(instance, "to_key"): # noqa: B009
identifier = f"{instance.__class__.__name__.lower()}_{instance.to_key}"
elif hasattr(instance, "pk") and getattr(instance, "pk"): # noqa: B009
identifier = f"{instance.__class__.__name__.lower()}_{instance.pk}"
else:
raise Exception(
f"Model instance must have either to_key or pk attribute {instance}"
)
elif isinstance(instance, type) and issubclass(instance, Model):
# Django model class
identifier = f"new_{instance.__name__.lower()}"
else:
if hasattr(instance, "to_key") and getattr(instance, "to_key"): # noqa: B009
# Developer can still use to_key property to generate the identifier
identifier = f"{instance.to_key}"
else:
# Use the string representation
identifier = str(instance)

if prefix:
identifier = f"{prefix}_{identifier}"

return identifier


ATTRIBUTE_RE = re.compile(
r"""
(?P<attr>
[@\w:_\.\/-]+
)
(?P<sign>
\+?=
)
(?P<value>
['"]? # start quote
[^"']*
['"]? # end quote
)
""",
re.VERBOSE | re.UNICODE,
)


VALUE_RE = re.compile(
r"""
['"] # start quote (required)
(?P<value>
[^"']* # match any character except quotes
)
['"] # end quote (required)
""",
re.VERBOSE | re.UNICODE,
)


@register.tag
def class_names(parser, token):
error_msg = f"{token.split_contents()[0]!r} tag requires " "a list of css classes"
try:
bits = token.split_contents()
tag_name = bits[0] # noqa
attr_list = bits[1:]
except ValueError as exc:
raise TemplateSyntaxError(error_msg) from exc

css_ls = []
css_dict = {}
for pair in attr_list:
attribute_match = ATTRIBUTE_RE.match(pair) or VALUE_RE.match(pair)

if attribute_match:
dct = attribute_match.groupdict()
attr = dct.get("attr", None)
# sign = dct.get("sign", None)
value = parser.compile_filter(dct["value"])
if attr:
css_dict[attr] = value
else:
css_ls.append(value)
else:
raise TemplateSyntaxError("class_names found supported token: " + f"{pair}")

return ClassNamesNode(css_ls=css_ls, css_dict=css_dict)


class ClassNamesNode(Node):
def __init__(self, css_ls, css_dict):
self.css_ls = css_ls
self.css_dict = css_dict

def render(self, context):
final_css = []

# for common css classes
for value in self.css_ls:
final_css.append(value.token)

# for conditionals
for attr, expression in self.css_dict.items():
real_value = expression.resolve(context)
if real_value:
final_css.append(attr)

return " ".join(final_css)
register.simple_tag(dom_id, name="dom_id")
register.tag(class_names)


class TurboFrameTagNode(Node):
Expand Down

0 comments on commit 2c0202a

Please sign in to comment.