Веб-разработка с ChicagoBoss framework

в 21:23, , рубрики: erlang, Erlang/OTP, mongodb, open source, Веб-разработка, метки: , , ,

Chicago Boss
«Chicago Boss — бескомпромиссный веб-фреймворк, любимый дизайнерами, инженерами и этим парнем с пейджером.» — © chicagoboss.org

«Erlang — язык для создания отказоустойчивых распределенных приложений. Это — один из немногих функциональных языков, который давно и успешно применяется на практике. Например — в телекомах (Ericsson AXD-301 switch), банковских системах, системах автоматизации, высоконагруженных веб-приложениях (Facebook Chat). При этом Erlang — продукт с открытым исходным кодом и распространяется бесплатно.» — говорит нам сайт русскоязычного сообщества Erlang.

Данный пост предназначен для расширения аудитории Erlang программистов. Если до этого вы считали что функциональное программирование не может быть использовано для бытовых целей, то пора расширить ваш кругозор. Этот пост писался при поддержке моего товарища — Ruby/C++ программиста с многолетним стажем, лично знакомого с создателем данного фреймворка (Эван Миллер) и последнее время фанатеющего от данного языка.

По заверению обоих разработка веб-приложений на базе этого фреймворка ничуть не медленнее разработки на тех же рельсах (Ruby on Rails). А в чем-то даже быстрее. Я с функциональным программированием до этого особо знаком не был, но под давлением товарища решил попробовать. Хочу сказать, что опыт был весьма удачным и у меня получилось создать мой первый веб-сайт быстрее, чем год назад с тем же Ruby on Rails.


Веб разработка с ChicagoBoss framework

Предыстория:
Дело было в октябре 2010 года на очередном Erlang семинаре, Чикаго, Иллинойс. Под конец лекции произошло следующее (со слов друга): I heard his voice across the room, saying, «I am working on a Rails for Erlang» and 99 people went, «Yeah, right!» (перевод: Я услышал его голос через всю аудитории: «Я работаю над Рельсами для Erlang» и 99 человек сказало «Ага, конечно!»). Звали этого парня Evan Miller. После семинара мой друг расспрашивал его о концепциях и идеях, вложенных в «функциональные рельсы». Идеи ему очень понравились, и с тех пор он все свободное время проводит над проектами с этим фреймворком, а также помогает с документацией Эвану. Он даже организовывал лекции по этому делу и внедрил некоторые вещи у себя в компании.

  1. Вступление
  2. Установка
  3. Настройка рабочей среды (по желанию)
  4. Создание первого проекта
  5. Работа с БД
  6. CRUD
  7. Дополнение
  8. Ресурсы

Вступление

В этой статье мы сделаем веб сайт, который будет представлять собой адресную книгу (addressbook) с возможностью добавления/удаления/просмотра/редактирования записей. Т.е. реализуем стандартный CRUD.

Для интересующихся: вся разработка отняла примерно 4-5 часов, включая установку, настройку инструментов и изучение.

Для достижения поставленной задачи были использованы следующие инструменты:
Ubuntu 11.10 32bit, Erlang R15B, ChicagoBoss MVC фреймворк, документно-ориентированная база данных MongoDB, Gedit с пакетом плагинов Gmate от iSkin (Gedit — это просто текстовый редактор).

Установка для Mac не сложнее установки для Ubuntu. Для Windows есть отдельная инструкция, но и она от приведенной здесь не особо отличается. Может быть найдутся добрые люди, которые поделятся своим опытом установки для Windows/Mac в комментариях.

По каждому из инструментов можно написать отдельную большую статью, да еще и не одну. Но чтобы не пугать читателя страшными словами и тоннами материала, скажу, что на самом деле для быстрого входа достаточно будет одной этой статьи.
Я лично никогда до этого не имел дела ни с функциональными языками, ни с документно-ориентированными базами данных, но это мне не помешало создать свое первое приложение с ними.

Все ссылки на полезные ресурсы, на исходники и на готовый веб сайт в разделе Ресурсы.
Стоит упомянуть, что сайт находится на моем личном ноуте (Dual-Core CPU T4300 @ 2.10GHz, 2GB). Так что пожалуйста без фанатизма. О падениях (если такие будут) буду сообщать. Предвидя ваш вопрос: запустил я сайт локально, т.к. хотел проверить, при какой нагрузке рухнет сервер. Ну и дешевле получается, чтоб лишний хостинг не покупать. Может кто проявит желание и поделится нормальным сервером, разместим сайт там.
Если сайт не открывается, значит я химичу с ноутом, попробуйте открыть через пару минут.
Если какой-то из моментов не понятен, пишите в комментариях, буду править статью.

