Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   Templating System for Node.JS. (https://javascript.ru/forum/project/27690-templating-system-node-js.html)

B~Vladi 21.04.2012 17:02

Templating System for Node.JS.
 
Допилил. Наконец-то.
В общем, тем кто ещё не знает, хочу рассказать про свой шаблонизатор для NodeJS.

Исходники: https://github.com/B-Vladi/TSN

Основные характеристики:
  • Простота. Т.е. это классический шаблонизатор, типа CTPP. Не требуется изучать новый синтаксис. Используется только HTML и JS.
  • Гибкость. За счет переменных и динамической подстановки контекста.
  • Скорость. Рвет fest, а fest рвет CTPP. Так же рвет Dust и Mu.
  • Расширяемость. Свои теги можно реализовать в этом файле, используя API шаблонизатора. Под это документации пока нет, но всегда можно подсмотреть в других тегах или спросить у меня.
  • IDE-friendly. Подсветка, автокомплит и валидация (если подцепить DTD к IDE), Zen-coding, не ломает синтаксис.

Некоторые моменты:
  • Подчищает отступы и переводы строк так, если бы шаблон изначально писался без тегов TSN.
  • При использовании TSN-сущности, допустимы только символы /a-z0-9\-_\./, в соответствии со спецификацией XML.
  • При парсинге корректно обрабатывает ошибки вложенности тегов. Обрабатывает только TSN-теги.
  • Скомпилированный шаблон можно отправить на клиент и юзать там, но для этого лучше использовать специально заточенные под это шаблонизаторы, имхо.
  • В значениях атрибутов тегов TSN можно использовать следующие сущности: & < > " ', а можно и не использовать, кому как больше нравиться.

На данный момент нет такого же классического XML-шаблонизатора для ноды. Да и XML-шаблонизаторов я знаю только 2 - Fest и TSN.

Убедительная просьба: не разводить holy war на тему - "Синтаксис XML - говно, юзай Dust".

Gozar 22.04.2012 02:06

Поставлю 12 бубунут под кедами прикручу туда nodeJS и попробую сие творение ;)

B~Vladi 22.04.2012 02:08

Gozar, не обязательно, сие заводится даже из-под окон :yes:

B~Vladi 22.04.2012 21:14

Довёл до ума документацию: разбил по wiki-страницам, описал остальные моменты. Вникнуть в суть стало теперь намного проще :)

Осталось только написать мануал по API создания тегов.

B~Vladi 23.04.2012 18:20

:blink: Вышла новая версия - 2.0.3.

Что изменилось:
1. Добавлен тег set, для изменения значения ранее созданной переменной (тег var). Работает точно по такому же принципу, как и тег var.
2. Добавлен атрибут item в теги for и each, что бы не приходилось явно сохранять текущий элемент массива/значение текущего свойства в переменную.

B~Vladi 26.04.2012 18:46

Я понимаю, что всем похуй, но всё же...
:blink: Вышла новая версия - 2.1.0

Что изменилось:
1. Выпилил тег set за ненадобностью ;)
2. Теперь переменные из внешнего шаблона не доступны в подключаемых, дабы не прострелить себе ногу.
3. Добавлен метод extend, для расширения набора тегов из кода.
4. Пойманы и наказаны несколько багов.
5. Расширилось API тегов.

Приведу пример шаблона:
page.xml:
<tsn:root xmlns:tsn="TSN">
        <!-- Создаем скрипт для хедера -->
        <!-- Сначала сгенерируем конечный результат и сохраним в переменную -->
        <tsn:var name="header">
            <script type="text/javascript">
                //<![CDATA[
                console.log();
                //
                ]]>
            </script>
        </tsn:var>

        <!-- А при вызове шаблона просто будем возвращать результат -->
        <tsn:template name="header">
            <tsn:echo text="_var.header" />
        </tsn:template>

        <!-- Контент страницы -->
        <tsn:template name="body">
            <div>Body</div>
        </tsn:template>

        <!-- Подключаем базовый шаблон -->
        <tsn:include src="base.xml" context="this.globalData" />
    </tsn:root>



Файл с базовой разметкой base.xml:
<html xmlns="http://www.w3.org/1999/xhtml">
        <head>
            <link href="base.css" type="text/css" rel="stylesheet"/>
            <!-- Здесь будет выводиться стили для конкретной страницы -->
            <tsn:include name="header" />
        </head>
        <body>
            <!-- Вставляем навигацию с передачей необходимых данных -->
            <tsn:include src="common/navigation.xml" context="this.navigation" />
            <!-- Вставляем контент, который был определён ранее и унаследован здесь -->
            <tsn:include name="body" />
            <!-- Вставляем футер с передачей необходимых данных -->
            <tsn:include src="footer.xml" context="this.footer" />
        </body>
    </html>


