forked from reactive-python/reactpy-django
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdecorators.py
61 lines (45 loc) · 2 KB
/
decorators.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from __future__ import annotations
from functools import wraps
from typing import TYPE_CHECKING, Any, Callable
from reactpy import component
from reactpy.core.types import ComponentConstructor
from reactpy_django.exceptions import DecoratorParamError
from reactpy_django.hooks import use_user
if TYPE_CHECKING:
from django.contrib.auth.models import AbstractUser
def user_passes_test(
test_func: Callable[[AbstractUser], bool],
/,
fallback: Any | None = None,
) -> ComponentConstructor:
"""You can limit component access to users that pass a test function by using this decorator.
This decorator is inspired by Django's `user_passes_test` decorator, but works with ReactPy components.
Args:
test_func: A function that accepts a `User` returns a boolean.
fallback: The content to be rendered if the test fails. Typically is a ReactPy component or \
VDOM (`reactpy.html` snippet).
"""
def decorator(user_component):
@wraps(user_component)
def _wrapper(*args, **kwargs):
return _user_passes_test(
user_component, fallback, test_func, *args, **kwargs
)
return _wrapper
return decorator
@component
def _user_passes_test(component_constructor, fallback, test_func, *args, **kwargs):
"""Dedicated component for `user_passes_test` to allow us to always have access to hooks."""
user = use_user()
if test_func(user):
# Ensure that the component is a ReactPy component.
user_component = component_constructor(*args, **kwargs)
if not getattr(user_component, "render", None):
raise DecoratorParamError(
"`user_passes_test` is not decorating a ReactPy component. "
"Did you forget `@user_passes_test` must be ABOVE the `@component` decorator?"
)
# Render the component.
return user_component
# Render the fallback content.
return fallback(*args, **kwargs) if callable(fallback) else (fallback or None)