Установка

Erlang

Для начала нужно установить Erlang последней версии. Идем на этот веб сайт и следуем инструкции для вашей операционной системы. Там же можно скачать нужный дистрибутив или посмотреть необходимый репо.
У меня Убунту 11.10 32 битная. Для нее дистрибутива нет, только для 64х битной. Поэтому я просто скачал дистрибутив для Убунту 11.04 32 бит. Некоторые фишки в ней не работают, как например графический просмотр существующих потоков и процессов (утилита appmon).

Веб разработка с ChicagoBoss framework

Но обучению, и даже созданию нормальных проектов, это не мешает.
Ну и конечно всегда можно собрать нужную версию для своей версии ОС из исходников :) Мой товарищ свои версии уже раз 5 собирал для разных машин: Ubuntu и Mac. Говорит, что конечно геморойно, но вполне возможно за один вечер осилить.

Еще с проблемами можно обращаться в Erlang-Solutions. Говорят, ребята отзывчивые.

После скачивания файла и при запуске установки мне выдало следующее:
installing esl-erlang 1:15.b
size - 63.8 mb

После установки для проверки приложения открываем консоль и пишем команду erl. Которая выдает примерно следующее:
Erlang R15B (erts-5.9) [source] [smp:2:2] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9

ChicagoBoss

Скачать framework можно и без Git, просто зайти на страницу разработки и стянуть последнюю версию.

Устанавливаем Git, если его у вас еще нет:
apt-get install git-core
получаем копию ChicagoBoss framework:
git clone git://github.com/evanmiller/ChicagoBoss.git

Заходим в дирректорию вашего репо:
cd ChicagoBoss
Компилируем:
./rebar get-deps clean compile

Чтобы завести новый проект (нужно находится в дирректории нашего фреймворка):
make app PROJECT=your_project_name_here
Затем:
cd ../your_project_name_here

Для запуска дебаг (developer) режима:
./init-dev.sh
Для запуска в продакшн:
./init.sh start

Дождитесь, пока сообщения перестанут двигаться. Если отчетов об ошибках или крешах нет, то можно открывать localhost:8001
Теперь можно создавать новые модели, контроллеры, вьюшки и сразу смотреть результат без перекомпиляции и перезапуска сервера. В некоторых случаях перекомпиляция все-таки нужна, например когда меняете маршруты (routes).
Компиляция вызывается командой:
./rebar compile

ChicagoBoss по умолчанию использует веб-сервер на Misultin и при создании приложения в конфиге прописывается порт 8001. При желании сервер можно заменить на Mochiweb, а возможно и на Cowboy.

MongoDB

Тут все очень просто. Качаем и устанавливаем. В Убунту вобще только одна команда:
apt-get install mongodb

Для проверки работоспособности можно ввести одну из команд в консоль:
mongo
или
ps aux | grep mongo
А также можно попробовать постучаться по адресу localhost:28017. Родной порт, по которому приложения обычно работают с Монго — 27017. Для браузера по умолчанию используется 28017.

Для управления MongoDB сервером используются следующие команды:
$ sudo status mongodb
$ sudo stop mongodb
$ sudo start mongodb
$ sudo restart mongodb

После некорректного выключения сервера, например, при выключении/перезагрузке машины, при старте сервера БД он все равно остается выключенным. Сделано это специально созданием lock файла. Чтобы запустить сервер снова нужно сделать следующее:
1. Удалить lock файл
2. Запустить скрипт восстановления

$ sudo rm /var/lib/mongodb/mongod.lock
$ sudo -u mongodb mongod -f /etc/mongodb.conf --repair

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

Настройка рабочей среды (по желанию)

Я скачал пакет плагинов Gmate. Из них реально использую пока только Zen Coding. Отличное пособие по использованию этого плагина представлено в этой видео лекции. Реально упрощает жизнь.
Один мой друг сказал что это HAML для бедных, но меня использование этой прослойки пока «не вставляет».
И еще использую сниппеты (Snippets). Этот плагин поставляется с Gedit, но по умолчанию он выключен. Для включения идем Edit -> Preferences -> Plugins -> Snippets.
Он прост в использовании, просто открываете Tools -> Manage Snippets -> «your_language» и редактируете существующие или добавляете свои сниппеты. Инструкция по работе с ними находится здесь. У меня на их разбор ушло минуты 3 и потом еще минут 15 на создание своих собственных. Зато как удобно потом написать что-то типа div, нажать на Tab и получить следующее:

