Handle dynamic routes (#250)

This commit is contained in:
Thomas Brandého 2023-01-12 23:38:29 +01:00 committed by GitHub
parent 98e9edd98a
commit 9d59936737
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 12 deletions

View File

@ -184,11 +184,13 @@ class App(Base):
), "Path must be set if component is not a callable." ), "Path must be set if component is not a callable."
path = component.__name__ path = component.__name__
# Get args from the path for dynamic routes. # Check if the path given is valid
args = utils.get_path_args(path) utils.verify_path_validity(path)
self.state.setup_dynamic_args(utils.get_path_args(path))
# Generate the component if it is a callable. # Generate the component if it is a callable.
component = component if isinstance(component, Component) else component(*args) component = component if isinstance(component, Component) else component()
# Add meta information to the component. # Add meta information to the component.
compiler_utils.add_meta( compiler_utils.add_meta(

View File

@ -279,6 +279,39 @@ class State(Base, ABC):
""" """
return self.router_data.get("query", {}) return self.router_data.get("query", {})
@classmethod
def setup_dynamic_args(cls, args: dict[str, str]):
"""Set up args for easy access in renderer.
Args:
args: a dict of args
"""
def param_factory(param):
@ComputedVar
def inner_func(self) -> str:
return self.get_query_params().get(param, "")
return inner_func
def catchall_factory(param):
@ComputedVar
def inner_func(self) -> List:
return self.get_query_params().get(param, [])
return inner_func
for param, value in args.items():
if value == "catchall":
func = catchall_factory(param)
elif value == "patharg":
func = param_factory(param)
else:
continue
cls.computed_vars[param] = func.set_state(cls) # type: ignore
setattr(cls, param, func)
def __getattribute__(self, name: str) -> Any: def __getattribute__(self, name: str) -> Any:
"""Get the state var. """Get the state var.

View File

@ -771,12 +771,34 @@ def indent(text: str, indent_level: int = 2) -> str:
return os.linesep.join(f"{' ' * indent_level}{line}" for line in lines) + os.linesep return os.linesep.join(f"{' ' * indent_level}{line}" for line in lines) + os.linesep
def get_path_args(path: str) -> List[str]: def verify_path_validity(path: str) -> None:
"""Verify if the path is valid, and throw an error if not.
Args:
path: the path that need to be checked
Raises:
ValueError: explains what is wrong with the path.
"""
check_catchall = re.compile(r"^\[\.\.\.(.+)\]$")
catchall_found = False
for part in path.split("/"):
if catchall_found:
raise ValueError(f"Catch-all must be the last part of the URL: {path}")
match = check_catchall.match(part)
if match:
catchall_found = True
def get_path_args(path: str) -> Dict[str, str]:
"""Get the path arguments for the given path. """Get the path arguments for the given path.
Args: Args:
path: The path to get the arguments for. path: The path to get the arguments for.
Raises:
ValueError: explains what is wrong with the path.
Returns: Returns:
The path arguments. The path arguments.
""" """
@ -785,19 +807,27 @@ def get_path_args(path: str) -> List[str]:
# Regex to check for path args. # Regex to check for path args.
check = re.compile(r"^\[(.+)\]$") check = re.compile(r"^\[(.+)\]$")
check_catchall = re.compile(r"^\[\.\.\.(.+)\]$")
# Iterate over the path parts and check for path args. # Iterate over the path parts and check for path args.
args = [] args = {}
for part in os.path.split(path): for part in path.split("/"):
match = check_catchall.match(part)
if match:
arg_name = match.groups()[0]
if arg_name in args:
raise ValueError(f"arg [{arg_name}] is used more than once in this URL")
args[arg_name] = "catchall"
continue
match = check.match(part) match = check.match(part)
if match: if match:
# Add the path arg to the list. # Add the path arg to the list.
v = BaseVar( arg_name = match.groups()[0]
name=match.groups()[0], if arg_name in args:
type_=str, raise ValueError(f"arg [{arg_name}] is used more than once in this URL")
state=f"{constants.ROUTER}.query", args[arg_name] = "patharg"
)
args.append(v)
return args return args