diff options
| author | Asko Nõmm <asko@nmm.ee> | 2025-01-02 21:36:00 +0200 |
|---|---|---|
| committer | Asko Nõmm <asko@nmm.ee> | 2025-01-02 21:36:00 +0200 |
| commit | 89848de97da23a8c732f54bde15080b09e2bb9d9 (patch) | |
| tree | 8f257163125fbaff61fcc2f4565911acdbe4cf08 | |
| parent | 689fe2613765de71101214a482c928cfdb1b2be6 (diff) | |
bump
| -rw-r--r-- | README.md | 18 | ||||
| -rw-r--r-- | htmtl/__init__.py | 2 | ||||
| -rw-r--r-- | htmtl/expression_parser.py | 75 | ||||
| -rw-r--r-- | htmtl/modifier.py | 11 | ||||
| -rw-r--r-- | htmtl/modifiers/truncate.py | 13 |
5 files changed, 75 insertions, 44 deletions
@@ -27,9 +27,9 @@ you to use any editor without needing any additional editor extensions. <div class="posts" if="posts"> <div foreach="posts as post"> <h2 class="post-title"> - <a :href="/blog/{post.url}" inner-text="{post.title | capitalize}"></a> + <a :href="/blog/{post.url}" inner-text="{post.title | Capitalize}"></a> </h2> - <div class="post-date" inner-text="{post.date | date:yyyy-MM-dd}"></div> + <div class="post-date" inner-text="{post.date | Date('yyyy-MM-dd')}"></div> <div class="post-content" inner-html="{post.body}"></div> </div> </div> @@ -269,14 +269,14 @@ If the `slug` key is `hello-world`. All interpolated values in expressions can be modified using modifiers. Modifiers are applied to the value of the attribute, and they can be chained, like so: ```html -<h1 inner-text="{title | uppercase | reverse}"></h1> +<h1 inner-text="{title | Uppercase | Reverse}"></h1> ``` Note that if you have nothing other than the interpolated variable in the attribute, then you can omit the curly brackets, and so this would also work: ```html -<h1 inner-text="title | uppercase | reverse"></h1> +<h1 inner-text="title | Uppercase | Reverse"></h1> ``` ### `date` @@ -284,7 +284,7 @@ this would also work: Parses the value into a formatted date string. ```html -<p inner-text="published_at | date:YYYY-mm-dd"></p> +<p inner-text="published_at | Date('YYYY-mm-dd')"></p> ``` ### `truncate` @@ -292,7 +292,7 @@ Parses the value into a formatted date string. Truncates the value to the specified length. ```html -<p inner-text="{title | truncate:10}"></p> +<p inner-text="{title | Truncate(10)}"></p> ``` This also works on collections, so you can use `truncate` to limit items in an array as well. @@ -370,10 +370,9 @@ Mdifiers must extend the `Modifier` class, like so: ```python from typing import Any -from htmtl import Modifier, modifier_name +from htmtl import Modifier -@modifier_name("truncate") class Truncate(Modifier): def modify(self, value: Any, opts: list[Any]) -> Any: if isinstance(value, str) and len(opts) > 0: @@ -386,9 +385,6 @@ class Truncate(Modifier): return value ``` -All modifiers need to have a name (that you will use in your templates), and you can name your modifier -with the `modifier_name` decorator. - #### List of built-in modifiers - `htmtl.modifiers.Truncate` - Truncates the value (both strings and collections). diff --git a/htmtl/__init__.py b/htmtl/__init__.py index f5708d1..7944cbf 100644 --- a/htmtl/__init__.py +++ b/htmtl/__init__.py @@ -1,3 +1,3 @@ from .htmtl import Htmtl from .parser import Parser -from .modifier import Modifier, modifier_name
\ No newline at end of file +from .modifier import Modifier
\ No newline at end of file diff --git a/htmtl/expression_parser.py b/htmtl/expression_parser.py index a99791b..795bde6 100644 --- a/htmtl/expression_parser.py +++ b/htmtl/expression_parser.py @@ -4,11 +4,11 @@ from .modifier import Modifier class ExpressionParser: __data: dict[str, Any] - __expression_modifiers: list[type[Modifier]] + __modifiers: list[type[Modifier]] - def __init__(self, data: dict[str, Any], expression_modifiers: list[type[Modifier]]) -> None: + def __init__(self, data: dict[str, Any], modifiers: list[type[Modifier]]) -> None: self.__data = data - self.__expression_modifiers = expression_modifiers + self.__modifiers = modifiers def parse(self, expression: str) -> Any: # no curly brackets means that the whole thing is an interpolation @@ -45,19 +45,70 @@ class ExpressionParser: modifiers = [x.strip() for x in parts[1:]] if len(parts) > 1 else [] for modifier in modifiers: - modifier_parts = modifier.split(":") - modifier_name = modifier_parts[0].strip() - modifier_opts = [x.strip() for x in modifier_parts[1:]] if len(modifier_parts) > 1 else [] - value = self.__use_modifier(value, modifier_name, modifier_opts) + modifier_name = "" + args_start = None + args_end = None + modifier_opts = [] + + for idx, char in enumerate(modifier): + if char == "(": + args_start = idx + 1 + + if char == ")": + args_end = idx + + if args_start is None and args_end is None: + modifier_name += char + + if args_start and args_end: + args_str = modifier[args_start:args_end] + modifier_opts = self.__parse_args_str_to_args(args_str) + + value = self.__modify_value(value, modifier_name, modifier_opts) return value - def __use_modifier(self, value: Any, modifier_name: str, modifier_opts: list[Any]) -> Any: - for modifier in self.__expression_modifiers: - modifier_instance = modifier() + @staticmethod + def __parse_args_str_to_args(args_str) -> list[str]: + args = [] + + for idx, char in enumerate(args_str): + if len(args) == 0: + args.append("") + + if char == "," and args[-1].count('"') % 2 == 0: + args.append("") + else: + args[-1] += char + + parsed_args = [] + + for arg in args: + if arg.startswith("'") and arg.endswith("'"): + parsed_args.append(arg[1:-1]) + continue + + if arg.startswith('"') and arg.endswith('"'): + parsed_args.append(arg[1:-1]) + continue + + if all([x in "1234567890" for x in arg.lstrip("-")]): + parsed_args.append(int(arg)) + continue + + if all([x in "1234567890." for x in arg.lstrip("-")]): + parsed_args.append(float(arg)) + continue + + if arg == "true" or arg == "false": + parsed_args.append(True if arg == "true" else False) + + return parsed_args - if modifier_instance.name == modifier_name: - return modifier_instance.modify(value, modifier_opts) + def __modify_value(self, value: Any, modifier_name: str, modifier_opts: list[Any]) -> Any: + for modifier in self.__modifiers: + if modifier.__name__ == modifier_name: + return modifier().modify(value, modifier_opts) return value diff --git a/htmtl/modifier.py b/htmtl/modifier.py index a771cca..c758022 100644 --- a/htmtl/modifier.py +++ b/htmtl/modifier.py @@ -3,17 +3,6 @@ from typing import Any class Modifier(ABC): - name: str - @abstractmethod def modify(self, value: Any, opts: list[Any]) -> Any: pass - - -def modifier_name(name: str): - def wrapper(cls: type[Modifier]): - cls.name = name - - return cls - - return wrapper
\ No newline at end of file diff --git a/htmtl/modifiers/truncate.py b/htmtl/modifiers/truncate.py index bac2862..85b1492 100644 --- a/htmtl/modifiers/truncate.py +++ b/htmtl/modifiers/truncate.py @@ -1,16 +1,11 @@ from typing import Any +from ..modifier import Modifier -from ..modifier import Modifier, modifier_name - -@modifier_name("truncate") class Truncate(Modifier): def modify(self, value: Any, opts: list[Any]) -> Any: - if isinstance(value, str) and len(opts) > 0: - if all([x in "1234567890" for x in opts[0]]): - char_limit = int(opts[0]) - - if len(value) > char_limit: - return f"{value[:char_limit - 3]}..." + if isinstance(value, str) and len(opts) > 0 and isinstance(opts[0], int): + if len(value) > opts[0]: + return f"{value[:opts[0] - 3]}..." return value |