<div>

</div>

А при желании можно еще текста добавить, например дополнительных полей id, style и т.д. А также поставить значения по умолчанию и точки, куда курсор будет прыгать при следующем нажатии Tab.
Я работал со сниппетами в Visual Studio, ИМХО в Gedit с ними работать гораздо удобнее.

И да, подобные плагины есть для Emacs, Notepad++ и еще для кучи других редакторов.

Создание первого проекта

Все поставлено, все работает. Можно приступать к созданию своего веб-сайта. Заходим в дирректорию с ChicagoBoss и пишем команду:
make app PROJECT=addressbook

Фреймворк создает новую директорию, по-умолчанию в домашнем каталоге. Весь проект находится внутри, если возникнет желание переместить/передать кому-то/удалить проект, достаточно будет манипуляций с этим каталогом. Переходим во вновь созданную директорию нашего проекта
cd ../addressbook

Весь наш код/ресурсы лежат в каталоге src. Общая структура:
Веб разработка с ChicagoBoss framework

Структура каталога src:
Веб разработка с ChicagoBoss framework
Файлы ресурсов (javascript, css, медиафайлы) лежат в /priv/static/. Подключение файла стилей будет выглядеть так:

<link rel="stylesheet" href="/static/css/style.css" type="text/css" charset="utf-8" />

Редактируем boss.config, добавляем данные о БД:
{db_host, «localhost»},
{db_port, 27017},
{db_adapter, mongodb},
{db_database, addressbook},
{db_write_mode, safe},
{db_read_mode, master},

Вот так выглядит мой конфиг (и вобще окружающая среда):
Веб разработка с ChicagoBoss framework

Я в первый раз слово addressbook неправильно написал, пришлось потом приложение пересоздавать с правильным именем, потому что к имени привязано многое.

Далее создаем файл address.erl в src/model/. Он нужен для доступа к БД. У меня он выглядит так:

-module(address, [Id, Firstname, Lastname, Address1, Address2, City, State, Country, Active, CreationTime, ModifictionTime ]).
-compile([export_all]).

Cоздадим контроллер с именем addressbook_main_controller.erl (appname_controllername_controller) и напишем внутри такой код:

-module(addressbook_main_controller, [Req, SessionID]).
-compile([export_all]).

index('GET', [])->{ok, [{data, "Hello World"}]}.

index — это наша функция,
все что находится после "->" является телом функции.
Эта функция будет реагировать на запрос GET и при удачной обработке вернет нашу строку в переменной data.
ok — означает что все прошло хорошо. Для обработки ошибки используется слово error. Выглядеть это может так:

index('GET', [])->
	{ok, [{data, "Hello World"}]};
	{error, Reason}->
			Reason.

Для того чтобы отобразить вернувшееся значение на странице, в нашей вьюшке нужно будет добавить тег {{data}}. Для проверки создадим наш первый html файл в каталоге /src/view/main/. Назовем его index.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>My addressbook</title>
  </head>
  <body>
    <form>
      <div id="main">
	  {{data}}
      </div>
    </form>
  </body>
</html>

Последний штрих заключается в настройке маршрутов. Открываем файл /priv/addressbook.routes и дописываем наш новый маршрут (по умолчанию у нас еще нет ни одного маршрута, все строки закомментированы):

{"/", [{controller, "main"}, {action, "index"}]}.

Компилируем наше приложение ./rebar compile
И запускаем ./init-dev.sh

Если теперь перейти в браузер и открыть localhost:8001, можно будет увидеть наш Hello World.
Все последующие изменения, за исключением изменения маршрутов, будут применены без перекомпиляции и перезапуска сервера, т.е. на лету.

Про маршруты: можно жестко указать, при каком адресе какой контроллер и метод вызывать. Либо можно в адресе задавать полный путь вида /[controller name]/[action name]. Т.е. в нашем случае у нас есть контроллер с именем main. Если мы добавим в наш контроллер новый метод с именем create, то вызвать его можно будет, обратившись по адресу localhost:8001/main/create. Либо указав следующий маршрут:

{"/create", [{controller, "main"}, {action, "create"}]}.

