Hypertag. Nowy język szablonów HTML zintegrowany z Django

8

Został udostępniony nowy język, Hypertag (http://hypertag.io), służący do generowania dokumentów i tworzenia szablonów HTML. Jego składnia wzorowana jest na języku Python i opiera się na indentacji, dzięki czemu zapewnia dużą przejrzystość i łatwość użytkowania, m.in. poprzez brak tagów zamykających. Hypertag posiada szereg oryginalnych cech:

  • możliwość definiowania własnych re-używalnych tagów bezpośrednio w skrypcie,
  • zaawansowane wyrażenia i wsparcie dla wszystkich operatorów Python,
  • struktura modułów i instrukcje importu wzorowane na Python,
  • bloki sterujące (if, for, while, try), zmienne lokalne i kontekstowe,
  • przetwarzanie potokowe (pipeline syntax) i możliwość użycia dowolnej funkcji jako filtra,
  • operacje na drzewie DOM dokumentu w trakcie jego generowania,
  • pełna integracja z Django,
  • wiele innych...

Język Hypertag jest dostępny na GitHub i PyPI. Pełna dokumentacja na stronie: http://hypertag.io

Przykłady skryptów Hypertag...

PRZYKŁAD #1:

ul
    li | Cat
    li | Dog
    li | Rabbit & Guinea pig

Powyższy skrypt zawiera tagowane bloki tekstowe i generuje następujący kod HTML (konwersja znaków specjalnych, np. &, realizowana jest automatycznie):

<ul>
    <li>Cat</li>
    <li>Dog</li>
    <li>Rabbit &amp; Guinea pig</li>
</ul>

PRZYKŁAD #2:

h1 | Table of Contents
div
    for k in [1,2,3]:
        a href='#heading-{k}' : b : i | Heading no. $k

Powyższy skrypt zawiera blok sterujący (for), połączone łańcuchowo tagi (a, b, i) oraz atrybut (href). Wynikowy HTML to:

<h1>Table of Contents</h1>
<div>
    <a href="#heading-1"><b><i>Heading no. 1</i></b></a>
    <a href="#heading-2"><b><i>Heading no. 2</i></b></a>
    <a href="#heading-3"><b><i>Heading no. 3</i></b></a>
</div>

Język Hypertag może być wywoływany bezpośrednio z kodu Python, lub podłączony do Django jako silnik szablonów. Skrypty Hypertag mogą korzystać ze wszystkich filtrów dostępnych w Django. Szczegóły na stronie: http://hypertag.io

3

Zatem... Pug? ;-)

1

@Patryk27: Pewne elementy są wspólne: indentacja, brak tagów zamykających, podobna organizacja kodu, niektóre znaki specjalne... Hypertag był wzorowanych na językach indentowanych takich jak Pug, np. Haml, Slim, Shpaml.