Вроде всё логично и понятно, как думаете?

Gozar 26.04.2012 20:50

Цитата:

Сообщение от B~Vladi (Сообщение 171440)
Я понимаю, что всем похуй, но всё же...

Мне интересно, но сейчас не до этого :)

B~Vladi 26.04.2012 22:36

Цитата:

Сообщение от Gozar
Мне интересно, но сейчас не до этого

Я так понял тут нодой никто не балуется. Щас запилю ещё один кейс в новой версии и на хабр тогда напишу. Тут как-то всё тихо. vflash тоже молчит, наверно обидно что их шаблонизатор медленнее.

B~Vladi 27.04.2012 11:23

Цитата:

Сообщение от B~Vladi
запилю ещё один кейс

Как и обещал:

:blink: Вышла новая версия - 2.1.1

Что изменилось:
1. Теперь в теге include абсолютные пути в атрибуте src начинаются с TSN.config.templateRoot. Относительные пути начинаются с папки текущего шаблона или от TSN.config.templateRoot, если текущий шаблон компилируется методом TSN.compile.
2. Пофикшен мелкий баг.

Более серьёзных правок не предполагается, все основные моменты, которые хотел реализовать - реализовал. Если пользователи будут предлагать достойные изменения - буду впиливать.

vflash 27.04.2012 14:11

Цитата:

Сообщение от B~Vladi
vflash тоже молчит, наверно обидно что их шаблонизатор медленнее.

яж писал что к ним отношение не имею. да и TSN не шибко быстрее, у них try catch и выход идет в поток, а у TSN в переменную. Да и что у них что у TSN на выходе XHTML а не HTML. Контрольный в голову, TSN парсит XML-шаблон регулярками.

И между прочем я тебе помогал с тестированием )

vflash 27.04.2012 14:22

ну и добавлю если сравнивать с шаблонизаторами под другие платформы то для шаблонизаторов на nodejs нужно учитывать потери на передачу данных. Как минимум это: формирование json, сетевые издержки и парсинг json в nodejs.

B~Vladi 27.04.2012 14:54

Цитата:

Сообщение от vflash
выход идет в поток

Там 2 варианта, можно и не в поток. На счет потока да, стоит прикрутить.
Цитата:

Сообщение от vflash
TSN не шибко быстрее

5000 fps разница - это не шибко?
Цитата:

Сообщение от vflash
Да и что у них что у TSN на выходе XHTML а не HTML.

Как раз нет. В TSN можно хоть JSON генерировать.
Цитата:

Сообщение от vflash
TSN парсит XML-шаблон регулярками.

И что? Главное - правильно парсит. И парсит только свои теги, всё остальное не трогает.

Цитата:

Сообщение от vflash
И между прочем я тебе помогал с тестированием )

Да, спасибо тебе. :)

B~Vladi 27.04.2012 16:42

:blink: Вышла новая версия - 2.2.1

Что изменилось:
1. Добавлена возможность записи в поток! Спасибо vflash за наводку, как-то я забыл про это совсем. Для этого нужно просто передать объект Stream вторым параметром при рендеринге:
template.call(data, response);

При этом функция по прежнему возвращает результат рендеринга.

Если ещё что смущает, не нравится или не хватает - пишите :write:

kobezzza 03.05.2012 08:26

Через недельку наверно понадобится твой шаблонизатор, так что если что, буду терраризировать вопросами на работе:)

B~Vladi 03.05.2012 10:37

Цитата:

Сообщение от kobezzza
если что, буду терраризировать вопросами на работе

Без проблем :)

B~Vladi 03.05.2012 16:04

Мне monolithed подкинул одну мысль: сделать компиляцию файлов пачкой.

Хочу спросить у сообщества - как вы это видите?
Пока есть такие варианты:

1. Выделить отдельный метод, в который будет передаваться массив/хеш шаблонов.
2. Реализовать рекурсивный обход директории и компилировать в ней все (?) файлы
3. Не делать.

melky 03.05.2012 18:13

B~Vladi, Вы же знаете, что рекурсия - это плохо, особенно когда нужна скорость. Я за второй метод, но его вариацию без рекурсии.

B~Vladi 03.05.2012 18:27

Цитата:

Сообщение от melky
рекурсия - это плохо

Ну, рекурсия может быть заменена на while, как реализуешь. С другой стороны, предполагается что компиляция будет происходить при инициализации всего приложения, так что скорость тут роли не играет.

Важно, что бы было удобно использовать.

melky 03.05.2012 18:32

какая разница в использовании от метода сбора файлов? на выходе же всё равно один файл получится :)
хм, может, через Makefile ? и заодно там же поуказывать настройки сборки.

B~Vladi 04.05.2012 10:54

Цитата:

Сообщение от melky
на выходе же всё равно один файл получится

Ты не понял.
Не один. При компиляции никаких файлов не создается. Шаблон из файла компилируется в JS-функцию, которую потом можно получить из кеша TSN.cache.

B~Vladi 04.05.2012 15:03

Обновилась версия - 2.3.0
Основное изменение - добавлен тег else с атрибутом if ;)

B~Vladi 11.05.2012 18:23

Добавил модуль в npm и в список модулей.

B~Vladi 13.06.2012 20:35

Перед выходом новой версии хочу провести небольшой эксперимент и попросить вашего участия в нем :)
Если вы потратите немного своего времени, я буду вам очень признателен :)

Есть некие файлы шаблонов:

page.xml:
<?xml version="1.0" encoding="UTF-8"?>
<tsn:root xmlns:tsn="TSN" xmlns="http://www.w3.org/1999/xhtml">

	<tsn:data key="JSBlock">
		<script type="text/javascript">
			//<![CDATA[
			alert('Page.');
			//]]>
		</script>
	</tsn:data>

	<tsn:render file="service.xml" />
</tsn:root>


service.xml:
<?xml version="1.0" encoding="UTF-8"?>
<tsn:root xmlns:tsn="TSN" xmlns="http://www.w3.org/1999/xhtml">
		<tsn:data key="JSBlock" action="prepend">
			<script type="text/javascript" src="&TSN.this.serviceName;.js"> </script>
		</tsn:data>

		<tsn:block name="JS" type="global">
			<tsn:echo data="_data.JSBlock" />
		</tsn:block>

		<tsn:block name="CSS" type="global">
			<link rel="stylesheet" type="text/css" href="service.css" />

			<tsn:if expr="_data.CSSBlock">
				<tsn:echo data="_data.CSSBlock" />
			</tsn:if>
		</tsn:block>

		<tsn:block name="content" type="default">
			Page not found
		</tsn:block>

		<tsn:render file="base.xml" />
</tsn:root>


base.xml:
<?xml version="1.0" encoding="UTF-8"?>
<tsn:root xmlns:tsn="TSN" xmlns="http://www.w3.org/1999/xhtml">
		<tsn:render file="common.xml" />

		<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
		<html>
			<head>
				<tsn:render block="CSS" />
			</head>
			<body>
				<tsn:render block="content" />

				<tsn:render block="JS" />
			</body>
		</html>
</tsn:root>


common.xml:
<?xml version="1.0" encoding="UTF-8"?>
<tsn:root xmlns:tsn="TSN" xmlns="http://www.w3.org/1999/xhtml">
		<tsn:data key="JSBlock" action="prepend">
			<script type="text/javascript" src="common.js" />
		</tsn:data>

		<tsn:block name="content" type="default">
			<tsn:render file="/404.xml" config="({
														cache: false
													})" />
		</tsn:block>
</tsn:root>


Вопрос: глядя на код шаблонов, можно ли, потратив несколько минут, вникнуть в суть происходящего. Прикинуть, что будет на выходе. Без документации.
Дополнительный вопрос: какие моменты показались вам нелогичными/непонятными.

melky 13.06.2012 22:02

Я понял более-менее, но не понял, зачем нужен элемент data, почему у него такое же значение атрибута key, как и name и элемента-блока, и что означает у него значение атрибута action - prepend.

B~Vladi 13.06.2012 22:55

Цитата:

Сообщение от melky
зачем нужен элемент data

Сохраняет данные в хранилище с ключом key. Тут дока по старой версии.

Цитата:

Сообщение от melky
почему у него такое же значение атрибута key, как и name и элемента-блока

Это просто случайность, блоки и данные не связаны.

Цитата:

Сообщение от melky
что означает у него значение атрибута action - prepend.

Действие, которое нужно сделать с новыми данными, если по такому ключу уже есть какое-то значение в хранилище (доступно как __data в JS-выражениях). Варианты: append, prepend, replace (по-умолчанию). Так понятно?

B~Vladi 14.06.2012 13:31

Немного подправил пример, что бы не возникало лишних недопониманий.

B~Vladi 17.06.2012 14:27

Вот более полный пример: https://github.com/B-Vladi/TSN/tree/2.4.0/example

B~Vladi 21.06.2012 11:28

Для тех, кому интересно, предлагаю обсудить API.

Обновленный JSDoc в репозитории: https://github.com/B-Vladi/TSN/tree/2.4.0/jsdoc
Для удобства выложил сюда: http://tsn.amxhost.ru

B~Vladi 21.06.2012 12:55

UPD: небольшой upgrade.

B~Vladi 22.06.2012 00:50

Мне тут подкинули идею и я реализовал теги header и status. Хотя раньше это можно было сделать и в JS-выражениях.

Как пиздато самим с собой разговаривать