И тогда вызов будет выглядеть как localhost:8001/create

Работа с БД

Подключение к нашей БД уже состоялось. Вы даже можете выполнять запросы в консоли во время работы веб-сервера. Для этого достаточно перейти в консоль, где мы выполнили команду запуска приложения ./init-dev.sh, нажать, например, Ввод и в новой открытой строчке ввести, например это:

boss_db:find(address, []).

Данная команда выводит все записи, привязанные к модели address. Сейчас там нет ни одной записи (или есть пустая по-умолчанию).
Вы можете ввести boss_db: нажать Tab и посмотреть доступные команды.

[] — означает «параметры». Если внутри скобок пусто, значит вы передаете 0 параметров. В случае с командой find можно получить список записей, у которых firstname=«ivan»:

boss_db:find(address, [{firstname, "ivan"}]).

В консоли можно выполнять множество полезных команд, и не только с БД.

CRUD

И так, наша модель готова к работе, теперь в контроллере можно добавлять новые функции. Добавим функцию create, которая будет добавлять новые записи:

create('GET', [])->ok;
create('POST', [])->
	Firstname = Req:post_param("firstname"), 
	Lastname = Req:post_param("lastname"), 
	Address1 = Req:post_param("address1"), 
	Address2 = Req:post_param("address2"), 
	City = Req:post_param("city"), 
	State = Req:post_param("state"), 
	Country = Req:post_param("country"), 
	Active = Req:post_param("active"), 
	CreationTime = erlang:now(), 
	ModificationTime = erlang:now(),
	NewAddress = address:new(id, Firstname, Lastname, Address1, Address2, City, State, Country, Active, CreationTime, ModificationTime),
	case NewAddress:save() of
		{ok, SavedAddress}->
			{redirect, [{action, "index"}]};
		{error, Reason}->
			Reason
	end.

Я думаю прочитать код для программиста не составляет труда, поэтому объяснять я тут ничего не буду. Единственное что могу сказать, что id создается автоматом.
id — зарезервированное слово.
Id — так можно назвать вашу переменную.
Да, язык регистрозависимый.

Теперь создадим html файл для этого метода. Назовем его create.html:

<!DOCTYPE html>
<html>
   <head>
      <title>My addressbook</title>
   </head>
   <body>
   <form method="post">
     <header></header>
     <div id="main">
       <table>
         <tr><td>Frist name</td>
           <td><input type="text" id="firstname" name="firstname" size="45" maxlength="255" value="" /></td></tr>
         <tr><td>Last name</td>
           <td><input type="text" id="lastname" name="lastname" size="45" maxlength="255" value="" /></td></tr>
         <tr><td>Address1</td>
           <td><input type="text" id="address1" name="address1" size="45" maxlength="255" value="" /></td></tr>
         <tr><td>Address2</td>
                <td><input type="text" id="address2" name="address2" size="45" maxlength="255" value="" /></td></tr>
            <tr><td>City</td>
                <td><input type="text" id="city" name="city" size="45" maxlength="255" value="" /></td></tr>
            <tr><td>State</td>
                <td><input type="text" id="state" name="state" size="45" maxlength="255" value="" /></td></tr>
            <tr><td>Country</td>
                <td><input type="text" id="country" name="country" size="45" maxlength="255" value="" /></td></tr>
            <tr><td>Active</td>
                <td><input type="checkbox" id="active" name="active" value="true" /></td></tr>
         </table>

         <button type="submit">Submit</button>
      </div>
   </form>
   </body>
</html>

Тут важно отметить, что для наших контролов, которые передают значения, нужно указывать и id и name параметры. Каждый из них используется разными частями фрэймворка. По-моему Эван это дело хочет пофиксить и оставить только один обязательный параметр. Кстати, обновления выходят довольно часто, чуть ли не каждую неделю, так что не исключено что к моменту публикации этой статьи фикс уже будет выпущен.

Добавим еще одну функцию для просмотра всех записей в нашей базе (точнее заменим написанную ранее функцию index):

index('GET', [])->Addresses = boss_db:find(address, []),
	{ok, [{addresses, Addresses}]}.

