From 4ea133608350e6e99008f46cb4c45dd18989b836 Mon Sep 17 00:00:00 2001 From: Asko Nõmm Date: Mon, 6 Jan 2025 01:16:33 +0200 Subject: Implement `Iterate` parser. --- README.md | 31 +++++++++++----------- htmtl/htmtl.py | 17 +++++++++--- htmtl/parsers/__init__.py | 10 ++++++- htmtl/parsers/iterate.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 5 files changed, 105 insertions(+), 22 deletions(-) create mode 100644 htmtl/parsers/iterate.py diff --git a/README.md b/README.md index 31bc1a0..032228e 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ you to use any editor without needing any additional editor extensions.

-
+

@@ -50,8 +50,8 @@ A simple example of how to use HTMTL with default configuration looks like this: ```python from htmtl import Htmtl -htmtl = Htmtl('

', {'who': 'World'}) -html = htmtl.html() # returns:

Hello World

+template = Htmtl('

', {'who': 'World'}) +html = template.to_html() # returns:

Hello World

``` ## Attributes @@ -214,15 +214,14 @@ Results in: ``` -### `foreach` +### `iterate` Loops anything iterable. -For example, to loop over a collection of `posts` and then use `post` as the variable of each iteration, you can do something -like this: +For example, to loop over a collection of `posts` and then use `post` as the variable of each iteration, you can do something like this: ```php -
+

``` @@ -231,7 +230,7 @@ If you do not care about using any of the iteration data, you can also entirely like so: ```php -
+
...
``` @@ -239,7 +238,7 @@ like so: And, you can also assign the key of the iteration to a variable, like so: ```php -
+