Natomiast Hypertag dodaje zaawansowane elementy, których w Pug nie ma, np. definicje nowych tagów (natywnych hipertagów) bezpośrednio w skrypcie (http://hypertag.io/#custom-tags) - jest to konstrukcja podobna do definicji funkcji w Pythonie, która pozwala wydzielić powtarzalny fragment kodu i używać go wielokrotnie w różnych miejscach; w odróżnieniu od zwykłej funkcji, hipertag może akceptować specjalny argument "body", który jest automatycznie wypełniany drzewem DOM z miejsca użycia - więc można konstruować drzewo dokumentu etapowo, przesyłając je poprzez kolejne hipertagi. Daje to pełną modularyzację kodu, która była dotąd niedostępna w (popularnych) językach generowania dokumentów. Co więcej, drzewo DOM może być analizowane i modyfikowane wewnątrz hipertaga, co daje ogromne możliwości dalszej modularyzacji i z pewnością nie jest dostępne nigdzie indziej... Polecam prześledzić przykłady z Quick Start i dokumentacji:

Inna rzecz to struktura importów. Hypertag pozwala importować pojedyncze symbole (zmienne, tagi) z dowolnego innego skryptu. Pug jedynie pozwala dołączyć inny skrypt w całości, bez próby interpretowania jego struktury i bez możliwości wyboru tylko tych elementów, które są w danym miejscu potrzebne.

Wydaje mi się też, że w Pug nie ma np. instrukcji przypisania, czyli możliwość tworzenia zmiennych jest ograniczona, tak samo jak możliwość tworzenia złożonych wyrażeń: manipulowania obiektami itp. Hypertag pozwala na budowanie dowolnych wyrażeń, jak w Pythonie, czyli np. można konstruować obiekty, wywoływać ich metody itp. Choć zaznaczam, że znam Pug wyłącznie z dokumentacji, więc mogę o pewnych szczegółach nie wiedzieć.

5

O ile jestem w stanie zrozumieć ideę separacji szablonu z wyglądem i slotami wklejanych elementów {cośtam}.

To nie kumam, jaki jest sens tworzenia całego nowego języka z instrukcjami warunkowymi, pętlami itp, który robi dokładnie to samo, co robiłby tam np. PHP, czyli ponownie miesza ze sobą warstwy logiki i prezentacji, tyle tylko, że robi to w ramach swoich własnych procedur.

2

@Freja Draco: Popularne języki szablonów, np. Jinja2 czy Django templates, też posiadają pętle i instrukcje warunkowe (!) - po to, żeby ułatwić programistom kodowanie i pozwolić na tworzenie bardziej czytelnego kodu, bo niestety teoria podziału na "logikę" i "prezentację" jest tyleż pociągająca, co w praktyce trudna do realizacji, poza najprostszymi przypadkami.

Dlatego, tak jak każdy język programowania posiada elementy pozwalające na generowanie warstwy prezentacji - poprzez chociażby instrukcje "print" oraz różnego rodzaju napisy formatowane (f-strings w Pythonie, printf() w C itd.), tak samo języki opisu dokumentu potrzebują niektórych konstrukcji typowo programistycznych, żeby ułatwić kodowanie i np. zredukować problem duplikacji kodu, który jest nagminny w HTML, i który dopiero w Hypertag jest rozwiązany w sposób kompleksowy poprzez natywne hipertagi (https://github.com/mwojnars/hypertag#custom-tags). Ostatecznym celem jest tworzenie czytelnego, łatwego w utrzymaniu kodu, i tworzenie go możliwie niskim nakładem pracy - w osiągnięciu tak zdefiniowanego celu ma pomagać Hypertag.

Jeżeli już mówimy o podziale na logikę i prezentację, to polecam przykład generowania spisu treści (ToC). Pytanie jest następujące: czy ToC jest elementem logiki, czy prezentacji?

Moim zdaniem, ToC jest integralnym elementem dokumentu, który np. posiada swój layout i własne style css, więc zgodnie z teorią, powinien być umieszczony w kodzie prezentacji. Jednakże, ToC w 100% zależy od treści dokumentu, więc jeżeli - zgodnie z podstawową zasadą programistyczną, DRY - chcemy uniknąć duplikacji kodu (dwukrotnego wpisywania z palca wszystkich nagłówków, raz w ToC, a drugi raz w treści dokumentu), to jesteśmy zmuszeni wprowadzić automatyzację: wyliczanie ToC na podstawie treści. I tutaj niestety pojawia się konieczność wykorzystania konstrukcji typowo programistycznych, bo samym HTML-em takiej automatyzacji nie zrobimy. Co więcej, nie zrobimy jej również popularnymi językami szablonów - chyba że wydzielimy generowanie ToC na zewnątrz jako funkcję pythonową, ale to z kolei będzie niezgodne z naszą teorią podziału (ToC jest częścią prezentacji...)

W tym miejscu dochodzimy do Hypertag. Jest to jedyny znany mi język opisu dokumentu, który pozwala powyższy problem rozwiązać, tzn. wygenerować ToC automatycznie (bez duplikacji kodu), i zrobić to w kodzie prezentacji, tam gdzie tworzony jest dokument, bez posiłkowania się zewnętrznymi funkcjami pythonowymi. Dzięki oryginalnym elementom składni języka, wystarczy na to kilka linijek kodu, rozwiązanie jest elementarnie proste - a jak to zrobić jest pokazane w dokumentacji :)

http://hypertag.io/#example-toc-generation

1

@Freja Draco: bo idea separacji logiki od wyglądu się nie sprawdziła. Jest zasadnicza różnica pomiędzy HTML z osadzonym kodem (jak PHP), a kodem, który generuje po drodze HTML (np. JSX i chyba to powyżej).

2
  1. pod kątem czytelności to lisp jest czyteniejszy :-)
  2. pod kątem szablonów ciężko wskazać czemu to jest lepsze niż np. jinja2 (jinja2 nie dość, że jest prosta to umożliwia pisanie własnych makr - funkcji które osadzają html)
  3. pod kątem django to DTL mimo, że jest gorszy to ma więcej pluginów i jeśli ktoś używa Django (bo full wypas framework) to za bardzo nie opłaca się zmierzać w kierunku innych silników szablonów
2

A mi się nawet podoba. :)

9
Marcin Wojnarski napisał(a):

@Freja Draco: Popularne języki szablonów, np. Jinja2 czy Django templates, też posiadają pętle i instrukcje warunkowe (!)

Wiem, i w nich też tego nie rozumiem.
Serio jest taka oszczędność i poprawa czytelności na:

h1 | Table of Contents
div
    for k in [1,2,3]:
        a href='#heading-{k}' : b : i | Heading no. $k

względem:

echo "<h1>Table of Contents</h1>";     
echo "<div>";     
for (i=1; i<=4; i++) {
  echo "<a href='#heading-{$i}'><b><i>Heading no. {$i}</i></b></a>";
}
echo "</div>";

Jeśli po stronie kosztów wrzucisz:

  • konieczność przetworzenia tego przez kolejny parser,
  • konieczność ogarnięcia jego składni,
  • kolejną warstwę abstrakcji, która utrudnia znalezienie ewentualnego babola.

Bo jak dla mnie np. taki zapis: a href='#heading-{k}' : b : i | Heading no. $k
to jakieś hieroglify z tej samej ligi co regeksy.

