summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAsko Nõmm <asko@nmm.ee>2025-01-02 21:36:00 +0200
committerAsko Nõmm <asko@nmm.ee>2025-01-02 21:36:00 +0200
commit89848de97da23a8c732f54bde15080b09e2bb9d9 (patch)
tree8f257163125fbaff61fcc2f4565911acdbe4cf08
parent689fe2613765de71101214a482c928cfdb1b2be6 (diff)
bump
-rw-r--r--README.md18
-rw-r--r--htmtl/__init__.py2
-rw-r--r--htmtl/expression_parser.py75
-rw-r--r--htmtl/modifier.py11
-rw-r--r--htmtl/modifiers/truncate.py13
5 files changed, 75 insertions, 44 deletions
diff --git a/README.md b/README.md
index 216758e..a980e2e 100644
--- a/README.md
+++ b/README.md
@@ -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