``` @@ -313,12 +312,12 @@ You can add (or replace) parsers in HTMTL when creating a new instance of the `H from htmtl import Htmtl from htmtl.parsers import InnerText -htmtl = Htmtl('

', {'who': 'World'}) -htmtl.set_parsers([ +template = Htmtl('

', {'who': 'World'}) +template.set_parsers([ InnerText, ]) -html = htmtl.html() # returns:

Hello World

+html = template.to_html() # returns:

Hello World

``` Prsers must extend the `Parser` class, like so: @@ -354,7 +353,7 @@ HTMTL is built upon the [Dompa](https://github.com/askonomm/dompa) HTML parser, - `htmtl.parsers.OuterPartial` - Parser the `outer-partial` attributes. - `htmtl.parsers.OuterHtml` - Parser the `outer-html` attributes. - `htmtl.parsers.OuterText` - Parser the `outer-text` attributes. -- `htmtl.parsers.Foreach` - Parses the `foreach` attributes. (**soon**) +- `htmtl.parsers.Iterate` - Parses the `iterate` attributes. ### Modifiers @@ -364,12 +363,12 @@ You can add (or replace) modifiers in HTMTL when creating a new instance of the from htmtl import Htmtl from htmtl.modifiers import Truncate -htmtl = Htmtl('

', {'who': 'World'}) -htmtl.set_modifiers([ +template = Htmtl('

', {'who': 'World'}) +template.set_modifiers([ Truncate, ]) -html = htmtl.html() # returns:

Hello World

+html = template.to_html() # returns:

Hello World

``` Mdifiers must extend the `Modifier` class, like so: diff --git a/htmtl/htmtl.py b/htmtl/htmtl.py index 7732f06..6570e11 100644 --- a/htmtl/htmtl.py +++ b/htmtl/htmtl.py @@ -1,18 +1,21 @@ from typing import Any from dompa import Dompa +from dompa.actions import ToHtml +from dompa.nodes import Node from .parser import Parser from .parsers.generic_value import GenericValue from .parsers.inner_html import InnerHtml from .parsers.inner_partial import InnerPartial from .parsers.inner_text import InnerText +from .parsers.iterate import Iterate from .parsers.outer_html import OuterHtml from .parsers.outer_partial import OuterPartial from .parsers.outer_text import OuterText +from .parsers.when import When +from .parsers.when_not import WhenNot from .modifier import Modifier from .modifiers.truncate import Truncate from .expression_parser import ExpressionParser -from .parsers.when import When -from .parsers.when_not import WhenNot class Htmtl: @@ -27,6 +30,7 @@ class Htmtl: # set default attribute parsers self.__attribute_parsers = [ + Iterate, InnerText, InnerHtml, InnerPartial, @@ -64,7 +68,12 @@ class Htmtl: parser_instance = parser(self.__data, expression_parser) self.__dom.traverse(parser_instance.traverse) - def html(self) -> str: + def to_html(self) -> str: + self.__parse() + + return self.__dom.action(ToHtml) + + def nodes(self) -> list[Node]: self.__parse() - return self.__dom.html() \ No newline at end of file + return self.__dom.get_nodes() \ No newline at end of file diff --git a/htmtl/parsers/__init__.py b/htmtl/parsers/__init__.py index 4ad8185..5c98cba 100644 --- a/htmtl/parsers/__init__.py +++ b/htmtl/parsers/__init__.py @@ -1,2 +1,10 @@ from .inner_text import InnerText -from .outer_text import OuterText \ No newline at end of file +from .inner_html import InnerHtml +from .inner_partial import InnerPartial +from .outer_text import OuterText +from .outer_html import OuterHtml +from .outer_partial import OuterPartial +from .generic_value import GenericValue +from .when import When +from .when_not import WhenNot +from .iterate import Iterate \ No newline at end of file diff --git a/htmtl/parsers/iterate.py b/htmtl/parsers/iterate.py new file mode 100644 index 0000000..a70d026 --- /dev/null +++ b/htmtl/parsers/iterate.py @@ -0,0 +1,67 @@ +from collections.abc import Iterable +from typing import Optional + +from dompa.nodes.actions import ToHtml +from dompa.nodes import Node, FragmentNode +from ..parser import Parser +import htmtl + +class IterateOp: + var: str + iter_var_as: Optional[str] + iter_index_as: Optional[str] + + def __init__(self, var: str, iter_var_as: Optional[str] = None, iter_index_as: Optional[str] = None): + self.var = var + self.iter_var_as = iter_var_as or None + self.iter_index_as = iter_index_as or None + + +class Iterate(Parser): + def traverse(self, node: Node) -> Optional[Node]: + if "iterate" in node.attributes: + replacement_nodes = [] + iterate_op = self.__parse_exp(node.attributes["iterate"]) + collection = self.expression(iterate_op.var) + node.attributes.pop("iterate") + + if isinstance(collection, Iterable): + data = self.data() + + for idx, item in enumerate(collection): + if iterate_op.iter_var_as: + data[iterate_op.iter_var_as] = item + + if iterate_op.iter_index_as: + data[iterate_op.iter_index_as] = idx + + template = htmtl.Htmtl(node.action(ToHtml), data) + template_nodes = template.nodes() + + if len(template_nodes) > 0: + replacement_nodes.append(template_nodes[0]) + + return FragmentNode(children=replacement_nodes) + + return node + + @staticmethod + def __parse_exp(exp: str) -> IterateOp: + parts = exp.split(" ") + + if len(parts) == 3: + if parts[2].count(":") == 1: + return IterateOp( + var=parts[0].strip(), + iter_index_as=parts[2].split(":")[0].strip(), + iter_var_as=parts[2].split(":")[1].strip() + ) + + return IterateOp( + var=parts[0].strip(), + iter_var_as=parts[2].strip() + ) + + return IterateOp( + var=parts[0] + ) diff --git a/pyproject.toml b/pyproject.toml index 440dc07..88142bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ authors = [ { name = "Asko Nõmm", email = "asko@nmm.ee" } ] dependencies = [ - "dompa>0.6.1" + "dompa>0.8.1" ] classifiers = [ "Programming Language :: Python :: 3", -- cgit v1.2.3