Также поменяем код нашей index.html страницы:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>My addressbook</title>
  </head>
  <body>
    <form method="post">
      <header></header>
      <div id="main">
	<table cellspacing="0" cellpadding="0">
		<tr>
			<th>First name</th>
			<th>Last name</th>
			<th>City</th>
			<th>Address1</th>
			<th>Address2</th>
			<th>State</th>
			<th>Country</th>
			<th>Active</th>
		</tr>
		{% if addresses %}
			{% for address in addresses %}
				<tr>
					<td>{{address.firstname}}</td>
					<td>{{address.lastname}}</td>
					<td>{{address.city}}</td>
					<td>{{address.address1}}</td>
					<td>{{address.address2}}</td>
					<td>{{address.state}}</td>
					<td>{{address.country}}</td>
					<td>{{address.active}}</td>
				</tr>
			{% endfor %}
		{% endif %}
	</table>
      </div>
    </form>
  </body>
</html>

ChicagoBoss использует систему шаблонов Django, через библиотеку ErlyDTL. The Django Template language.

Теперь при обращении по адресам /main/ и /main/create вы получите 2 формы: с вводом и выводом информации из БД.

Для удаления (delete) записи будет достаточно следующей функции в нашем контроллере:

delete('GET', [Id])->
	boss_db:delete(Id),
		{redirect, [{action, "index"}]}.

Остается только его вызвать localhost:8001/main/delete/[id]

Для отображения записи (show), добавим в контроллер эту функцию:

show('GET', [Id])->
	Address = boss_db:find(Id),
		{ok, [{address, Address}]}.

Создадим view файл show.html:

<!DOCTYPE html>
<html>
   <head>
      <title>My addressbook</title>
   </head>
   <body>
   <form method="post">
		<div id="main">
			<table cellspacing="0" cellpadding="0">
			{% if address %}
			<tr><td>First name</td><td>{{address.firstname}}</td></tr><tr>
				<td>Last name</td><td>{{address.lastname}}</td></tr><tr>
				<td>City</td><td>{{address.city}}</td></tr><tr>
				<td>Address1</td><td>{{address.address1}}</td></tr><tr>
				<td>Address2</td><td>{{address.address2}}</td></tr><tr>
				<td>State</td><td>{{address.state}}</td></tr><tr>
				<td>Country</td><td>{{address.country}}</td></tr><tr>
				<td>Modification time</td><td>{{address.modification_time}}</td></tr><tr>
				<td>Creation time</td><td>{{address.creation_time}}</td></tr><tr>
				<td>Active</td><td>{{address.active}}</td></tr>
			{% endif %}
			</table>
		 </div>
    </form>
  </body>
</html>

Редактирование записи — это смесь create и show. Добавим еще две функции в наш контроллер:

edit('GET', [Id])->
	Address = boss_db:find(Id),
		{ok, [{address, Address}]};

edit('POST', [Id])->
	Address = boss_db:find(Id),
	NewAddress = Address:set([{firstname, Req:post_param("firstname")}, {lastname, Req:post_param("lastname")}, {address1, Req:post_param("address1")}, {address2, Req:post_param("address2")}, {city, Req:post_param("city")}, {state, Req:post_param("state")}, {country, Req:post_param("country")}, {active, Req:post_param("active")}, {modification_time, erlang:now()}]),
	case NewAddress:save() of
	{ok, SavedAddress}->
		{redirect, [{action, "index"}]};
	{error, Reason}->
		Reason
	end.

Важно отметить, что в Erlang все переменные — неизменны! Поэтому сначала приходится получать Address, а затем измененное состояние этой переменной присваивать NewAddress.
Первая функция делает тоже самое, что и функция show, вторая обновляет информацию в БД после нажатия кнопки Submit в форме.
Добавляем эту самую форму edit.html:

<!DOCTYPE html>
<html>
   <head>
      <title>My addressbook</title>
   </head>
   <body>
   <form method="post">
		<table cellspacing="0" cellpadding="0">
		{% if address %}
			<tr><td>First name</td><td><input type="text" id="firstname" name="firstname" size="45" maxlength="255" value="{{address.firstname}}" /></td></tr><tr>
				<td>Last name</td><td><input type="text" id="lastname" name="lastname" size="45" maxlength="255" value="{{address.lastname}}" /></td></tr><tr>
				<td>City</td><td><input type="text" id="city" name="city" size="45" maxlength="255" value="{{address.city}}" /></td></tr><tr>
				<td>Address1</td><td><input type="text" id="address1" name="address1" size="45" maxlength="255" value="{{address.address1}}" /></td></tr><tr>
				<td>Address2</td><td><input type="text" id="address2" name="address2" size="45" maxlength="255" value="{{address.address2}}" /></td></tr><tr>
				<td>State</td><td><input type="text" id="state" name="state" size="45" maxlength="255" value="{{address.state}}" /></td></tr><tr>
				<td>Country</td><td><input type="text" id="country" name="country" size="45" maxlength="255" value="{{address.country}}" /></td></tr><tr>
				<td>Active</td><td><input type="checkbox" id="active" name="active" value="{{address.active}}" /></td></tr>
		{% endif %}
		</table>
		<button type="submit">Submit</button>
	</form>
  </body>