devote 22.06.2012 13:22

Цитата:

Сообщение от B~Vladi
Как пиздато самим с собой разговаривать

и не говори, главное тебя слышат :D

А вообще я наблюдаю за развитием твоего проекта, пока не придумал конечно куда это внедрить. Но мысли некоторые навивают.

B~Vladi 22.06.2012 13:39

Цитата:

Сообщение от devote
А вообще я наблюдаю за развитием твоего проекта

:thanks:
Из последнего: закончил реализацию тега script, буквально пару часов назад. Пара примеров:
<tsn:script>
<[CDATA[
  // Код выполняется в глобальной области видимости шаблона.
  // Созданные переменные будут доступны в других JS-выражениях и тегах script.
  var data = {
    key: 'value'
  };
]]>
</tsn:script>
<tsn:echo data="data.key" />

Выведет: "value".

Или так:
<tsn:script type="local">
<[CDATA[
  // Код выполняется в локальной области видимости анониймной функции.
  // Тег этого типа может выводить текстовые данные в результат через return:
  return 'Text';
]]>
</tsn:script>

На выходе: "Text".

Ещё есть атрибут context в теге типа local, работает так же как и в других тегах.

Вообще раньше я был против подобных возможностей в шаблоне, но потом понял, что раз шаблон реализует view-логику, то такой тег необходим для написания костылей или упрощения преобразования данных. Как вариант, в нем можно создавать функции-модификаторы, что бы потом использовать в JS-выражениях в других шаблонах.

Gozar 22.06.2012 21:33

Цитата:

Сообщение от B~Vladi (Сообщение 183189)
Как пиздато самим с собой разговаривать

Я успеваю только иногда просматривать тему. Я даже бы взял шаблонизатор и прикрутил его, но останется только заставить всех хостеров поддерживать node.js.

B~Vladi 22.06.2012 23:06

Цитата:

Сообщение от Gozar
но останется только заставить всех хостеров поддерживать node.js.

Вопрос времени. Да и хостеров хватает. Немного погодя будет версия, не привязанная к NodeJS, нужен будет только интерпретатор JS. На работе собираемся внедрить. Вы бы писали что требуется, я же не в курсе всего...

B~Vladi 24.06.2012 14:11

В примере есть одно некрасивое решение с переопределением блока CONTENT. Сейчас, что бы обернуть существующий блок в разметку и заменить его, нужно изъебываться с сохранением в хранилище данных результат рендеринга блока. Для такой ситуации я реализовал новый тип блока wrapper:
<tsn:block name="CONTENT" type="wrapper">
	<div class="service">
		<tsn:render block="CONTENT" />
	</div>
</tsn:block>


Этот блок сначала сохраняет глобальный блок с именем name как локальный, а затем заменяет глобальный блок собой. Это позволяет вызывать оригинальный блок CONTENT внутри блока типа wrapper, потому как локальные блоки имеют больший приоритет при использовании тега render.
Если смотреть на код шаблона, не должно возникать двусмысленностей с именами блоков, если знать про тип wrapper.

Как вам такое решение? Пока не пушил на гитхаб.

devote 02.08.2012 20:18

B~Vladi,
вот сижу выбираю шаблонизатор какой нить, но нужен что бы работал c PHP а не NodeJS, есть идеи? Твой реально на PHP переписать? И как ты на это смотришь?

B~Vladi 02.08.2012 22:01

Цитата:

Сообщение от devote
нужен что бы работал c PHP

Пэхапэ отстой, юзай ноду :D

Цитата:

Сообщение от devote
есть идеи?

PHP?! smarty?

Цитата:

Сообщение от devote
Твой реально на PHP переписать?

Конечно реально, там нет ничего магического. Особенно если найдется подходящий SAX-парсер.

Цитата:

Сообщение от devote
И как ты на это смотришь?

Вообще я не против, даже буду всячески способствовать, но сам писать не буду, ибо см. первый ответ.

devote 02.08.2012 22:07

Цитата:

Сообщение от B~Vladi
Пэхапэ отстой, юзай ноду

я бы с удовольствием, но увы не везде есть нода.

Цитата:

Сообщение от B~Vladi
Вообще я не против, даже буду всячески способствовать, но сам писать не буду, ибо см. первый ответ.

ясно.. Тогда сам перепишу его на пыху. Отпишусь сюда.

B~Vladi 02.08.2012 22:34

Цитата:

Сообщение от devote
Тогда сам перепишу его на пыху.

Он будет компилировать шаблон в PHP-код?

devote 02.08.2012 22:42

Цитата:

Сообщение от B~Vladi
Он будет компилировать шаблон в PHP-код?

зачем, в JS конечно же.. или не? или у тебя во что-то другое компилит?


Часовой пояс GMT +3, время: 10:24.