Już zwykła minifikacja html/css/js potrafi utrudniać znajdowanie błędów.

p.s. Ale tak tylko sobie tu dywaguję, bo pewnie lamerka jestem.

4

Zwykłe "wymyślanie koła na nowo" i to trochę kanciastego, bo czytelność tego poraża

2
Freja Draco napisał(a):
Marcin Wojnarski napisał(a):

@Freja Draco: Popularne języki szablonów, np. Jinja2 czy Django templates, też posiadają pętle i instrukcje warunkowe (!)

Wiem, i w nich też tego nie rozumiem.
Serio jest taka oszczędność i poprawa czytelności na:

i racja

Prawie od ponad 15 lat piszę strony w czystym PHP nigdy nie dałem się przekonać do Twiga, Smarty itp... Dobry podział strony na pojedyncze obiekty zawsze pozwalał i wciąż pozwala na minimalizację wielkości plików widoku i występujących w nich iteracji i warunków. Metoda mieszania HTML z PHP była naprzemiennie wychwalana albo wyśmiewana. Największą hipokryzją było wyśmiewanie widoków z kodem PHP przez "twigowców". Teraz ponownie widzę tendencje do mieszania HTML z kodem języka czy to PHP czy react itp... Wszystkie te języki do widoków są super dopóki mamy jeden if albo jedną pętlę na widok.
Osobiście zupełnie nie rozumiem wszelkich wynalazków, które usilnie próbują odwzorować jedynie to co w zwykłym PHP można było robić od 20 lat.

2

Czyli generalnie połączenie największych wad obu światów. Pythonic way of web templating
Pyhthonic way of web templating to nie robienie ich wcale, jakbym chciał fronty klepać to bym frontem został.
No ja jestem na nie osobiście, ale może komuś się przyda/spodoba.
Chociaż imo i tak Jinja2 FTW.

Pół projektów tego używa i to zazwyczaj nie w kontekście flaska czy django, bo to tak dobre narzędzie.

EDIT: W sumie to pprzejrzałem sobie tam jeszcze kod. Odniosłem wrażenie, że @Marcin Wojnarski pisząc tą libkę miałeś jakiś mocno konkretny use case pod nosem i stąd pojawiłą się potrzeba na ten projekt. Czy nie?
Bo widzę całkiem sporo pracy włożonej, przemyślenia i różnych wzorców, niekoniecznie takich 'basicowych' powiedzmy - nie jest to gównokawałek kodu sklepany przez byle kogo - wręcz przeciwnie, od strony technicznej, ale brakuje mi tu spięcia 'biznesowoego' czy 'produktowego', by razem się to dobrze spinało.
O, coś takiego.

1

Będę tym straszył dzieci jak będą niegrzeczne. Żeby nie było, że to wyjątkowy rak - taki sam jak HAML czy inne Pugi.

2
Saalin napisał(a):

Będę tym straszył dzieci jak będą niegrzeczne. Żeby nie było, że to wyjątkowy rak - taki sam jak HAML czy inne Pugi.

Żeby nie było że tylko inni mają to Scala też ma swoje patologie

scaml:

%html
  %body
    The quick brown fox jumps 
    over the lazy dog

jade:

html
  body
    | The quick brown fox jumps 
    | over the lazy dog
1

Jak rozumiem wcięcia w Pythonie, tak nie lubię yamli, które się źle czyta. Niestety trochę to podobne to yamli.

0

Po co wam to jak resty robicie? :)
A jak ktos siedzi we froncie to nie bedzie jakiegos Pythona bral tylko np. wyzej wymieniony jade

EDIT:
Dla porownania jeszcze: https://kotlinlang.org/docs/typesafe-html-dsl.html

import kotlinx.browser.*
import kotlinx.html.*
import kotlinx.html.dom.*

fun main() {
    document.body!!.append.div {
        h1 {
            +"Welcome to Kotlin/JS!"
        }
        p {
            +"Fancy joining this year's "
            a("https://kotlinconf.com/") {
                +"KotlinConf"
            }
            +"?"
        }
    }
}
3

@Freja Draco:

Ja bym to zrobił tak, jest bardziej czytelne i bardziej pokazuje zbędność silnika szablonów:

<h1>Table of Contents</h1>
<div><?php
for (i=1; i<=4; i++) {
  echo "<a href='#heading-{$i}'><b><i>Heading no. {$i}</i></b></a>";
}
?></div>

Dla mnie silnik szablonów ma największy sens w rzadko występującej sytuacji, kiedy oddajemy komuś niezaufanemu dostęp do warstwy prezentacji, i boimy się dawać dostęp do PHP, lub generalnie: backendu bez względu na język(i) które w nim działają.

Wtedy dobry język skryptowy szablonu, nie pozwoli ani na włamanie do systemu, ani np. na zbyt długą egzekucję kodu lub zajęcie zasobów.

Rozdział warstwy prezentacji od reszty ma jak najbardziej sens i ten rozdział się maksymalizuje, ale nie da się osiągnąć w 100%: pętle i jakieś instrukcje warunkowe są niezbędne, ani nie utonąć w absurdalnie dużej fragmentacji treść przeznaczonych dla frontendu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1