</html>

Ну вот собственно и все, наш CRUD готов.

Дополнение

Пока писал статью, успел прикрутить логин, регистрацию, список зарегистрированных пользователей, чат, валидацию ввода, а также админку (весь код сайта лежит на github, ссылка в разделе Ресурсы).

Чат без всяких наворотов, исключительно для теста и все еще содержит баги, особенно при работе под IE, поэтому специально для него я запретил логин, иначе он вызывает некоторые проблемы. Чатиться с ним можно, сервер не падает, но не комфортно. У меня задача показать работу Erlang, а не проблемы Ajax в IE решать.

Админку можно установить на любой новый сайт, там работы минут на 5.

А также поигрался с шаблонами. В итоге у меня получился один мастер-шаблон, который выглядит вот так:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link rel="stylesheet" href="/static/css/style.css" type="text/css" charset="utf-8" />
    <title>{% block title %}My amazing site{% endblock %}</title>
    {% block res %} {% endblock %}
</head>

<body>
   <header>
		{% if person %}
			Hello, {{person.name}}
			(<a href="/user/logout">Quick, Log Me Out!</a>)
			<br />
		{% endif %}
		Your IP: {{ip}}
	</header>
	<nav>
		<ul class="nav">
			<li class="nav"><a href="/">Home</a></li>
			<li class="nav"><a href="/create">Create</a></li>
			<li class="nav"><a href="/chat/live/public">Chat</a></li>
			<li class="nav"><a href="/user/login">Login</a></li>
			<li class="nav"><a href="/user/showall">Users</a></li>
		</ul>
	</nav>
	<section>
	  {% block content %}{% endblock %}
	</section>
	<footer>
	   {% block footer %}{% endblock %}
	</footer>
</body>
</html>

И вот так теперь выглядит мой show.html:

{% extends "layout/application.html" %}

{% block title %}My addressbook{% endblock %}
{% block content %}
	<table cellspacing="0" cellpadding="0">
	{% if address %}
		<tr><td>First name</td><td>{{address.firstname}}</td></tr><tr>
			<td>Last name</td><td>{{address.lastname}}</td></tr><tr>
			<td>City</td><td>{{address.city}}</td></tr><tr>
			<td>Address1</td><td>{{address.address1}}</td></tr><tr>
			<td>Address2</td><td>{{address.address2}}</td></tr><tr>
			<td>State</td><td>{{address.state}}</td></tr><tr>
			<td>Country</td><td>{{address.country}}</td></tr><tr>
			<td>Modification time</td><td>{{address.modification_time}}</td></tr><tr>
			<td>Creation time</td><td>{{address.creation_time}}</td></tr><tr>
			<td>Active</td><td>{{address.active}}</td></tr>
	{% endif %}
	</table>
{% endblock %}

Получается один каркас и разный контент.
Подробнее о Django шаблонах можно почитать на официальном сайте, ссылка есть в Ресурсах ниже.

Еще ChicagoBoss имеет в своем арсенале свои собственные шаблоны. Немного ниформации о них можно найти на официальном сайте. Там ничего сложного нет. Просто замена часто используемого кода тегами. Ну и еще некоторые плюшки.

Ресурсы

Ссылка на тестовый сайт
Ссылка на исходники

Официальный веб-сайт ChicagoBoss. На этом же сайте вы найдете API документацию, ссылки на Wiki, обучение работы с фреймворком.
Официальный веб-сайт Erlang
Русское сообщество Erlang
Отличный сайт для изучения Erlang
Erlang solutions. Отсюда можно скачать нужную версию Erlang.

Обучающее видео от Эвана: эпизод 1, эпизод 2, эпизод 3

Различия между Ruby on Rails и ChicagoBoss
Сравнение веб-серверов для ChicagoBoss

The Django Template language. Система шаблонов Django.

ChicagoBoss Google Group

Автор: iSeiryu

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js