<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Блог Конфуция</title>

<link href="http://kung-fu-tzu.ru/" />
<updated>2011-07-20T13:23:19-07:00</updated>
<id>http://kung-fu-tzu.ru/</id>
<author><name>Петр Леонов</name><email>comments@kung-fu-tzu.ru</email></author>

<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/cmsbuilder" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="cmsbuilder" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
	<title>Ручное наследование в яваскрипте</title>
	<link href="http://kung-fu-tzu.ru/posts/2011/04/06/native-javascript-inheritance" />
	<updated>2011-04-06T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2011/04/06/native-javascript-inheritance</id>
	<content type="html">&lt;style&gt;
section { margin: 1.75em 0; }
section h { font-size: 1.17em; font-weight: bold; }
section h a { display: none; margin-left: 1em; border-bottom: 1px dashed; cursor: pointer; }
section h:hover a { display: inline; }
&lt;/style&gt;

&lt;section&gt;
&lt;p&gt;Наследование без фреймворков вызывает приступ паники даже у матерых яваскриптеров. Особенно, если надо не просто добавлять новые методы, а модифицировать поведение родительских. А между тем, процедура очень проста.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="simple"&gt;
&lt;h&gt;Просто класс &lt;a href="#simple"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}

var a = new A()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Да, как не удивительно, класс — это просто функция.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="untitled"&gt;
&lt;h&gt;Безымянный класс &lt;a href="#untitled"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
var o = new function () {}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Классом может быть любая функция. Даже безымянное замыкание право имеет.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="method"&gt;
&lt;h&gt;Класс с методом &lt;a href="#method"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}
A.prototype =
{
	method: function () {}
}

var a = new A()
a.method()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Методы добавляются с помощью прототипов. Про прототипы мы &lt;a href="http://www.google.com/search?q=javascript+prototype+inheritance"&gt;всё уже знаем&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="static"&gt;
&lt;h&gt;«Статический» метод &lt;a href="#static"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}
A.staticMethod = function () {}
A.staticMethod()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;В экземплярах &lt;code&gt;staticMethod&lt;/code&gt; доступен не будет.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="inheritance"&gt;
&lt;h&gt;Наследование в вакууме &lt;a href="#inheritance"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}


function B () {}
B.prototype = new A()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Да, именно &lt;code&gt;prototype = new A()&lt;/code&gt;, а не просто &lt;code&gt;prototype = A()&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="useful-inheritance"&gt;
&lt;h&gt;Небесполезное наследование &lt;a href="#useful-inheritance"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}
A.prototype =
{
	methodA: function () {}
}


function B () {}
B.prototype = new A()

var b = new B()
b.methodA()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Получили в экземпляре класса &lt;code&gt;B&lt;/code&gt; полезный &lt;code&gt;methodA()&lt;/code&gt; от класса &lt;code&gt;A&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="add-method"&gt;
&lt;h&gt;Добавление метода &lt;a href="#add-method"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}
A.prototype =
{
	methodA: function () {}
}


function B () {}
B.prototype = new A()
B.prototype.methodB = function () {}

var b = new B()
b.methodA()
b.methodB()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Теперь в экземпляре класса &lt;code&gt;B&lt;/code&gt; есть оба полезных метода.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="bunch-of-methods"&gt;
&lt;h&gt;Добавление кучи методов &lt;a href="#bunch-of-methods"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}


function B () {}
B.prototype = new A()

var methodsB =
{
	methodB1: function () {},
	methodB2: function () {},
	methodB3: function () {}
}

Object.extend(B.prototype, methodsB)

var b = new B()
b.methodB1()
b.methodB2()
b.methodB3()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;&lt;a href="http://www.google.com/search?q=Object.extend+JavaScript"&gt;Object.extend&lt;/a&gt;(dst, src)&lt;/code&gt; просто копирует свойства из &lt;code&gt;src&lt;/code&gt; в &lt;code&gt;dst&lt;/code&gt;. Если договориться с санитарами, можно заменить &lt;code&gt;Object.extend()&lt;/code&gt; на цикл &lt;code&gt;for in&lt;/code&gt; ;)&lt;/p&gt;
&lt;/section&gt;


&lt;section id="replace-method"&gt;
&lt;h&gt;Замена метода &lt;a href="#replace-method"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}
A.prototype =
{
	methodA: function () {}
}


function B () {}
B.prototype = new A()
B.prototype.methodA = function () { alert(42) }

var b = new B()
b.methodA()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Вылетит алерт.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="constructor"&gt;
&lt;h&gt;Конструктивное наследование &lt;a href="#constructor"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A ()
{
	this.array = []
}


function B ()
{
	A.apply(this)
}
B.prototype = new A()

var b1 = new B()
var b2 = new B()
console.log(b1.array == b2.array)
//&gt;&gt;&gt; false
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Если бы не выполнили &lt;code&gt;A.apply(this)&lt;/code&gt;, то &lt;code&gt;array&lt;/code&gt; был бы один на все экземпляры &lt;code&gt;B&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="simple-supermethod"&gt;
&lt;h&gt;Суперметод попроще &lt;a href="#simple-supermethod"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}
A.prototype =
{
	methodA: function () {}
}


function B () {}
B.prototype = new A()

B.prototype.superMethodA = B.prototype.methodA
B.prototype.methodA = function ()
{
	this.superMethodA()
}

var b = new B()
b.methodA()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Прекрасно работает в простых случаях.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="complex-supermethod"&gt;
&lt;h&gt;Суперметод посложнее &lt;a href="#complex-supermethod"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}
A.prototype =
{
	methodA: function () {}
}


function B () {}
B.prototype = new A()

B.prototype.methodA = function ()
{
	A.prototype.methodA.apply(this)
}


function C () {}
C.prototype = new B()

C.prototype.methodA = function ()
{
	B.prototype.methodA.apply(this)
}

var c = new C()
c.methodA()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Прекрасно работает во всех случаях.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="faster-supermethod"&gt;
&lt;h&gt;Суперметод пошустрее &lt;a href="#faster-supermethod"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}
A.prototype =
{
	methodA: function () {}
}


function B () {}
B.prototype = new A()

var superMethodA = A.prototype.methodA
B.prototype.methodA = function ()
{
	superMethodA.apply(this)
}

var b = new B()
b.methodA()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Работает пошустрее, но сломается, если кто-то изменит &lt;code&gt;A.prototype&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;


&lt;section id="supermethod-example"&gt;
&lt;h&gt;Полезный суперметод &lt;a href="#supermethod-example"&gt;#&lt;/a&gt;&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
function A () {}
A.prototype =
{
	setPosition: function (x)
	{
		this.x = x
	}
}


function B () {}
B.prototype = new A()

B.prototype.setPosition = function (x, y)
{
	A.prototype.setPosition.call(this, x)
	this.y = y
}


function C () {}
C.prototype = new B()

C.prototype.setPosition = function (x, y, z)
{
	B.prototype.setPosition.call(this, x, y)
	this.z = z
}

var c = new C()
c.setPosition(1, 2, 3)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;В результате скрытого от нас последовательного вызова &lt;code&gt;A#setPosition()&lt;/code&gt; и &lt;code&gt;B#setPosition()&lt;/code&gt; внутри &lt;code&gt;C#setPosition()&lt;/code&gt;, мы получим объект с данными &lt;code&gt;{x: 1, y: 2, z: 3}&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;


&lt;section&gt;
&lt;h&gt;Ах, да!&lt;/h&gt;
&lt;p&gt;Все кусочки кода, конечно же, обернуты в &lt;code&gt;(function(){ … })()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Тесты &lt;a href="http://kung-fu-tzu.ru/liby/tests/manual-inherit.html"&gt;тут&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/MhJaUWmE9Xw" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Произвольный линукс в контейнере</title>
	<link href="http://kung-fu-tzu.ru/posts/2010/11/28/changing-os-in-virtuozzo" />
	<updated>2010-11-28T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2010/11/28/changing-os-in-virtuozzo</id>
	<content type="html">&lt;p&gt;Началось все с того, что техподдержка мастерхоста отказала в установке операционки на наш выбор. А разрешается на их виртуальные серверы ставить только дебиан 5, федору 7 и центос 5. Пятый дебиан полностью устроил бы, если бы в нем уже был &lt;a href="http://upstart.ubuntu.com/"&gt;апстарт&lt;/a&gt;, а будет апстарт только в шестой версии. И без него никак. А так как операционки на наших старых серверах уже давно устарели (Ubuntu 6.10 последовательно обновленная до 8.04), пришла пора все менять.&lt;/p&gt;

&lt;p&gt;У нас в офисе уже полтора года вертится &lt;a href="/posts/2009/04/28/mac-os-leopard-launchd-vboxheadless/"&gt;несколько OpenVZ-контейнеров&lt;/a&gt;. Для домашних нужд все подходит просто прекрасно. Вдобавок, из экспериментов с OpenVZ удалось вынести несколько полезных наблюдений. Вот они, кстати. Чистый виртуозный контейнер — это всего-лишь небольшая папочка да один конфиг. Готовых контейнеров с разными дистрибутивами линукса &lt;a href="http://wiki.openvz.org/Download/template/precreated"&gt;пруд пруди&lt;/a&gt;. Всей виртуализации в OpenVZ, по большому счету: изоляция файловой системы, разделение памяти и процессора, да айпишников. Ядро одно на всех, тип файловой системы — один, и никакой магии с виртуализацией устройств. А еще свою виртуалку внутри контейнера не запустишь (если только полноценный эмулятор запустить). Итого, с одной стороны — никакой тонкой настройки под железо, а с другой — никакого геморроя с этим железом. Вдобавок, возможен интересный трюк, о котором эта статья.&lt;/p&gt;

&lt;p&gt;Итак. Ось можно элементарно заменить, удалив все файлы старой системы и заменив файлами из новой. Конечно, в процессе надо не уронить уже запущенную систему и суметь потом попасть в новую. Иначе получим кирпич вместо сервера. И, да, прочитайте, пожалуйста, весь пост до конца, чтобы понять, сумеете ли вы потом получить доступ к новой системе, или, хотя бы, восстановить старую.&lt;/p&gt;

&lt;h3&gt;Подготовительный этап&lt;/h3&gt;

&lt;ul&gt;
	&lt;li&gt;делаем полный бекап контейнера (лучше создайте новый и мучайте его);&lt;/li&gt;
	&lt;li&gt;заходим под рутом по SSH;&lt;/li&gt;
	&lt;li&gt;создаем папочку &lt;code&gt;new-system&lt;/code&gt; там, где вам удобно;&lt;/li&gt;
	&lt;li&gt;скачиваем в &lt;code&gt;new-system&lt;/code&gt; образ новой оси (у меня это &lt;code&gt;ubuntu-10.04-x86.tar.gz&lt;/code&gt;);&lt;/li&gt;
	&lt;li&gt;заходим в &lt;code&gt;new-system&lt;/code&gt; и распаковываем образ (&lt;code&gt;tar xzf ubuntu-10.04-x86.tar.gz&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;В итоге, папочка &lt;code&gt;new-system&lt;/code&gt; (у меня &lt;code&gt;/root/new-system&lt;/code&gt;) содержит структуру каталогов новенькой операционки. На следующем шаге мы заменим текущую операционку новой. Только, вот один важный момент. В данном примере новая система родственна старой (сестрички Ubuntu 10.04 и Debian 5.0), поэтому надо будет заменить всего несколько папок, оставив остальные нетронутыми. Если вы собираетесь менять федору на генту, будьте готовы немного поэкспериментировать.&lt;/p&gt;

&lt;h3&gt;Заеняем системные файлы&lt;/h3&gt;

&lt;p&gt;Для того, чтобы сократить период перехода системы из одного состояния в другое, не стоит сразу удалять папки старой системы. Достаточно переименовать их, а на их место переместить новые. Так как мы заменяем все привычные программы, типа &lt;code&gt;mv&lt;/code&gt;, &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;mkdir&lt;/code&gt;, придется делать все вручную прямо из sftp-клиента.&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;переименовываем эти папки новой системы: &lt;code&gt;bin&lt;/code&gt;, &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;etc&lt;/code&gt;, &lt;code&gt;lib&lt;/code&gt;, &lt;code&gt;sbin&lt;/code&gt;, &lt;code&gt;usr&lt;/code&gt;, &lt;code&gt;var&lt;/code&gt;, во что-то типа &lt;code&gt;bin1&lt;/code&gt;, &lt;code&gt;dev1&lt;/code&gt;, &lt;code&gt;etc1&lt;/code&gt; и т.д.;&lt;/li&gt;
	&lt;li&gt;перемещаем переименованные папки из &lt;code&gt;new-system&lt;/code&gt; в корень, они встанут бок о бок с папками текущей оси;&lt;/li&gt;
	&lt;li&gt;одну за другой переименовываем получившиеся пары по такому шаблону &lt;code&gt;bin&lt;/code&gt; ⟶ &lt;code&gt;bin0&lt;/code&gt;, &lt;code&gt;bin1&lt;/code&gt; ⟶ &lt;code&gt;bin&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Перезагружаемся. Запустится новая система. Так как мы развернули кем-то заранее подготовленный образ, в нем уже будет запущен &lt;code&gt;sshd&lt;/code&gt;. Только вот пароль рута будет другой. Если вы обновляли контейнер на хостинге с контрольной панелью (которая, на 4643-м порту), то можете смело менять его оттуда. Можно схитрить и сохранить файл &lt;code&gt;/root/.ssh/authorized_keys&lt;/code&gt; от старой системы. Тогда нас пустят по ключу, и можно будет спокойно менять пароль рута через &lt;code&gt;passwd&lt;/code&gt;. Теперь оставшиеся от старой системы папки можно спокойно удалять (или архивировать на память).&lt;/p&gt;


&lt;p&gt;П.С. У нас контейнеры такие: &lt;code&gt;Linux vXXXX.vps.masterhost.ru 2.6.18-028stab070.7 #1 SMP Fri Oct 1 13:53:00 MSD 2010 i686 GNU/Linux&lt;/code&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/2Bj_XpWLB_w" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Про заголовки в энжи</title>
	<link href="http://kung-fu-tzu.ru/posts/2010/05/14/post-about-nginx-headers" />
	<updated>2010-05-14T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2010/05/14/post-about-nginx-headers</id>
	<content type="html">&lt;p&gt;Пока писал управление заголовками из яваскриптового модуля, удалось родить немного текста &lt;a href="/pages/nginx-headers-management.html"&gt;на английском про заголовки в энжинксе&lt;/a&gt;. А не на русском &lt;a href="http://softwaremaniacs.org/blog/2010/04/01/switching-locale/"&gt;вот почему&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/flGc4g1YBsU" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Хитрые строки против склеивания массива</title>
	<link href="http://kung-fu-tzu.ru/posts/2010/02/05/cons-strings-vs-array-join" />
	<updated>2010-02-05T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2010/02/05/cons-strings-vs-array-join</id>
	<content type="html">&lt;p&gt;Почитывая рассылку &lt;a href="http://groups.google.com/group/v8-users"&gt;v8-users&lt;/a&gt;, заметил интересную тему про оптимизации, специфичные для v8. Особенно интересным показалось сообщение &lt;a href="http://groups.google.com/group/v8-users/browse_thread/thread/fa6746b6fa2d4b2c#msg_13f5f3e44a1c3740"&gt;о ConsStrings&lt;/a&gt; (&lt;a href="http://lingvo.yandex.ru/en?text=cons&amp;lang=en&amp;search_type=lingvo&amp;st_translate=on"&gt;cons в словаре&lt;/a&gt;). И вот, собственно, об этих «составных строках» мне и хочется рассказать. Так как восторг! :)&lt;/p&gt;

&lt;section&gt;
&lt;h&gt;Загадка&lt;/h&gt;

&lt;p&gt;Веб текстовый как со стороны клиента, так и со стороны сервера (особенно сервера). И всем нам постоянно приходится клеить кучу мелких строк в большие строки, а эти большие — в еще б&lt;em&gt;о&lt;/em&gt;льшие, и так далее. На сервере путь последней, самой большой строки (отрендеренной странички, например), оканчивается на передаче их ядру операционки для отправки по сети. В браузере, обычно, строки финишируют на присваивании &lt;code&gt;node.innerHTML = string&lt;/code&gt; или так же, как на сервере — перед отправкой в сокет (аяксом, например).&lt;/p&gt;

&lt;p&gt;Самая вкуснятина в том, что большинство промежуточных строк никому не нужно. Они часто даже своих переменных не имеют и являются всего лишь временными контейнерами для строк большего размера. В худшем случае на каждую конкатенацию требуется: сначала выделить память для новой строки, проинициализировать ее, защитить от сборки мусора, скопировать в выделенную память две (!) склеиваемые строки, передать полученную строку дальше, а потом уничтожить. Но всего этого можно избежать, если…&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;Разгадка&lt;/h&gt;

&lt;p&gt;… если строки в вашем любимом языке нельзя изменять, то есть при любом воздействии всегда создается новая строка, а исходная строка остается нетронутой. Тогда пропадает необходимость сразу копировать конкатенируемые строки в новую область памяти, достаточно только запомнить, из каких строк состоит новая склеенная строка, а реальное копирование отложить до лучших времен. Может показаться, что все эти манипуляции только усложнят и замедлят работу со строками, но нет, все наоборот (особенно в скриптовом языке), и мы это проверим в небольшом тесте.&lt;/p&gt;

&lt;p&gt;Судя по сообщению в рассылке, в v8 конкатенация строк построена как раз по такому принципу (в перле, помнится, это тоже уже давно есть). Цитирую это сообщение &lt;a href="http://twitter.com/erikcorry"&gt;Эрика Кори&lt;/a&gt;: «Когда вы используете &lt;code&gt;+=&lt;/code&gt;, V8 создает структуру типа &lt;code&gt;ConsString&lt;/code&gt;, это узел дерева, который занимает 20 байт на 32-х разрядных машинах или 32 байта — на 64-х разрядных. Это относительно быстрая операция. Как только вы пытаетесь использовать строку для чего-то отличного от конкатенации, она превращается в простую «плоскую» строку похожим способом, как бы это сделал &lt;code&gt;join&lt;/code&gt; (имеется ввиду &lt;code&gt;Array#join()&lt;/code&gt;). Узел &lt;code&gt;ConsString&lt;/code&gt; становится мусором, который мы можем собрать довольно быстро. Алгоритм, по которому должен работать &lt;code&gt;join&lt;/code&gt;, описан в крайне загадочных спецификациях (например, работа с разреженными массивами), потому &lt;code&gt;join&lt;/code&gt; — это не совсем простая операция…».&lt;/p&gt;

&lt;p&gt;И последнее, что мне бы хотелось узнать (сейчас уже не осилю, но как узнаю — допишу), умеет ли кто-нибудь так же хитрить со сравнением строк. Ведь, при такой отложенной склейке строк, можно заодно складывать контрольные суммы этих строк. А когда понадобится сравнить две составные строки строки, то можно будет сначала сравнить их контрольные суммы, и только если суммы совпадают, собрать обе строки и честно сравнить.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;Тестим&lt;/h&gt;

&lt;p&gt;Сравнить производительность простой конкатенации (от которой мы всегда старались уйти) и склеивания массива очень просто. Единственный важный момент, чтобы честно сравнить время, надо попросить движок яваскрипта «собрать» полученную строку (например, пробежавшись регуляркой), если она у него хранится как дерево. Вот небольшой тестик (&lt;a href="http://kung-fu-tzu.github.com/liby/tests/benchmarks/cons-string-vs-array-join.html"&gt;полная версия&lt;/a&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;
// испечем массив исходных строк
// обязательно разных и обязательно честных,
// иначе будем мерить неизвестно что
function bake (arr, prefix, count)
{
	console.time('bake')
	var rex = /xxx/
	for (var i = 0; i &lt; count; i++)
		arr[i] = prefix + i,
		rex.test(arr[i]) // «собираем» строку
	console.timeEnd('bake')
}

var count = 100000, // сколько строк
	rex = /^\w/ // тестовый рекс

;(function(){
	var src = [], prefix = 'testA'
	bake(src, prefix, count)
	
	// берем самый неприятный случай,
	// когда мы не знаем сколько будет строк
	var joiner = []
	
	console.time('join()')
	for (var i = 0; i &lt; count; i++)
		// а еще мы не можем использовать конструкцию joiner[i] = …
		// так как в реальной задаче у нас может не быть цикла
		joiner.push(src[i])
	
	var res = joiner.join('')
	var test = rex.test(res)
	console.timeEnd('join()')
	
	console.log(test, 'regexp test')
	console.log(res === src.join(''), 'index of prefix')
})();

;(function(){
	var src = [], prefix = 'testB'
	bake(src, prefix, count)
	
	var joiner = ''
	
	console.time('+=')
	for (var i = 0; i &lt; count; i++)
		joiner += src[i]
	
	var res = joiner // дублируем, чтобы быть честными
	var test = rex.test(res)
	console.timeEnd('+=')
	
	console.log(test, 'regexp test')
	console.log(res === src.join(''), 'index of prefix')
})();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Тестю все на том же стареньком интеловском маке.&lt;/p&gt;

&lt;p&gt;Результаты вполне ожидаемые. В Хроме простая конкатенация строк в два раза быстрее, чем склеивание массива строк. В Сафари строго наоборот: массив в два раза быстрее строк. Фаерфокс показывает противоречивые результаты: в 3.0 и 3.5 строки процентов на 15% быстрее, чем склейка массива, а в 3.6 наоборот — строки немного отстают. Пока сошлюсь на погрешность измерений. В Опере картинка поинтереснее, все оперы складывают строки до нескольких раз раз быстрее, чем склеивают массив (зачет!). В эксплорере тоже не все печально: шестой делает &lt;code&gt;join()&lt;/code&gt; в 50 раз быстрее конкатенации, седьмой — так же и за то же время, а вот восьмой складывает строки примерно в два раза быстрее, чем склеивает массив, да еще делает любое из этих действий в 100 раз быстрее чем шестой или седьмой на том же железе.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;Итог&lt;/h&gt;
Если делаете сайт, который собирается проработать годы, то забудьте о создании массива только для конкатенации строк. Если же старые эксплореры будут постоянными гостями, то, увы, пока придется клеить строки через массив.
&lt;/section&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/z6Web7T_jxo" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Ветвисто</title>
	<link href="http://kung-fu-tzu.ru/posts/2010/02/02/lots-of-branches" />
	<updated>2010-02-02T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2010/02/02/lots-of-branches</id>
	<content type="html">&lt;p&gt;Еще один пост во славу &lt;a href="http://git-scm.com/"&gt;гита&lt;/a&gt;! Вот такая картина веток образовалась сегодня. Как бы мы жили с ЭссВэЭном? А как оно все пулью работает.&lt;/p&gt;
&lt;p&gt;&lt;img src="i/lots-of-branches.png" style="border:4px solid #eee"/&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/kJaSKIzoRWw" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Вычисление констант на этапе компиляции</title>
	<link href="http://kung-fu-tzu.ru/posts/2010/02/02/compile-time-arithmetics" />
	<updated>2010-02-02T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2010/02/02/compile-time-arithmetics</id>
	<content type="html">&lt;p&gt;Решили сортировать объекты по новизне. Если объект создан не позже чем полгода назад, то его считаем новым. «Полгода» решили хранить в количестве секунд: &lt;code&gt;3600 * 24 * (366 / 2) * 1000&lt;/code&gt;. Из этого выражения сразу видно, что за полгода мы берем половину високосного года, не учитывая никаких високосных минут или секунд.&lt;/p&gt;

&lt;p&gt;И вот, так как мы на скорости повернутые (а иначе Иншейкер бы и не завелся), решили узнать, что быстрее: вычислить самим и зашить в код число 15811200000, или же оставить вычисления на совести машины. Под «чем быстрее», конечно, не стоит понимать выигрыш нескольких наносекунд. Дело в принципе: умеют ли современные интерпретаторы яваскрипта сворачивать такие выражения еще на этапе компиляции. Мало ли когда пригодится ;)&lt;/p&gt;

&lt;p&gt;Получился небольшой тестик (&lt;a href="http://kung-fu-tzu.github.com/liby/tests/benchmarks/compile-time-arithmetics.html"&gt;полная версия&lt;/a&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;
console.time('1 + 1 + … + 1')
var sumA = 0
for (var i = 0; i &lt; 100000; i++)
	sumA += 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +
			1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +
			// and ~100 lines more
			1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
console.timeEnd('1 + 1 + … + 1')
	
var sumB = 0
console.time('+ 2430')
for (var i = 0; i &lt; 100000; i++)
	sumB += 2430
console.timeEnd('+ 2430')
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;А вот и результаты. Фаерфокс умеет это с незапамятных времен. То есть все доступные мне версии (1.5, 2.0, 3.0, 3.5, 3.6) выполнили оба цикла меньше чем за 10 миллисекунд. Хром (4.0) и Сафари (4.0) тоже свернули сумму, на сколько можно об этом судить по времени выполнения в одну миллисекунду. Оперы никакие (9.2, 9.5, 9.6, 10, 10.5) этого не умеют. Больше того 9.2 думала над первым циклом 10 секунд, 9.5, 9.6 и 10 по две секунды, а Опера 10.5 не смогла скомпилить (&lt;a href="mailto:DSK-278503@bugs.opera.com"&gt;баг&lt;/a&gt;) столь длинное выражение из сумм. Эксплореры ушли не дальше Опер: ни один из них (6, 7, 8) не смог угадать маневр, и все они потратили на первый цикл по три секунды. Второй цикл все версии всех браузеров прокрутили в пределах 10-20 миллисекунд.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD 2010-02-02&lt;/strong&gt;: Если в середину этой кучи суммирования вставить одну единственную переменную, то все браузеры сдаются и начинают честно считать сумму в рантайме. А Хром отказывается парсить тест, ссылаясь на переполнение стека вызовов («Maximum call stack size exceeded», &lt;a href="http://code.google.com/p/v8/issues/detail?id=596"&gt;баг&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Старый добрый перл умел такие оптимизации когда еще и браузеров-то не было ;)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/wkuZ0gzDTso" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Пуст ли объект?</title>
	<link href="http://kung-fu-tzu.ru/posts/2010/01/29/is-an-object-empty" />
	<updated>2010-01-29T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2010/01/29/is-an-object-empty</id>
	<content type="html">&lt;p&gt;Сколько способов проверить объект (хеш) на пустоту?&lt;/p&gt;

&lt;p&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
var hash = {}
JSON.stringify(hash) === '{}'
Object.equals(hash, {}) // for in comparision
Object.keys(hash).length === 0
Object.isEmpty(hash) // for (var k in hash) return false
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;section&gt;
&lt;h&gt;Потестим…&lt;/h&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
Object.equals = function (a, b)
{
	for (var k in a)
		if (!(k in b) || a[k] !== b[k])
			return false
	
	for (var k in b)
		if (!(k in a) || a[k] !== b[k])
			return false
	
	return true
}

Object.keys = function (s)
{
	var r = []
	for (var k in s)
		r.push(k)
	return r
}

Object.isEmpty = function (s)
{
	for (var k in s)
		return false
	return true
}

var hash = {},
	count = 10000

console.time('JSON')
for (var i = 0; i &lt; count; i++)
	JSON.stringify(hash) === '{}'
console.timeEnd('JSON')
//=&gt; 61ms

console.time('equals')
for (var i = 0; i &lt; count; i++)
	Object.equals(hash, {})
console.timeEnd('equals')
//=&gt; 49ms

console.time('keys.length')
for (var i = 0; i &lt; count; i++)
	Object.keys(hash).length === 0
console.timeEnd('keys.length')
//=&gt; 44ms

console.time('isEmpty')
for (var i = 0; i &lt; count; i++)
	Object.isEmpty(hash)
console.timeEnd('isEmpty')
//=&gt; 38ms

console.time('isEmpty inlined')
for (var i = 0; i &lt; count; i++)
{
	for (var k in hash)
		false
	true
}
console.timeEnd('isEmpty inlined')
//=&gt; 26ms
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Тестил на самом старом интеловском маке (2 ГГц) в фаерфоксе 3.6.&lt;/p&gt;

&lt;/section&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/LaffxOObeqQ" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Раскрашиваем яваскрипт</title>
	<link href="http://kung-fu-tzu.ru/posts/2010/01/28/highlight-javascript-kinda-textmate" />
	<updated>2010-01-28T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2010/01/28/highlight-javascript-kinda-textmate</id>
	<content type="html">&lt;p&gt;Осилил, наконец, полноценную раскраску для яваскрипта. В &lt;a href="http://softwaremaniacs.org/soft/highlight/"&gt;хайлайтере&lt;/a&gt; уже была &lt;a href="http://softwaremaniacs.org/blog/2007/01/20/highlight-js-javascript/"&gt;поддержка яваскрипта&lt;/a&gt;, но простенькая. А мне хотелось, чтобы все было так же, как в текстмейте, да еще с такими же точно цветами. И вот, на основе стиля sunburst с парочкой добавлений, и с полностью переделанными правилами раскраски получилась это:&lt;/p&gt;

&lt;p&gt;&lt;img src="i/example.png"/&gt;&lt;/p&gt;

&lt;p&gt;А вот тестовая страничка с кучей &lt;a href="/pages/highlight-js-tm.html"&gt;хитрого яваскрипта&lt;/a&gt;, откуда и была сфоткана картиночка выше.&lt;/p&gt;

&lt;p&gt;Для того, чтобы надежно распарсить регулярки (они &lt;a href="/pages/highlight-js-tm.html#regexps"&gt;внизу тестовой странички&lt;/a&gt;), которые очень похожи на оператор деления и на комментарии («&lt;a href="http://softwaremaniacs.org/blog/2008/11/24/highlightjs-50-beta/"&gt;Регэкспы и деления&lt;/a&gt;»), пришлось чуть-чуть пропатчить хайлайтер. Надеюсь, Иван скоро примет &lt;a href="i/highlight-javascript-like-textmate.patch"&gt;патч&lt;/a&gt;, или придумает красивый способ объяснить парсеру нужные яваскрипту тонкости.&lt;/p&gt;

&lt;p&gt;Если вам тоже хочется такую раскраску, милости прошу: модуль с правилами &lt;a href="i/javascript-tm.js"&gt;javascript-tm.js&lt;/a&gt; и пропатченный &lt;a href="i/highlight.js"&gt;highlight.js&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/SYwytqPCTGU" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>RuTils String#translify() и String#dirify() JavaScript port</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/12/11/rutils-string-translify-and-string-dirify-javascript-port" />
	<updated>2009-12-11T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/12/11/rutils-string-translify-and-string-dirify-javascript-port</id>
	<content type="html">&lt;p&gt;Да, это именно то, что написано в заголовке.&lt;/p&gt;

&lt;p&gt;Сейчас мы используем эту штуковину, чтобы получать адреса коктейлей из их названий так же, как это делает серверная часть на руби. Честно говоря, гонять строки на клиенте мне не очень понравилось, так что мы скоро откажемся от этого кода. Поэтому, выкладываю исходник здесь, а не на гитхабе.&lt;/p&gt;

&lt;p&gt;&lt;pre&gt;
&lt;code class="javascript"&gt;
// ported from rutils.rb
;(function(){

var LOWER =
{
	"і":"i","ґ":"g","ё":"yo","№":"#","є":"e",
	"ї":"yi","а":"a","б":"b",
	"в":"v","г":"g","д":"d","е":"e","ж":"zh",
	"з":"z","и":"i","й":"y","к":"k","л":"l",
	"м":"m","н":"n","о":"o","п":"p","р":"r",
	"с":"s","т":"t","у":"u","ф":"f","х":"h",
	"ц":"ts","ч":"ch","ш":"sh","щ":"sch","ъ":"'",
	"ы":"yi","ь":"","э":"e","ю":"yu","я":"ya"
}

var UPPER =
{
	"Ґ":"G","Ё":"YO","Є":"E","Ї":"YI","І":"I",
	"А":"A","Б":"B","В":"V","Г":"G",
	"Д":"D","Е":"E","Ж":"ZH","З":"Z","И":"I",
	"Й":"Y","К":"K","Л":"L","М":"M","Н":"N",
	"О":"O","П":"P","Р":"R","С":"S","Т":"T",
	"У":"U","Ф":"F","Х":"H","Ц":"TS","Ч":"CH",
	"Ш":"SH","Щ":"SCH","Ъ":"'","Ы":"YI","Ь":"",
	"Э":"E","Ю":"YU","Я":"YA"
}

var undef, myName = 'RuTils', Me = self[myName] = 
{
	// Заменяет кириллицу в строке на латиницу. Немного специфично потому что поддерживает
	// комби-регистр (Щука -&gt; Shuka)
	translify: function (str)
	{
		var res = []
		
		for (var i = 0; i &lt; str.length; i++)
		{
			var c = str.charAt(i), r
			
			if ((r = UPPER[c]) !== undef)
			{
				if (LOWER[str.charAt(i+1)] !== undef)
					r = r.toLowerCase().capitalize()
			}
			else
			{
				r = LOWER[c]
				if (r === undef)
					r = c
			}
			
			res[i] = r
		}
		
		return res.join('')
	},
	
	dirify: function (s)
	{
		return s.translify()
				.replace(/(\s&amp;\s)|(\s&amp;amp;\s)/g, ' and ')
				.replace(/\W/g, ' ')
				.replace(/^_+|_+$/g, '')
				.replace(/^\s+|\s+$/g, '') // trim
				.translify() // yes, second
				.replace(/[\s\-]+/g, '-')
				.toLowerCase()
	}
}

// log("Щука".trans() == 'schuka')
// log("апельсиновый сок".trans() == 'apelsinovyiy-sok')

String.prototype.translify = function ()
{
	return Me.translify(this);
}

String.prototype.trans = function ()
{
	return Me.dirify(this);
}


})();

&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/wA09BZzeKkE" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Управление яваскриптовой памятью в энжинксе</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/12/03/nginx-spidermonkey-memory-management" />
	<updated>2009-12-03T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/12/03/nginx-spidermonkey-memory-management</id>
	<content type="html">&lt;section&gt;
&lt;h&gt;Пишем сценарий&lt;/h&gt;

&lt;p&gt;&lt;strong&gt;UPD 2010-06-08&lt;/strong&gt;: Внимание! С тех пор модуль оченно уложнился и точек входа в яваскрипт стало гораздо больше. Теперь каждая обертка укоренняется и связывается с нативным запросом сразу же после оборачивания.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPD 2011-04-08&lt;/strong&gt;: А теперь еще и устройство SpiderMonkey поменялось целиком, и поди разберись, как оно там теперь работает ;)&lt;/p&gt;

&lt;p&gt;Когда только задумался, а не встроить ли яваскрипт в энжинкс, все казалось легко. Достаточно обернуть нативный энжинксовский запрос в объект и передать в яваскриптовый движок. А что там с ним дальше будет — не моя забота, ведь, в яваскрипте есть автоматическая сборка мусора. Оказалось все не так уж и просто. Есть как минимум три разных сценария обработки запроса, каждый из которых требует вдумчивого анализа.&lt;/p&gt;

&lt;p&gt;Первый, и самый простой, тип. Пришел запрос, обернули его в яваскриптовый объект, передали назначенному в конфиге обработчику (обычная яваскриптовая функция, то есть замыкание) и получили ответ. Собственно все. Дальше нам не нужна ни структура запроса в памяти энжинкса, ни объект-обертка в памяти cпайдерманки. Надо только передать текст ответа дальше энжинксу и можно идти спать.&lt;/p&gt;

&lt;p&gt;Второй сценарий посложнее. Начало такое же как и у первого. Запрос → обертка → обработчик. Дальше в развитие событий врывается самая клевая штука во всем этом яваскриптовом встраивании — сохранение запроса в глобальном объекте, чтобы его не съел уборщик. После того, как яваскрипт вернет нам управление, мы уже не сможем просто забыть об объекте-оберке. Это потому, что энжинкс и слыхом не слыхивал о сборке мусора в яваскрипте: он просто освободит память из пула запроса и отдаст ее другому запросу, а то и хуже — под данные чьих-нибудь буферов :) Следовательно, почти сразу после сохранения, объект-обертка начинает указывать в пустоту. А это значит что энжинкс упадет тоже сразу (или хуже того — не сразу) после того, как &lt;nobr&gt;кто-то&lt;/nobr&gt; в яваскрипте попробует обратиться к сохраненному запросу. Например, &lt;nobr&gt;кто-то&lt;/nobr&gt; решит собрать статистику по запросам и станет складывать их в какой-нибудь массив, чтобы потом за один раз посчитать к каким локациям обращались чаще всего. &lt;nobr&gt;Тут-то&lt;/nobr&gt; все и рухнет, так как ни одного их этих запросов уже нет, а память совсем не похожа на то, что там было в момент сохранения объекта-обертки в массиве. Да, вот так закручено, люблю этот мир ;)&lt;/p&gt;

&lt;p&gt;Третий тип запросов еще хитрее, если такое вообще возможно. Начало опять простое: запрос к энжинксу → обертка → вызов яваскрипта. И тут в развитие событий врывается вторая суперудобная и обалденная штука из мира динамических языков — замыкания. &lt;nobr&gt;Кто-то&lt;/nobr&gt; в яваскрипте решает выполнить подзапрос или поставить таймер. При таких делах яваскрипт сохранит объект-обертку так же, как и во втором ходе событий, и нам придется решать все те же проблемы. Но само замыкание для нас никто не сохранит, а это значит, что когда подзапрос выполнится или таймер сработает, уже не будет ни объекта-обертки, ни замыкания — их всех удалит сборщик мусора — останутся просто указатели в пустоту. То есть этот тип развития событий подразумевает взаимные ссылки: яваскрипта на энжинкс, а энжинкса на яваскрипт.&lt;/p&gt;
&lt;/section&gt;


&lt;section&gt;
&lt;h&gt;О скорости&lt;/h&gt;

&lt;p&gt;Третий тип развития событий самый полный и включает в себя решение всех задач по встраиванию скриптового движка в материнское приложение. Если так, то тогда зачем мы тут себе голову забиваем этими примитивными путями. А вот зачем.&lt;/p&gt;

&lt;p&gt;Энжинкс славится своей скоростью и малым потреблением ресурсов. Ну, собственно одно из другого следует. И раз он так хорош, то не стоит его совсем уж портить этим медленный SpiderMonkey (в сравнении с Google v8 и SquirrelFish Extreme — да, ключевики всё же должны встречаться в теле страницы). Так вот, если все делать по третьему варианту, то для каждого, даже, малюсенького запросика мы должны проделать кучу работы по управлению памятью. И если научиться определять, по какому пути пошел процесс, то можно изрядно сэкономить. Один момент. Второй вариант — это когда яваскрипт &lt;nobr&gt;куда-то&lt;/nobr&gt; складывает запросы — совершенно неопределим для спайдерманки (или v8) без запуска полного цикла сборки мусора. А это просто сказочно медленно. Таким образом, мы избавляемся от одной из веток развития событий. Итак, их осталось две: простая, когда можно забить на мусоросборщик почти совсем; и сложная, когда надо везде все запомнить и всячески перестраховаться.&lt;/p&gt;

&lt;p&gt;Запросов первого типа будет подавляющее большинство. Это могут быть запросы к кешу, к статусу какого-нибудь процесса, или запросы типа «запланируй вот это, а я пока погуляю». Поэтому, стоит научиться определять их быстро. В энжинксе, начиная с версии 0.8 (не помню точно с какой подверсии) появился счетчик ссылок на запрос. Да, как в перле — &lt;a href="http://en.wikipedia.org/wiki/Reference_counting"&gt;reference counting&lt;/a&gt;. ИМХО, просто, понятно и относительно надежно. Нам, скриптерам (а я далеко не один такой — чего только в энжинкс не страивают), этот счетчик вообще незаменим, и не только для решения этой задачи. Так вот, его-то мы и используем для разделения задачи на две, обозначенные выше. Если &lt;code&gt;r-&gt;main-&gt;count&lt;/code&gt; больше единицы, то у нас ситуация, когда запрос будет жить дольше, чем выполняется наш обработчик. Если же равен единице, то можно на все забить и все выкинуть (ну, почти). Если меньше единицы, значит мы &lt;nobr&gt;где-то&lt;/nobr&gt; накосячили, и стоит ждать сегфолта. Научившись быстро определять, какие задачи надо решить в конкретном случае, пора уже начать решать эти задачи.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;Решаем проблемы&lt;/h&gt;

&lt;p&gt;В первом варианте задач никаких и нет. Вновь созданный объект-обертка сразу же передается функции внутри яваскрипта в виде параметра (или инвоканта), что защищает его от уничтожения, пока эта эта функция не завершится. А завершится она как раз тогда, когда этот объект становится нам больше не нужен. Надо только не забыть выставить указатель на нативный запрос в ноль. Здесь могут быть мелкие неприятности, типа того, что спайдерманки решит начать уборку как раз между созданием объекта и передачей его в функцию. Это возможно в многопоточных приложениях и для борьбы с такими неприятностями есть функции входа и выхода в «неубираемую» область (&lt;code&gt;&lt;a href="https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_EnterLocalRootScope"&gt;JS_EnterLocalRootScope()&lt;/a&gt;&lt;/code&gt; и &lt;code&gt;&lt;a href="https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_LeaveLocalRootScope"&gt; JS_LeaveLocalRootScope()&lt;/a&gt;&lt;/code&gt;). А еще, из неприятностей, могут взять и отменить защиту от сборки для параметров, переданных извне. Но это все мелочи и они решаются легко и быстро.&lt;/p&gt;

&lt;p&gt;И вот, &lt;nobr&gt;наконец-то,&lt;/nobr&gt; решение задач второго варианта (он же третий по первоначальному табелю). Повторим коротко условия:
&lt;ol&gt;
	&lt;li&gt;Нельзя ссылаться на отработавший свое запрос энжинкса.&lt;/li&gt;
	&lt;li&gt;Нельзя дать уборщику уничтожить объект-обертку, который еще может понадобиться энжи.&lt;/li&gt;
	&lt;li&gt;Необходимо сохранить от уборщика все яваскриптовые функции, которые еще понадобятся.&lt;/li&gt;
	&lt;li&gt;Надо не забыть разрешить уничтожение объекта-обертки, когда он больше не нужен.&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;Итак по порядку.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;1. Инвалидация указателя&lt;/h&gt;

&lt;p&gt;Избавиться от ссылки на запрос энжинкса (это такая большущая многоярусная структура) &lt;nobr&gt;вообще-то&lt;/nobr&gt; нельзя. То есть пока обертка плавает &lt;nobr&gt;где-то&lt;/nobr&gt; в памяти яваскрипта, она будет ссылаться &lt;nobr&gt;куда-то&lt;/nobr&gt;. То есть, через час после запроса мы все-таки можем вызвать &lt;code&gt;r.sendHeaders(200, "Content-type: plain: encoding=utf-8")&lt;/code&gt;. Важно &lt;nobr&gt;как-то&lt;/nobr&gt; понять, что этого делать нельзя и сообщить об этом скрипту. Тут все очень просто: достаточно сказать объекту-обертке, что теперь он указывает на &lt;code&gt;NULL&lt;/code&gt;, а при каждом обращении к свойствам или методам проверять, не равен ли &lt;code&gt;NULL&lt;/code&gt;, случайно, наш обернутый запрос. В том случае, если уже нал (нулл, нил, нуль, ноль, зеро), то выбросить исключение и вернуть пользователю красивую ошибку 500. Это, прямо скажем, очень здорово, если сравнивать с падением всего рабочего процесса, обслуживающего в этот момент тысячу—другую невинных клиентов.&lt;/p&gt;

&lt;p&gt;Как же выставить указатель на оборачиваемый объект в &lt;code&gt;NULL&lt;/code&gt;? Спасибо Игорю, это очень просто. Достаточно подписаться на событие уничтожения запроса, и в обработчике этого события выставить указатель в ноль. Просто, быстро и надежно, как сам нжинкс.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;2. Защита от уничтожения — корни&lt;/h&gt;

&lt;p&gt;Защитить объект от уборки (на рабочем столе и по дому, a еще разрешить ему посуду не мыть) можно как угодно легко. Можно присвоить его глобальной переменной или свойству другого защищенного объекта (&lt;code&gt;global. requests. push(r)&lt;/code&gt;). Можно записать его в слот защищенного объекта (&lt;code&gt;&lt;a href="https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_GetReservedSlot"&gt;JS_SetReservedSlot()&lt;/a&gt;&lt;/code&gt;), а можно сделать его самого защищенным, назначив корнем (&lt;code&gt;&lt;a href="https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_AddRoot"&gt;JS_AddRoot()&lt;/a&gt;&lt;/code&gt;). Сборщик мусора начинает помечать нужные еще объекты с таких корней. Глобальный объект, например, является именно таким корнем. В данном случае, мы создадим в структуре запроса энжинкса специальный указатель (&lt;code&gt;jsval&lt;/code&gt;), который попросим назначать корнем всякий объект, на который он указывает. И, да, именно, запишем туда над объект-обертку. Все, его не уничтожат до тех пор, пока мы не уничтожим корень. А корень мы уничтожим там же, где выставляли &lt;code&gt;NULL&lt;/code&gt; — в обработчике события уничтожения запроса. Но об этом далее.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;3. Защита от уничтожения — слоты в корнях&lt;/h&gt;

&lt;p&gt;Защитить функции (они же замыкания, или, почти, как &lt;a href="http://alenacpp.blogspot.com/2007/04/blog-post.html"&gt;функторы&lt;/a&gt;) можно теми же методами, что и сам объект-обертку. Собственно, год назад я так и поступил: добавлял для обработчика тела новый корень, а потом его удалял. Теперь же добавились таймеры и подзапросы. Клепать корни для всех этих товарищей в глобальном пространстве не хочется, так как запросов будут тысячи. Нам хватит нагрузки от создания корней для самих этих тысяч запросов. К тому же создание корня (как сейчас &lt;a href="http://mxr.mozilla.org/mozilla-central/source/js/src/jsgc.cpp#1163"&gt;видно из исходников&lt;/a&gt; спайдерманки) — это добавление ключа в хеш по значению указателя. И чем больше мы туда запихнем, тем хуже для нас. Кстати, как раз для того, чтобы разгрузить этот хеш мы тут и затеяли все эти выкрутасы с разделение запросов на два типа, из чего теперь получился целый здоровенный пост (вы читать не устали?).&lt;/p&gt;

&lt;p&gt;Так вот, вместо корней лучше используем слоты. Они тоже не быстрые (судя по исходникам спайдерманки там нет ничего быстрого), но это хотя бы не поиск в таблице из тысяч записей. А еще слоты хороши тем, что их можно потом не стирать. То есть объект-обертка когда-нибудь будет убран а вместе с ним и все эти функции со своими локальными и замкнутыми переменными. Для v8 это вообще подарок с его поколениями переменных. Этот подход позволяет обработчику запроса энжинкса отцепить объект-обертку и одним махом избавиться ото всех его паразитов.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;4. Освобождение памяти&lt;/h&gt;

&lt;p&gt;Правильно отцепить объект-обертку — дело не хитрое. Отцепим его там же, где помечали его пустым, в обработчике уничтожения запроса. Ах, как же я был рад, когда нашел его в исходниках энжинкса! Тут достаточно вызвать &lt;code&gt;&lt;a href="https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference/JS_RemoveRoot"&gt;JS_RemoveRoot()&lt;/a&gt;&lt;/code&gt; и все в порядке.&lt;/p&gt;

&lt;p&gt;После подробного разбора всех накладных расходов по управлению памятью, становится понятно, зачем мы тут всю эту кашу заварили.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;Послесловие&lt;/h&gt;

&lt;p&gt;При чем тут v8? А при том, что завязываться на один браузер, тьфу, то есть движок яваскрипта, мы, клиентские программисты, не приучены. Сегодня (точнее уже вчера) хорош и стабилен спайдерманки, а завтра (точнее уже сегодня) становится хорош v8. А потом станет хорош еще &lt;nobr&gt;кто-то,&lt;/nobr&gt; или даже совсем не яваскрипт. А ведь корни, слоты и указатели на нативные объекты есть везде, где убирают мусор автоматом.&lt;/p&gt;

&lt;p&gt;Где исходники? Они — &lt;a href="http://github.com/kung-fu-tzu/ngx_http_js_module"&gt;на гитхабе&lt;/a&gt;. Скоро поставлю красивые гитхабовские ссылочки на строчечки (обожаю гитхаб!).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ого, а хотел в пару абзацев уложиться.&lt;/em&gt;&lt;/p&gt;
&lt;/section&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/RRemQlwBndM" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Немного о работе</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/11/14/about-work" />
	<updated>2009-11-14T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/11/14/about-work</id>
	<content type="html">&lt;p&gt;Наш любимый &lt;a href="http://www.inshaker.ru/"&gt;коктейльный сайт&lt;/a&gt; — и, в частности, я сам — нуждается в ежедневной хорошо оплачиваемой поддержке людей, программирующих на яваскрипте. Мы, даже, сделали &lt;a href="http://www.inshaker.ru/job.html"&gt;секретную страничку&lt;/a&gt; специально для них. Она маленькая и очень оптимистичная ;)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/WCCueC2YBTc" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Статистика от Google Analytics для amCharts через XSLT</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/10/26/charts-from-google-analytics-to-amcharts-by-xslt" />
	<updated>2009-10-26T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/10/26/charts-from-google-analytics-to-amcharts-by-xslt</id>
	<content type="html">&lt;p&gt;Сделал новую &lt;a href="http://www.inshaker.ru/about.html"&gt;статистику посещаемости&lt;/a&gt; для &lt;a href="http://www.inshaker.ru/"&gt;Иншейкера&lt;/a&gt;. Рисуется она, как и &lt;a href="http://vaskas.ru/2008/08/08/displaying-google-analytics-charts-on-the-website/"&gt;прежняя&lt;/a&gt;, с помощью &lt;a href="http://www.amcharts.com/"&gt;amCharts&lt;/a&gt;. Но теперь все действия по выкачиванию и преобразовыванию данных выполняются простым bash-скриптом и стандартной утилитой &lt;a href="http://xmlsoft.org/XSLT/xsltproc2.html"&gt;xsltproc&lt;/a&gt;. Думаю, &lt;code&gt;bash&lt;/code&gt; и &lt;code&gt;xsltproc&lt;/code&gt; есть у всех.&lt;/p&gt;

&lt;p&gt;Дело делают всего два скрипта: &lt;code&gt;login.sh&lt;/code&gt; и &lt;code&gt;update.sh&lt;/code&gt;. Первый логинится в гугл, используя мыло и пароль из переменных окружения (нечего им в коде делать). Гугл в ответ отдает авторизационный токен, который, согласно документации, можно использовать около двух недель. Второй скрипт выкачивает статистику в формате &lt;a href="http://en.wikipedia.org/wiki/Atom_(standard)"&gt;Atom&lt;/a&gt;, расширенном гуглом. На этом программирование на баше заканчивается, и начинается XSLT.&lt;/p&gt;

&lt;p&gt;Скрипты со стилями положил на гитхаб в &lt;a href="http://github.com/kung-fu-tzu/analytics-xslt"&gt;analytics-xslt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD&lt;/strong&gt;: разошелся и сделал нам &lt;a href="http://www.inshaker.ru/geeks/stats.html"&gt;статистику по браузерам&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/PMYDaBlX6h8" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>GitX</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/10/10/gitx" />
	<updated>2009-10-10T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/10/10/gitx</id>
	<content type="html">&lt;p&gt;&lt;img src="i/gitx.png" alt="GitX" class="aside"/&gt;Выбросил дорогущий &lt;a href="/posts/2008/11/24/versions-app-released/"&gt;Versions.app&lt;/a&gt; вслед за эсвээном. Теперь обожаю бесплатный &lt;a href="http://gitx.frim.nl/"&gt;GitX&lt;/a&gt;. Thanks a lot Pieter!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/s48S81W41Rk" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Array#sort()</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/09/02/array-sort" />
	<updated>2009-09-02T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/09/02/array-sort</id>
	<content type="html">&lt;section&gt;
&lt;p&gt;Так как во время сортировки массива (а также обращения) яваскрипт не создает его копию, сортировать числа можно очень быстро. И это здорово — но скучно.&lt;/p&gt;

&lt;p&gt;Интереснее становится, когда надо отсортировать большой массив объектов по нескольким вложенным правилам. Например, отсортировать &lt;a href="http://www.inshaker.ru/cocktails.html"&gt;кучу коктейлей&lt;/a&gt; по количеству в них водки, далее отсортировать по количеству ингредиентов, а затем еще и по алфавиту. Вопрос в том, как выразить такую логику.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;Многообразие&lt;/h&gt;
&lt;p&gt;Предположим, что нам нужно отсортировать массив объектов &lt;code&gt;{x: число, y: число, z: число}&lt;/code&gt; сначала по &lt;code&gt;x&lt;/code&gt;, потом по &lt;code&gt;y&lt;/code&gt; потом по &lt;code&gt;z&lt;/code&gt;. Есть как минимум три очевидных способа.&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;Каскадный:&lt;/dt&gt;
&lt;dd&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
array.sort(byZ).sort(byY).sort(byX)
&lt;/code&gt;&lt;/pre&gt;
&lt;small&gt;Так можно делать, когда интерпретатор поддерживает «stable sort» и сортировки не связаны (тут так и есть).&lt;/small&gt;
&lt;/dd&gt;
&lt;/dl&gt;

&lt;dl&gt;
&lt;dt&gt;Одиночный:&lt;/dt&gt;
&lt;dd&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
array.sort(function (a, b) { return byX(a, b) || byY(a, b) || byZ(a, b) })
&lt;/code&gt;&lt;/pre&gt;
&lt;small&gt;&lt;strong&gt;UPD 2010-01-12&lt;/strong&gt;: Конструкция хитрая, но работает. В яваскрипте (и еще, как минимум, в перле, руби, питоне и Си) оператор &lt;code&gt;||&lt;/code&gt; не приводит аргументы к булеву типу, что позволяет клеить такие цепочки хоть до посинения.&lt;/small&gt;
&lt;/dd&gt;
&lt;/dl&gt;

&lt;dl&gt;
&lt;dt&gt;Раскрытый (он же «inlined»):&lt;/dt&gt;
&lt;dd&gt;
&lt;pre&gt;&lt;code class="javascript"&gt;
array.sort(function (a, b) { return a.x - b.x || a.y - b.y || a.z - b.z })
&lt;/code&gt;&lt;/pre&gt;
&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;В хороших и зрелых браузерах они все работают быстро и сортируют одинаково правильно (ага!). Но в нехороших браузерах, что-то подтормаживает, а что-то даже и не сортирует.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;Кросбраузерность и скорость&lt;/h&gt;
&lt;p&gt;Каскадный способ очень красив и даже не очень тупит, но не работает в Опере (включая зарелизенную десятку) и в Хроме (включая альфы четверки, &lt;a href="http://code.google.com/p/v8/issues/detail?id=90"&gt;они об этом знают&lt;/a&gt;). В третьем фаерфоксе, во всех ИЕ, а также в сафари три типа сортировки дают правильный порядок элементов. То есть придется париться с обертками.&lt;/p&gt;

&lt;p&gt;Тесты показывают, что одиночный &lt;code&gt;sort()&lt;/code&gt; почти в два раза быстрее каскадного. Раскрытый — еще быстрее раза в полтора-два-три (даже в хроме с его мегаумным В8). Забавно, что каскадный и одиночный способы сортировки дают одинаковые результаты в ИЕ 6 и 7 (в восьмом картина такая же как и в других браузерах).&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;Тестим&lt;/h&gt;
&lt;p&gt;&lt;pre&gt;&lt;code class="javascript"&gt;var messed =
[
	{x: 2, y: 0, z: 0},
	{x: 2, y: 0, z: 1},
	{x: 2, y: 0, z: 2},
	{x: 2, y: 1, z: 0},
	{x: 2, y: 1, z: 1},
	{x: 2, y: 1, z: 2},
	{x: 2, y: 2, z: 0},
	{x: 2, y: 2, z: 1},
	{x: 2, y: 2, z: 2},
	
	{x: 0, y: 0, z: 0},
	{x: 0, y: 0, z: 1},
	{x: 0, y: 0, z: 2},
	{x: 0, y: 1, z: 0},
	{x: 0, y: 1, z: 1},
	{x: 0, y: 1, z: 2},
	{x: 0, y: 2, z: 0},
	{x: 0, y: 2, z: 1},
	{x: 0, y: 2, z: 2},
	
	{x: 1, y: 0, z: 0},
	{x: 1, y: 0, z: 1},
	{x: 1, y: 0, z: 2},
	{x: 1, y: 1, z: 0},
	{x: 1, y: 1, z: 1},
	{x: 1, y: 1, z: 2},
	{x: 1, y: 2, z: 0},
	{x: 1, y: 2, z: 1},
	{x: 1, y: 2, z: 2}
]

var sorted =
[
	{x: 0, y: 0, z: 0},
	{x: 0, y: 0, z: 1},
	{x: 0, y: 0, z: 2},
	{x: 0, y: 1, z: 0},
	{x: 0, y: 1, z: 1},
	{x: 0, y: 1, z: 2},
	{x: 0, y: 2, z: 0},
	{x: 0, y: 2, z: 1},
	{x: 0, y: 2, z: 2},
	
	{x: 1, y: 0, z: 0},
	{x: 1, y: 0, z: 1},
	{x: 1, y: 0, z: 2},
	{x: 1, y: 1, z: 0},
	{x: 1, y: 1, z: 1},
	{x: 1, y: 1, z: 2},
	{x: 1, y: 2, z: 0},
	{x: 1, y: 2, z: 1},
	{x: 1, y: 2, z: 2},
	
	{x: 2, y: 0, z: 0},
	{x: 2, y: 0, z: 1},
	{x: 2, y: 0, z: 2},
	{x: 2, y: 1, z: 0},
	{x: 2, y: 1, z: 1},
	{x: 2, y: 1, z: 2},
	{x: 2, y: 2, z: 0},
	{x: 2, y: 2, z: 1},
	{x: 2, y: 2, z: 2}
]

function byX (a, b) { return a.x - b.x }
function byY (a, b) { return a.y - b.y }
function byZ (a, b) { return a.z - b.z }
function single (a, b) { return byX(a, b) || byY(a, b) || byZ(a, b) }
function combined (a, b) { return a.x - b.x || a.y - b.y || a.z - b.z }

var m = messed.slice()
console.time('multiple')
for (var i = 0; i &lt; 1000; i++)
{
	m.sort(byZ),
	m.sort(byY),
	m.sort(byX)
}
console.timeEnd('multiple')

var s = messed.slice()
console.time('single')
for (var i = 0; i &lt; 1000; i++)
{
	s.sort(single)
}
console.timeEnd('single')

var c = messed.slice()
console.time('combined')
for (var i = 0; i &lt; 1000; i++)
{
	c.sort(combined)
}
console.timeEnd('combined')
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;/section&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/Y3-4_B1zBKk" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Кастомные события</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/07/27/custom-events" />
	<updated>2009-07-27T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/07/27/custom-events</id>
	<content type="html">&lt;p&gt;Во всех браузерах, кроме сами знаете какого, можно играться с событийным движком браузера, как душе угодно. Гибкость дается наравне с нативными событиями: можно называть их как угодно, настраивать всплываемость и отменяемость, добавлять свои свойства и методы. Такие трюки удобны для создания виджетов со своей моделью событий; и для имитации полезных событий браузера там, где их не поддерживают (типа, &lt;code&gt;onready&lt;/code&gt; или &lt;code&gt;onhashchanged&lt;/code&gt;); а еще для расширения базовой логики приложения кем-то третьим (например, события &lt;code&gt;hide&lt;/code&gt;/&lt;code&gt;show&lt;/code&gt; для отключения анимации в скрытых нодах); да мало ли для чего еще могли бы пригодиться свои собственные события. Но не в IE.&lt;/p&gt;

&lt;p&gt;Событийную модель можно реализовать на яваскрипте, и воспользоваться всеми этими прелестями. Но только не для нод в дереве документа, а для простых обектов (тут ничего нового — очередная реализация интерфейса EventTarget). А ведь хочется вешать обработчики именно на ноды, документ, окно… на аяксовый запрос, наконец!&lt;/p&gt;

&lt;p&gt;Решили научить IE распространять по документу выдуманные типы событий и посмотреть, что из этого выйдет. У этой задачи есть простое решение: достатачно на яваскрипте повторить движок браузера для подписки/отписки и распространения событий. Код получается интересный, так что это решение вполне приемлемо. С другой стороны, зачем дублировать то, что уже реализовали нативно, а значит быстро. Надо всего-то чуть-чуть допилить браузер.&lt;/p&gt;

&lt;p&gt;В этом случае можно выбрать некоторое событие как транспорт. Это событие должно по умолчанию всплывать и не иметь никаких встроенных обработчиков. Поискав немного, нашел &lt;code&gt;oneditfocus&lt;/code&gt;. Мы его нигде не используем, а по параметрам он подходит как раз. На нем, пока, и остановился — самое сложное позади. Далее дело техники: научить &lt;code&gt;addEventListener&lt;/code&gt; и &lt;code&gt;dispathEvent&lt;/code&gt; понимать, какие события обрабатывать как есть, а какие оборачивать в &lt;code&gt;oneditfocus&lt;/code&gt;. Здесь тоже ничего сложного: ищем событие в списке нативных и, если не нашли — оборачиваем.&lt;/p&gt;

&lt;p&gt;В итоге, за наши труды получаем такой код, без проблем работающий в IE 6-7-8:
&lt;pre&gt;&lt;code class="javascript"&gt;
window.addEventListener("ready", function (e) { alert("got an event type " + e.type) }, false)
var ready = document.createEvent("Event")
ready.initEvent("ready", true, true)
window.dispathEvent(ready)
//=&gt; got event type ready
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Конечно, IE не поддерживает ни &lt;code&gt;addEventListener&lt;/code&gt;, ни &lt;code&gt;dispathEvent&lt;/code&gt;, но их поддержку нетрудно добавить.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD&lt;/strong&gt;: &lt;a href="http://dean.edwards.name/"&gt;Дин Эдвардс&lt;/a&gt; сделал работающую модель событий для IE в своей библиотеке &lt;a href="http://code.google.com/p/base2/"&gt;Base2&lt;/a&gt;. Она показалась мне громоздкой и сумбурной. Да и стиль кодирования Дина меня тоже не особо прет: все эти точки с запятой, бее ;)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/xu5A8aChRCI" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Закрывающий крестик</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/07/15/closing-cross" />
	<updated>2009-07-15T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/07/15/closing-cross</id>
	<content type="html">&lt;style type="text/css"&gt;
.cross { background: url(i/cross.png) 0 0; width: 76px; height: 53px; }
.cross:hover { background-position: 0 -53px; }
&lt;/style&gt;

&lt;p&gt;Во всплывающем окошке принято делать закрывающий крестик. Его можно сделать картинкой. Можно — буквой «X». А можно заюзать символ «×», который красивый и отлично видимый везде.&lt;/p&gt;

&lt;dl&gt;
&lt;dt&gt;Как-то так:&lt;/dt&gt;
&lt;dd&gt;
&lt;div class="cross"&gt;&lt;/div&gt;
&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Вы все еще пишете &lt;code&gt;&amp;amp;nbsp;&lt;/code&gt;? — Тогда мы идем к вам!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/iHc-t7Ljw4g" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>git filter-branch</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/07/11/git-filter-branch" />
	<updated>2009-07-11T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/07/11/git-filter-branch</id>
	<content type="html">&lt;p&gt;&lt;em&gt;Завтра переписывал историю. Понравилось.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Напишу тут немного про &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-filter-branch.html"&gt;git filter-branch&lt;/a&gt; потому, что ничего внятного ни на русском, ни на аглицком нагуглить не удалось. Повторять мои ошибки вам будет дороговато, так как более менее сложное отфильтровывание истории — штука скучная и затяжная.&lt;/p&gt;

&lt;p&gt;Не забудьте забекапить папочку &lt;code&gt;.git&lt;/code&gt; из вашего репозитория.&lt;/p&gt;


&lt;h3&gt;Раздвоение&lt;/h3&gt;

&lt;p&gt;Если у вас больше одного бранча (а как же иначе?), то, судя по названию команды, отфильтровать получится историю только текущего бранча. И вы получите раздвоение репозитория. А это значит, что лучше всего сначала смерджить все в мастер, и только потом начинать творить все эти противоестественные вещи. Даже больше. Если вы работате в команде (а как же иначе?), то важно всем все смерджить в свои мастеры, а потом смерджить все мастеры у кого-то одного, и только потом запускать &lt;code&gt;git filter-branch&lt;/code&gt; на его компе. Иначе, получив новый репозиторий с абсолютно иными именами коммитов, вы замучаетесь мерджиться друг с другом. Еще хуже то, что новая история будет только у того, кто ее себе смастерил. Остальные перцы (включая боевые сервера, если вы храните на них репу целиком) получат из новой истории только последний комит, а впридачу всю его новую историю в качестве багажа.

Повторим. Сначала слейте все в один репозиторий, потом максимально упростите его структуру, а потом уже издевайтесь над ним. В случае, если не удается смердить все в мастер, запустите фильтр сначала в мастере, а потом в каждом из оставшихся бранчей. Чтобы не тратить время на переписывание тех коммитов, которые общие у мастера и у текущей ветки, воспользуйтесь опциями для выделения отрезка истории, описанными в руководстве. Мужайтесь.

После того, как вы перелопатили историю на одном из компьютеров, знайте, что это практически новый репозиторий. А это значит, что его надо всем заново склонировать (не забудьте про боевые и сборочные сервера). Чтобы быстрее заливалось, запустите &lt;code&gt;git gc --aggressive&lt;/code&gt;, и гит аккуратненько уложит все объекты в один файл.&lt;/p&gt;

&lt;h3&gt;Редактируем авторов&lt;/h3&gt;

&lt;p&gt;Так как мы в &lt;a href="http://www.inshaker.ru/"&gt;Иншейкере&lt;/a&gt; на гит набросились, как голодные псы, никто, конечно, не удосужился сконфигурировать свои имя и мыло через &lt;code&gt;&lt;a href="http://github.com/guides/tell-git-your-user-name-and-email-address"&gt;git configure --global&lt;/a&gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Чтобы исправить эту досадную ошибку и заменить логин в системе на свое имя, а кривой адрес — на прямой, запускайте:
&lt;pre&gt;&lt;code class="bash"&gt;git filter-branch --env-filter \
'
if [ "$GIT_AUTHOR_NAME" = "user123" ];
then
  export GIT_AUTHOR_NAME="User Name"
fi

if [ "$GIT_AUTHOR_EMAIL" = "user123@host.local" ];
then
  export GIT_AUTHOR_EMAIL="user@name.ru"
fi
'
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;h3&gt;Выбрасываем мусор&lt;/h3&gt;

&lt;p&gt;Как-то давно, мы решили хранить псдшники и видео в репозитории. Наверное заболели. Вот лекарство от файлов &lt;code&gt;*.flv&lt;/code&gt;, &lt;code&gt;*.mp3&lt;/code&gt; и &lt;code&gt;*.psd&lt;/code&gt; (нужное подчеркнуть):
&lt;pre&gt;&lt;code class="bash"&gt;git filter-branch --tree-filter 'find . \( -name "*.flv" -o -name "*.mp3" -o -name "*.psd" \) -exec rm -rf {} \;'&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;


&lt;h3&gt;Если не получается&lt;/h3&gt;

&lt;p&gt;Повторно запускать фильтр гит откажется, предупреждая о том, что придется затереть бекап. Вылетит со словами «Cannot create a new backup. A previous backup already exists in refs/original/». Настоять на своем можно, указав ключ &lt;code&gt;-f&lt;/code&gt; или вручную стерев папочку &lt;code&gt;refs/original/&lt;/code&gt;. Кстати, да, всего несколько файликов в этой папочке и есть весь «бекап». Дело в том, что гит очень умный, и он не удалит старое пространство объектов, пока вы его явно об этом не попросите (&lt;code&gt;git gc --prune=now&lt;/code&gt;). Именно поэтому, пока в папке &lt;code&gt;refs/&lt;/code&gt; есть ссылки на старое дерево, ничего не удалится. В общем, стандартный сборщик мусора, знакомый нам по яваскрипту.&lt;/p&gt;

&lt;p&gt;Если вы переписывали историю для того, чтобы выбросить из нее мусор, а по окончании мучений репа стала даже чуть больше, не отчаивайтесь. Дело как раз в бекапе. Чтобы стопроцентно очистить репозиторий, надо склонировать этот репозиторий в другое место и запустить там &lt;code&gt;git gc --prune=now&lt;/code&gt;. Не очень красиво, но так &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-filter-branch.html#_checklist_for_shrinking_a_repository"&gt;официально советуют&lt;/a&gt; делать.&lt;/p&gt;

&lt;p&gt;Знаю по опыту, что гит ошарашивает своей гибкостью и многословием. Поэтому — спрашивайте, если что-то не получается. Было бы мне у кого спросить, замучал бы его совсем :)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/W2clnPAfTuY" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Многоточие</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/07/08/three-dots" />
	<updated>2009-07-08T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/07/08/three-dots</id>
	<content type="html">&lt;p&gt;Если текст не помещается по ширине, можно сделать резиновые блоки или переход текста на новую строчку. Но если не хочется ломать сетку, то можно взять и отрубить лишние буквы, а на их место аккуратно постивать многоточие. Грубо, конечно, но работает. Чтобы не напрягаться с логикой появления полной версии строки, достаточно выставить ноде свойство &lt;code&gt;title&lt;/code&gt;. Этот путь тоже слишком легок для джедаев, но он подойдет, когда у вас под рукой нет светового меча.&lt;/p&gt;

&lt;p&gt;Красивый пример можно посмотреть на нашем восхитительном Иншейкере &lt;a href="http://www.inshaker.ru/events/moskva/finans-bank-forum-2"&gt;в разделе событий&lt;/a&gt;.&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/2ikrNSvB1PI" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Белый фон</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/07/08/pngm" />
	<updated>2009-07-08T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/07/08/pngm</id>
	<content type="html">&lt;p&gt;Нужна была консольная утилита добавляющая в png-файл поле с белым цветом фона по умолчанию (bKGD chunk). Было это нужно, чтобы при распечатке коктейлей в IE6 не было противного серого фона (AlphaImageLoader при печати не пашет). Изобретать велосипед не хотелось, поэтому нашел &lt;a href="http://www.codeproject.com/KB/cs/PngUtil.aspx"&gt;статью&lt;/a&gt;, где чувак подробно рассказывает, как добавить подобную функциональность в программу на C#. Самое полезное там — это объяснение того, что чанк с цветом фона можно добавить прямо перед первым чанком с данными. Но у него все на микрософтовском сишарпе, а мы-то сидим на маках с линуксами. Погуглив еще минутку, нашел и остальное. Все, что связано с PNG, как ни странно, вполне доступно изложено на W3C: и про &lt;a href="http://www.w3.org/TR/PNG/#11bKGD"&gt;фон&lt;/a&gt;, и про &lt;a href="http://www.w3.org/TR/PNG/#5DataRep"&gt;формат&lt;/a&gt; вообще.&lt;/p&gt;

&lt;p&gt;Поколдовав немного, получил очень прямолинейную программу, выполняющую только одно действие: задание белого, и только белого фона. Выложил &lt;a href="http://github.com/kung-fu-tzu/pngm"&gt;на гитхаб&lt;/a&gt;, так как планирую добавить возможность хотя бы задавать цвет :)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD&lt;/strong&gt; (02.12.2009): если обработать один файл дважды, то туда попадут два чанка с бекграундом, от этого картинка сломается в IE. Все дело в этих байтах: &lt;em&gt;00000006624B474400FF00FF00FFA0BDA793&lt;/em&gt;, сотрите их и все будет норм :) А еще утилитка &lt;code&gt;&lt;a href="http://www.imagemagick.org/script/composite.php"&gt;composite&lt;/a&gt;&lt;/code&gt; (из &lt;a href="http://www.imagemagick.org/"&gt;ImageMagick&lt;/a&gt;) добавляет свой чанк с бекграундом — будте осторожны, не сломайте картинки (как сломал я)!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/CyDoIRKWVvE" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Блог Паши Корнилова</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/07/08/lusevers-blog" />
	<updated>2009-07-08T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/07/08/lusevers-blog</id>
	<content type="html">&lt;p&gt;У моего бывшего коллеги Паши Корнилова есть несколько необычных способностей. И не так давно он завел себе уютненький, чтобы периодически делиться священным.&lt;/p&gt;

&lt;p&gt;Рассказывает &lt;a href="http://lusever.livejournal.com/26082.html"&gt;про PNG&lt;/a&gt;. Являет &lt;a href="http://lusever.livejournal.com/16827.html"&gt;скрытое&lt;/a&gt;. &lt;a href="http://lusever.livejournal.com/15868.html"&gt;Выступает&lt;/a&gt;. Снова &lt;a href="http://lusever.livejournal.com/36921.html"&gt;выступает&lt;/a&gt;. Фигачит &lt;a href="http://lusever.livejournal.com/27150.html"&gt;Jotsky.com&lt;/a&gt;. Пишет &lt;a href="http://lusever.livejournal.com/44179.html"&gt;про меня&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;С днем рождения, Паша!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/hCiuE34usFU" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Весь интерфейс в одной картинке</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/07/08/all-in-wonder" />
	<updated>2009-07-08T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/07/08/all-in-wonder</id>
	<content type="html">&lt;p&gt;Верстальщики знают, что для быстрой загрузки сайта хорошо укладывать все маленькие картиночки в один большой файл: меньше запросов, плотнее сжатие, не надо предварительных загрузчиков и т.д.&lt;/p&gt;

&lt;p&gt;Все бы хорошо, если бы правка таких упакованных файлов не была жутким геморроем. Нужно же сначала сохранить измененную картинку в PNG, потом вставить новую версию в спрайт. Если она на свое место не помещается, то весь спрайт надо переделывать. А это значит, что и часть CSS тоже нужно обновлять: лазить по спрайту с нереальным зумом и вычислять позиции каждой картиночки. Кошмар.&lt;/p&gt;

&lt;p&gt;Потом еще добавляются муки с повторяющимися фонами. Они бывают вертикальные и горизонтальные, ограниченные и безграничные, полупрозрачные и нет. Бардак.&lt;/p&gt;

&lt;p&gt;Ну и напоследок. IE6 не поддерживает смещение полупрозрачных фонов, хакнутых через AlphaImageLoader. Значит, в нем все это вообще невозможно. Абзац.&lt;/p&gt;


&lt;p&gt;В общем случае эту задачу действительно решить не удается. Либо файл получится огроменным и не влезет в память, либо для шестого ИЕ придется изобретать новый движок отображения полупрозрачного пнг на экспрешнах (и все будет безумно тормозить), либо верстальщик повесится при следующем, со стороны незаметном, апгрейде дизайна. Правда, если постараться, то всех этих напастей можно избежать и превратить спрайты из запущенного геморроя, в панацею.&lt;/p&gt;

&lt;p&gt;Мы сговорились с дизайнером и решили не упростить интерфейс настолько, чтобы его верстка смогла влезть в один спрайт. Да, вот так просто взяли и поставили себе творческое ограничение. Спрайты были нам просто необходимы, так как сайт должен был моментально открываться, выдерживая нагрузку в несколько сотен человек в минуту на недорогом ВПСе. Вторая задача, которую призван был помочь решить спрайтинг — это легкая скинизация всего сайта или его части.&lt;/p&gt;

&lt;p&gt;Главную проблему (гемор при обновлении) решили хитро: перенесли весь дизайн иконок, кнопок, плашек и других элементов интерфейса со всеми слоями в один файл. Теперь ни дизайнеру, ни верстальщику не надо париться в поисках актуальной версии какой-нибудь зеленок кнопки с белыми буковками. Когда надо обновить спрайт мы просто сохраняем его как картинку. Затем, чтобы помочь верстальщику с вычислением позиций, мы разделили файл на квадраты по 50 пикселей. Каждый элемент спрайта занимает свой квадрат (или несколько квадратов) и картинка начинается в левом верхнем углу этого квадрата. Таким образом смещение фона можно узнать на глаз, вообще не открывая фотошоп.&lt;/p&gt;

&lt;p&gt;В решении проблемы повторяющихся фонов зашли с двух сторон. Во-первых, договорились не делать больших пространств со сложными, пересекающимися фонами. Во-вторых, для простых случаев заготовили картинки достаточной ширины, чтобы фон был заведомо больше любого элемента на сайте. В итоге обошлись одним длинным и двумя коротенькими фонами. Решение не универсальное, но нам подошло.&lt;/p&gt;

&lt;p&gt;С эксплорером обошлись наиболее жестоко: забили на него. Не совсем, конечно, но выкручиваться со скриптованием всех элементов ради полупрозрачного фона не стали. Просто сделали восьмибитную версию спрайта для шестого ИЕ. Получили отсутствие полупрозрачных фонов с градиентами, а также ожидаемые зазубренные края, как в девяностые. А чего еще ждут от этого браузера его пользователи? К моменту запуска сайта, шестой ИЕ составлял всего 12% аудитории, и мы решили, что они ничего не заметят, а мы сэкономим кучу времени и эмоций для более перспективных задач.&lt;/p&gt;

&lt;p&gt;И вот что получилось (темный фон для наглядности).&lt;/p&gt;

&lt;p&gt;24 бита (16,2 Кб): &lt;a href="i/icons.png"&gt;&lt;img src="i/icons.png" style="width:100%;background:#555;margin:1em 0"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;8 бит (7,3 Кб): &lt;a href="i/icons.8.png"&gt;&lt;img src="i/icons.8.png" style="width:100%;background:#555;margin:1em 0"/&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Все издевательства выносил сайт &lt;a href="http://moscow.retailstars.ru/form/zara/manager/"&gt;РитейлСтарса&lt;/a&gt;. Там всего две картинки: спрайт отвечает за интерфейс, а джипег — за фон. Все максимально облегчено. Даже фотки, кстати, грузятся по мере пролистывания.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD&lt;/strong&gt;: для решения задачи спрайтинга в лоб взрослые дяди сделали &lt;a href="http://www.stevesouders.com/spriteme/"&gt;мега-закладку&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/YTU6_Y7vZ38" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>IE в VirtualBox</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/06/10/ie678-in-virtualbox" />
	<updated>2009-06-10T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/06/10/ie678-in-virtualbox</id>
	<content type="html">&lt;p&gt;Для тестирования сайтов в ИЕ использую &lt;a href="http://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt;. На новых маках виртуалбокс просто летает (&lt;strong&gt;UPD&lt;/strong&gt;: на самых старых интеловских — тоже) и всего за три секунды восстанавливает и сохраняет состояние машины. То есть эксплорер стал просто еще одиним браузером для мака, в котором можно тестить все без тормозов.&lt;/p&gt;

&lt;p&gt;Были, правда, две маленькие неприятности: все виртуальные машины надо было запускать из специального окошка, и их иконки мало чем отличались друг от друга по виду. Яблоко-табом переключаться было неудобно: постоянно попадаешь не на ту машину, или — совсем мимо — в список машин. Кроме неудобств, такой метод плох еще и с эстетической стороны, так как в доке не было видно всех браузеров, в которых надо тестить перед пушем. И вообще.&lt;/p&gt;

&lt;p&gt;Решить сразу все две с половиной проблемы удалось простым трюком. Достаточно создать фейковые приложения, которые вызывают виртуалбокс с нужными параметрами командной строки. Да, и иконочки им приделать красивые.&lt;/p&gt;

&lt;p&gt;Вот что получилось:
&lt;img src="http://www.cmsbuilder.ru/blog/wp-content/uploads/2009/06/ies.png" alt="ies" title="ies" width="528" height="123" class="alignnone size-full wp-image-410" /&gt;.&lt;/p&gt;

&lt;p&gt;Да, после серьезных изменений в верстке, всегда прогоняю эти 12 браузеров (Safari 4, Chromium 3, Firefox 2, 3, 3.5, IE 6, 7, 8-8, 8-7, Opera 9.2, 9.5, 9.6, 10).  А если будут силы, то еще и в виндовых версиях хороших браузеров.&lt;/p&gt;

&lt;p&gt;Вот мой набор приложений с иконками: &lt;a href='http://www.cmsbuilder.ru/blog/wp-content/uploads/2009/06/virtualbox-ie678-apps.zip'&gt;virtualbox-ie678-apps.zip&lt;/a&gt;. Чтобы эти аппы запускали ваши виртуальные машины, необходимо в файликах &lt;code&gt;Contents/MacOS/VirtualBoxVM&lt;/code&gt; (простые bash-скрипты) заменить все UUID на ваши. Эти уиды можно посмотреть через &lt;code&gt;VBoxManage list vms&lt;/code&gt; или с помощью команды &lt;code&gt;ps -A | grep startvm&lt;/code&gt;, предварительно запустив интересующие машины.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/QHCQYbwf8x0" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Округляем</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/06/05/rounding" />
	<updated>2009-06-05T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/06/05/rounding</id>
	<content type="html">&lt;section&gt;
&lt;p&gt;Сколько способов отбросить дробную часть числа есть в яваскрипте?
&lt;pre&gt;&lt;code class="javascript"&gt;
var t = 36.6
Math.floor(t)
parseInt(t)
t - t % 1
t &amp; -1
+/^-?\d+/.exec(t)
+t.toFixed()
t &gt;&gt; 0
//=&gt; 36
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD 2009-09-23&lt;/strong&gt;: &lt;a href="http://video.yandex.ru/users/ya-events/view/27/"&gt;Александр Тармолов&lt;/a&gt; подзсказал метод &lt;code&gt;Number#toFixed()&lt;/code&gt;, огромное ему спасибо — буду тестить :)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPD 2010-01-14&lt;/strong&gt;: придумалось еще &lt;code&gt;t &gt;&gt; 0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Для отрицательного числа &lt;code&gt;floor()&lt;/code&gt; подходит не очень, а &lt;code&gt;&amp; -1&lt;/code&gt; — для чисел больше десяти миллионов. Но в том-то и прелесть, что всегда можно выбрать подходящий вариант.&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h&gt;Тестим&lt;/h&gt;
&lt;p&gt;Интересно, а как сильно тормозит каждый из вариантов?
&lt;pre&gt;&lt;code class="javascript"&gt;
(function(){
console.time('empty')
for (var i = 0.5; i &lt; 100000; i++);
console.timeEnd('empty')
})();
//=&gt; 1ms


(function(){
var f = Math.floor
console.time('floor')
for (var i = 0.5; i &lt; 100000; i++)
	f(i)
console.timeEnd('floor')
})();
//=&gt; 1ms (round() и ceil() дают то же)

(function(){
console.time('Math.floor:Math.ceil')
for (var i = 0.5; i &lt; 100000; i++)
	i &gt;= 0 ? Math.floor(i) : Math.ceil(i)
console.timeEnd('Math.floor:Math.ceil')
})();
//=&gt; 36ms


(function(){
var f = Math.floor, c = Math.ceil
console.time('var floor:ceil')
for (var i = 0.5; i &lt; 100000; i++)
	i &gt;= 0 ? f(i) : c(i)
console.timeEnd('var floor:ceil')
})();
//=&gt; 1ms


(function(){
console.time('parseInt')
for (var i = 0.5; i &lt; 100000; i++)
	parseInt(i)
console.timeEnd('parseInt')
})();
//=&gt; 162ms


(function(){
var f = parseInt
console.time('var parseInt')
for (var i = 0.5; i &lt; 100000; i++)
	f(i)
console.timeEnd('var parseInt')
})();
//=&gt; 1ms


(function(){
console.time('%')
for (var i = 0.5; i &lt; 100000; i++)
	i - i % 1
console.timeEnd('%')
})();
//=&gt; 3ms


(function(){
console.time('&amp;')
for (var i = 0.5; i &lt; 100000; i++)
	i &amp; -1
console.timeEnd('&amp;')
})();
//=&gt; 1ms


(function(){
console.time('&gt;&gt;')
for (var i = 0.5; i &lt; 100000; i++)
	i &gt;&gt; 0
console.timeEnd('&gt;&gt;')
})();
//=&gt; 1ms


(function(){
var rex = /^-?\d+/
console.time('toFixed')
for (var i = 0.5; i &lt; 100000; i++)
	+i.toFixed()
console.timeEnd('toFixed')
})();
//&gt;&gt;&gt; 83ms


(function(){
console.time('rex')
for (var i = 0.5; i &lt; 100000; i++)
	+/^-?\d+/.exec(i)
console.timeEnd('rex')
})();
//=&gt; 1100ms


(function(){
var rex = /^-?\d+/
console.time('cached rex')
for (var i = 0.5; i &lt; 100000; i++)
	+rex.exec(i)
console.timeEnd('cached rex')
})();
//=&gt; 1111ms
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;С регулярками просто пошутил. Кроме разброса по скорости округления, есть еще ограничения на значение округляемого числа. До миллиона все варианты отрабатывают хорошо, а дальше проверять пока не интересно.&lt;/p&gt;

&lt;p&gt;Ах, да, результаты для фаерфокса 3.5.7 на Core 2 Duo 2,66 ГГц 6 Мб.&lt;/p&gt;

&lt;/section&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/pUZyNoXjR3c" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Юнит-тесты для nginx</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/05/18/nginx-unit-tests" />
	<updated>2009-05-18T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/05/18/nginx-unit-tests</id>
	<content type="html">&lt;p&gt;&lt;strong&gt;UPD 2011-04-08&lt;/strong&gt;: Внимание! Всё это старье, и лежит здесь только для истории.&lt;/p&gt;
&lt;p&gt;Начал потихонечку составлять &lt;a href="http://github.com/kung-fu-tzu/nginx-unit-tests"&gt;набор юнит-тестов&lt;/a&gt; для энжи. Основные трудности возникают, когда надо подобраться к функции, не затрагивая другие. Юнит-тестирование кода, который не можешь изменить, всегда дело непростое. Но, как говориться, если гора не идет к Магомету :)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/kvrM2UooVas" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>df и du</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/05/07/df-vs-du" />
	<updated>2009-05-07T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/05/07/df-vs-du</id>
	<content type="html">&lt;p&gt;&lt;strong&gt;UPD&lt;/strong&gt;: Как сделует из объяснения citrin'а, место освободилось потому, что пока техподдержка тянула с ответом, сервак перезагрузился и высвободил безымянное пространство. Все починилось само, а вовсе не от того, что мастерхост что-то сделал. Они, конечно, не виноваты, и мне очень стыдно ;) Но даже не знаю, радоваться такому молчанию или нет :)&lt;/p&gt;

&lt;p&gt;Люблю поочищать сервак от всего лишнего. Делаю это с помощью ручного хождения по директориям и запуска в них &lt;code&gt;du -xh --max-depth=1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;И тут недавно прошерстил весь сервер, а места, по показаниям &lt;code&gt;df&lt;/code&gt;, освободилось — чуть. Запустил &lt;code&gt;du&lt;/code&gt; в корне: 1.4Гб. В свою очередь &lt;code&gt;df&lt;/code&gt; все так же показывает показывает 3.0Гб. Именно эти завышенные показания &lt;code&gt;df&lt;/code&gt; используются мастерхостом в качестве оценки занятого места на сервере.&lt;/p&gt;

&lt;p&gt;Написал им заявку, что, мол, различаются показания двух надежных утилит, и где искать пропавшие полтора гига. Подождал сутки и позвонил. Парень выслушал проблему, попросил подождать, а потом сказал, что напомнил каким-то дядям, что надо работать.&lt;/p&gt;

&lt;p&gt;Через час показания сошлись на 1.7Гб (за это время сервер действительно прибавил 300 метров), но от мастерхоста объянений так и не дождался. Интересно, кто скушал наше место? И если бы оно так совсем кончилось на сервере, нас бы отрубили или сначала удалили бы свою темную материю?&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/-b3GCYDUqFE" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Mac OS Leopard + launchd + VBoxHeadless</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/04/28/mac-os-leopard-launchd-vboxheadless" />
	<updated>2009-04-28T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/04/28/mac-os-leopard-launchd-vboxheadless</id>
	<content type="html">&lt;p&gt;Вот и мы решились, наконец, поиграть в виртуализацию. Поставили &lt;a href="http://www.ubuntu.com/getubuntu/downloadmirrors#bt"&gt;Ubuntu&lt;/a&gt; в &lt;a href="http://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt; на леопарде, а в убунте настроили &lt;a href="http://www.openvz.org/"&gt;OpenVZ&lt;/a&gt;. Все это крутится на мак мини с 4-мя гигами оперативы. Мак тут, конечно, не особо нужен, но покупать здоровенный сервак или проводастый писюк совсем не хотелось.&lt;/p&gt;

&lt;p&gt;Все это добро (Mac mini MB463LL/A, Ubuntu 8.04, VirtualBox 2.2, OpenVZ kernel 2.6.24-23-openvz) завелось с полоборота. Линукс совершенно не напрягает мак мини своим присутствием. Из отведенного гига для виртуальной машины реально расходуется чуть больше 300. Мак летает, линукс летает, контейнеры тоже летают. Все прекрасно. Пока не настает время перезагружаться.&lt;/p&gt;

&lt;p&gt;Ясное дело, после загрузки виртуалбокс не запускает никакие машины. Это грустно, но не смертельно. В комплекте виртуалбокса есть консольная утилита для запуска виртальных машин без привязки к пользовательскому интерфейсу. Пользоваться ей очень просто: &lt;code&gt;VBoxHeadless -startvm $VMID&lt;/code&gt;. Идентификатор виртуальной машины узнать нетрудно: эта команда покажет подробный список машин &lt;code&gt;VBoxManage list vms&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Но изюминка этой статьи вовсе не в восторгах от современных технологий виртуализации. Дело в том, что запускать виртуалбокс при старте и выключать его при перезагрузке — задача нетривиальная.&lt;/p&gt;

&lt;p&gt;Для начала скажу, что мы запускаем VBoxHeadless с помощью встроенного в макос &lt;a href="http://developer.apple.com/MacOsX/launchd.html"&gt;launchd&lt;/a&gt;. Он умеет перезапускать процесс, если тот накернился. launchd умеет выдерживать паузы, запускать задания с определнным интервалом, при подключении диска. Да он, вообще, много чего умеет. В этом ему близок полюбившийся мне &lt;a href="http://upstart.ubuntu.com/"&gt;UpStart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Чтобы виртуальная машина запускалась при старте, необходимо положить следующий скрипт в папку &lt;code&gt;/Library/LaunchDaemons&lt;/code&gt;:
&lt;pre&gt;&lt;code class="xml"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;
&amp;lt;plist version="1.0"&amp;gt;
&amp;lt;dict&amp;gt;
  &amp;lt;key&amp;gt;RunAtLoad&amp;lt;/key&amp;gt;
  &amp;lt;true/&amp;gt;
  &amp;lt;key&amp;gt;KeepAlive&amp;lt;/key&amp;gt;
  &amp;lt;dict&amp;gt;
    &amp;lt;key&amp;gt;SuccessfulExit&amp;lt;/key&amp;gt;
    &amp;lt;false/&amp;gt;
  &amp;lt;/dict&amp;gt;
  &amp;lt;key&amp;gt;Label&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;ubuntu-openvz&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;ProgramArguments&amp;lt;/key&amp;gt;
  &amp;lt;array&amp;gt;
    &amp;lt;string&amp;gt;VBoxHeadless&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;-startvm&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX&amp;lt;/string&amp;gt;
  &amp;lt;/array&amp;gt;
  &amp;lt;key&amp;gt;Nice&amp;lt;/key&amp;gt;
  &amp;lt;integer&amp;gt;3&amp;lt;/integer&amp;gt;
  &amp;lt;key&amp;gt;GroupName&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;staff&amp;lt;/string&amp;gt;
  &amp;lt;key&amp;gt;UserName&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;someuser&amp;lt;/string&amp;gt;
&amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
Вместо иксов нужно подставить ид машины, которую будем запускать. И еще надо сменить владельца этого файла на рута. Назвать файл можно как вам удобно, но принят формат домен.задача. Например, &lt;code&gt;ru.company.virtualbox.ubuntu-vz&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;А проблема со свзякой ланчд+виртуалбокс в том, что при перезагрузке ланчд шлет процессу виртуалбокса простой &lt;code&gt;SIGTERM&lt;/code&gt;. После чего виртуальная машина ресетится и отключается. Состояние линукса внутри виртуалбокса не сохраняется никаким образом. Линукс даже не узнает, что машина скоро погаснет.&lt;/p&gt;

&lt;p&gt;В документации так прямо и написано: не умеешь ловить сигтерм — не лезь в ланчд. И как раз обучение виртуалбокса правильно обрабатывать сигтерм и есть та задача, которую решать было интересно.&lt;/p&gt;

&lt;p&gt;Сначала заменим в ланчдемонском конфиге эти строки:
&lt;pre&gt;&lt;code class="xml"&gt;  &amp;lt;array&amp;gt;
    &amp;lt;string&amp;gt;VBoxHeadless&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;-startvm&amp;lt;/string&amp;gt;
    &amp;lt;string&amp;gt;XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX&amp;lt;/string&amp;gt;
  &amp;lt;/array&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
на эти:
&lt;pre&gt;&lt;code class="xml"&gt;  &amp;lt;array&amp;gt;
      &amp;lt;string&amp;gt;/usr/local/bin/vboxheadless-wrapper&amp;lt;/string&amp;gt;
      &amp;lt;string&amp;gt;b25ac594-9b5e-4ac7-bc38-c105947bf2ad&amp;lt;/string&amp;gt;
  &amp;lt;/array&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Внутри &lt;code&gt;vboxheadless-wrapper&lt;/code&gt; написано следующее:
&lt;pre&gt;&lt;code class="perl"&gt;#!/usr/bin/env perl
use strict;

my ($vm) = @ARGV;
our $stopping = 0;

$SIG{TERM} = sub { stop($vm); };

start($vm);
exit 0;


sub start
{
	my $vm = shift;
	# waiting for VB drivers to load
	sleep 1 while scalar(grep { /org.virtualbox/ } `/usr/sbin/kextstat`) &lt; 3;
	my $res = system "/usr/bin/VBoxHeadless -startvm $vm";
	# sleeping until exit()
	unless ($res) { sleep while 1 };
}

sub stop
{
	return if $stopping;
	$stopping = 1;
	my $vm = shift;
	exit system "/usr/bin/VBoxManage controlvm $vm savestate";
}
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Этот код делает вот что. Сначала запускает виртуалбокс и засыпает в вызове &lt;code&gt;system&lt;/code&gt;. Он, скорее всего, не проснется раньше времени, но на всякий случай следующей строкой мы подстраховываемся. Далее, машина какое-то время работает. Потом кто-то перезагружает макос и скрипту от ланчд приходит сигтерм. Скрипт просит виртуалбокс сохранить состояние машины на диск. После этого процесс виртуальной машины успешно завершается и скрипт выходит с кодом 0.&lt;/p&gt;

&lt;p&gt;Все это на самом деле очень просто. Но эта конструкция все равно не заработала. А из-за того, что ланчд запускал наш скрипт еще до того, как ядро макоса загружало дрова виртуалбокса. Как раз от преждевременного запуска нас спасает строка &lt;code&gt;sleep 1 while scalar(grep { /org.virtualbox/ } `/usr/sbin/kextstat`) &lt; 3;&lt;/code&gt;. Тут перл ждет, пока не загрузится хотя бы три драйвера с именем, содержащим &lt;/code&gt;&lt;code&gt;org.virtualbox&lt;/code&gt;. В моем случае это были следующие кексты: &lt;code&gt;org.virtualbox.kext.VBoxDrv&lt;/code&gt;, &lt;code&gt;org.virtualbox.kext.VBoxUSB&lt;/code&gt;, &lt;code&gt;org.virtualbox.kext.VBoxNetFlt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;После этого, виртуальной машиной можно управлять с помощью ланчд:
&lt;pre&gt;&lt;code class="bash"&gt;launchctl start ubuntu-openvz
launchctl stop ubuntu-openvz
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Эх, люблю потанцевать! Особенно проникновенными считаю танцы с бубном :)&lt;/p&gt;

&lt;p&gt;Самый кайф в том, что можно подключиться по ссш, запустить, например, &lt;code&gt;ping&lt;/code&gt; или &lt;code&gt;ab&lt;/code&gt;, перезагрузить железку, и процесс пойдет дальше. При этом &lt;code&gt;ab&lt;/code&gt;, пережив перезагрзку компа, не сообщил ни об одной ошибке. Состояние виртуальной машины сохраняется за 4 секунды. Восстанавливается — за 3. Восстановить виртуальную машину можно уже на другом компе, не потеряв ни одного соединения. Горячий перенос, кстати, умеет как сам виртуалбокс, так и опенвз внутри него :)&lt;/p&gt;

&lt;p&gt;Удачной виртуализации!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/zghcsJluJr0" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Стокгольм</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/04/23/stockholm" />
	<updated>2009-04-23T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/04/23/stockholm</id>
	<content type="html">&lt;p&gt;На сайте мастерхоста написано, что у них канал до Стокгольма шириной в 10 Гбит/с. Для сравнения скорость связи с Голден-Телекомом всего 1 Гбит/с. То есть у них канал до Швеции (из Зеленограда) в десять раз шире, чем до отдельно взятого провайдера в Москве. В сумме-то все равно шире до Москвы, но до посетителя сайта трафик, скорее всего, пойдет по проводам только одного из провайдеров.&lt;/p&gt;

&lt;p&gt;Пошел проверить, как открывается &lt;a href="http://www.retn.net/?lang=r"&gt;сайт их партнера&lt;/a&gt; в Швеции, и… моментально! Если они не схитрили, поставив зеркало у себя в датацентре, то это просто прекрасно (тот, кто хостился в Америке, поймет).&lt;/p&gt;

&lt;p&gt;Это заставило провести еще более глубокое расследование (sic!) и протестить скорость из дома (net-by-net/utech) и из офиса (RMT) до этого чудо-сервера в Швеции. Получилось 1,8 Мбит/с из дома и 5,3 Мбит/с из офиса. До сервера програмики для сравнения: 6,4 Мбит/с и 3,5 Мбит/с. То есть из офиса быстрее связь с далеким шведским сервером, чем с сервером в подмосковном мастерхосте.&lt;/p&gt;

&lt;p&gt;После этого, решил протестить линк из самого мастерхоста. С сервера програмики (VPS) Швеция открывается просто молниеносно: 35 Мбит/с (сам с себя сервер сливает на скорости 200 Мбит/с).&lt;/p&gt;

&lt;p&gt;Такие скорости вполне подходят для несильно нагруженных сайтов. А у нас вдобавок все сайты хорошо сверстаны и кеширование настроено. Посмотрим, что покажут есты через месяц-другой. Если скорость поднимется еще немного, то можно будет, наконец, подумать о Голландском хостинге от мастерхоста за небольшие деньги и на приличной скорости.&lt;/p&gt;

&lt;p&gt;Такая возможность сама по себе очень романтична: мы живем в России, они — в Голландии, и мы можем делать дело бок о бок, не замечая границ, благодаря простому проводу. Мир становится меньше. Скорей бы уж и мгновенная телепортация изобрелась…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD&lt;/strong&gt;: Ах, да. Тестил в час ночи по Москве.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/RJeWPhYM0OM" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Дырявая песочница</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/04/09/buggy-sandbox" />
	<updated>2009-04-09T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/04/09/buggy-sandbox</id>
	<content type="html">&lt;p&gt;SpiderMonkey, который не перестает удивлять, давно умеет запускать произвольный код изолированно от остального мира, внутри так называемых песочниц. Эта возможность завораживает своей простотой и удобством. Например, можно по-честному разделить свое большое приложение на ядро и модули. Или, если сойти с ума, разрешить контент-менеджерам писать простенький код в шаблонах. Можно даже чужой код загрузить издалека и запускать, но это уже чересчур.&lt;/p&gt;

&lt;p&gt;Добиться такой изоляции несложно. Достаточно вызвать &lt;code&gt;evalcx()&lt;/code&gt;, передав первым параметром код, который нужно выполнить под присмотром. Функция вернет результат выполнения, как обычный &lt;code&gt;eval()&lt;/code&gt;. Такой способ подходит, если созданное в результате окружение нас не интересует. Можно, конечно, вернуть из кода глобальный объект и возиться уже с ним. Но это не наш метод.&lt;/p&gt;

&lt;p&gt;Для того, чтобы создать изолированное пространство имен, нужно вызвать &lt;code&gt;evalcx()&lt;/code&gt; с пустой строкой в качестве первого параметра: &lt;code&gt;evalcx("")&lt;/code&gt;. Так мы получим полностью независимое окружение со своим набором глобальных переменных и классов. Полученный таким хитрым способом объект нужно передавать вторым параметром в &lt;code&gt;evalcx()&lt;/code&gt;, а впоследствии использовать повторно или с пользой проанализировать.&lt;/p&gt;

&lt;p&gt;Если выполнить &lt;code&gt;evalcx('String.abc = 123', {})&lt;/code&gt;, то код сможет изменить наш объект String (или любой другой), а этого делать не следует.&lt;/p&gt;

&lt;p&gt;Штука хитрая и полезная, но, конечно, не панацея. Так что использовать для запуска чего-то чужого не рекомендую.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/R-A-uqYkGAQ" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Конкуренты</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/04/03/rivals" />
	<updated>2009-04-03T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/04/03/rivals</id>
	<content type="html">&lt;p&gt;За последние три месяца обнаружил на просторах интернета два проекта по снабжению нжинкса яваскриптом. &lt;a href="http://github.com/chrisfarms/nginx_ecmascript_module/"&gt;Один&lt;/a&gt; из энтузиастов пошел схожим путем и решил встроить SpiderMonkey. &lt;a href="http://code.google.com/p/ngxv8/"&gt;Другому&lt;/a&gt;, видимо, ближе C++, и он решил обратиться к прекрасному созданию гугла — движку V8. Оба разработчика скоро разочаровались в своих проектах (насколько можно судить по интенсивности коммитов). Но мне важным показалось другое.&lt;/p&gt;

&lt;p&gt;Задачей, которую поставили себе эти ребята, стала поддержка запуска нжинксом яваскриптовых файлов (например, встроенных в HTML-код). В принципе, это было бы интересно, если бы нжинкс уже не поддерживал перл с его прорвой библиотек. А найти применение клону ПХП, но на яваскрипте (учитывая, что серверные программеры его просто ненавидят) и без библиотек не ручаюсь совсем.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;UPD&lt;/b&gt;: Был неправ, исправляюсь. Второй парень (японец, скрывающийся под псевдонимом rykomats) оказался не так прост. Он тоже сделал небольшую обертку к структуре запроса, и передает пришедший запрос в функцию из заранее загруженного яваскрипта (на сколько можно понять этот кошмарный C++). Такая конфигурация работает быстро и понятно.&lt;/p&gt;

&lt;p&gt;Согласен, обе технологии (spidermonkey &amp;amp; nginx) просто сахарные, и, полюбив их по отдельности, начинаешь подумывать об объединении возлюбленных. Но зачем же все так пошло?&lt;/p&gt;

&lt;p&gt;Кстати говоря, если сделать на яваскрипте не обработчик (хендлер) локейшна, а фильтр, то можно распрекрасно и быстро реализовать, например, легко расширяемый клон &lt;a href="http://sysoev.ru/nginx/docs/http/ngx_http_ssi_module.html"&gt;SSI&lt;/a&gt;, но на яваскрипте. А можно встроить &lt;a href="http://dean.edwards.name/packer/"&gt;пакер&lt;/a&gt; Дина Эдвардса, и минифаить яваскрипт налету. Можно даже простенький шаблонизатор для джейсона накатать. Но вот делать простую запускалку яваскрипта — этого понять невозможно.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;UPD2&lt;/b&gt;: Вот еще дядька американец &lt;a href="http://github.com/ngerakines/erlang_nginx"&gt;встраивает эрланг&lt;/a&gt;. Хакеры нжинкса всех стран, обединяйтесь! :)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/7yi8phvBjoc" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Сказочно медленный className</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/04/03/fabulously-slow-classname" />
	<updated>2009-04-03T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/04/03/fabulously-slow-classname</id>
	<content type="html">&lt;p&gt;Как выяснилось, лучше выполнить кучу предварительных проверок в яваскрипте, и только потом, при крайней необходимости, трогать &lt;code&gt;className&lt;/code&gt;. Дело в том, что это свойство затратно не только записать, но и прочитать. Тормозить будет даже в том случае, когда фактическое значение не меняется (&lt;code&gt;node.className = node.className&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Почему так происходит, легко понять из кода, которым обвязывается каждое обращение к свойству оборачиваемого объекта (в данном случае обернут элемент DOM-дерева). Такая обвязка, как правило, состоит из внушительного куска кода, который выясняет, какое именно свойство вызвано, а потом ищет, как это свойство прочитать или записать. Поиск чаще используется простой, перебором. Затем, управление передается дальше. Наверняка, это будет вызов некоторого метода. Метод сделает что-то свое (возможно, просто возвратит строку, а может и другие методы повызывает). И напоследок, результат работы метода необходимо обернуть в подходящий яваскриптовый объект. В нашем случае это строка. А строку еще надо создать, выделить память под символы (предварительно посчитав, сколько байт нужно), скопировать в нее данные, вернувшиеся из метода (почти наверняка, перекодировав в UTF-16), и только потом возвратить управление яваскрипту.&lt;/p&gt;

&lt;p&gt;Пример грустный от того, что в нем перечислена куча действий, от которых можно отказаться. Однако, отказаться от них очень непросто, и это потребует от разработчиков браузера много терпения и желания сделать людям быстро. Такие оптимизации как раз ведут к тому, что называется быстрым ДОМом (&lt;a href="http://www.google.com/search?q=faster+dom+site%3Aajaxian.com"&gt;faster DOM&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Мы можем немного помочь разработчикам браузеров, если научим свою страничку кешировать значение className. Таким образом, этот код в фаерфоксе будет быстрее:
&lt;pre&gt;&lt;code class="javascript"&gt;
cache = 'a'
cache = className = cache + ' b'
&lt;/code&gt;&lt;/pre&gt;
чем этот:
&lt;pre&gt;&lt;code class="javascript"&gt;
className += ' b'
&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Когда появится свободное время, дополню пост парой бенчмарков, а пока, остается верить мне на слово ;)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/0M4WyKO08oM" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Идет кругом голова…</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/03/24/head-is-spinning" />
	<updated>2009-03-24T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/03/24/head-is-spinning</id>
	<content type="html">&lt;ul&gt;
&lt;li&gt;IE 6, 7, 8 как 8, 8 как 7.&lt;/li&gt;
&lt;li&gt;Фаерфокс 2, 3, 3.5.&lt;/li&gt;
&lt;li&gt;Опера 9.2, 9.5, 9.6, 10.0.&lt;/li&gt;
&lt;li&gt;Сафари 3, 4, мобайл.&lt;/li&gt;
&lt;li&gt;Хром 1, 2.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;И умножим на Виндоуз, Линукс и Мак Ос. И добавим Виндоуз Мобайл, айФон Ос, Симбиан и Андроид.&lt;/p&gt;

&lt;p&gt;Искренне желаю удачи Джону &lt;a href="http://ejohn.org/blog/javascript-testing-does-not-scale/"&gt;в рубке гордиева узла&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/oUKrVtm2fwo" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Вредный гиф</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/03/06/malicious-gif" />
	<updated>2009-03-06T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/03/06/malicious-gif</id>
	<content type="html">&lt;p&gt;В нашем новом проекте очень много графики, а именно больших фотографий. Раньше так делать было нельзя — большие изображения магически тормозили всю страницу, но теперь эти времена ушли. Уши, но не для винды. Если задать фоном у достаточно большой картинки маленький анимационный, то под виндой одно ее спокойное лицезрение будет стоить вам 100% ресурсов процессора. У нас такое наблюдалось в IE7 и FF3.&lt;/p&gt;

&lt;p&gt;— Что-то у тебя сайт медленно работает!&lt;br/&gt;
— Так это картинки тормозят, картинки…&lt;/p&gt;


&lt;p&gt;Гиф тот показывал индикатор загрузки — картинки-то большие ;)&lt;/p&gt;

&lt;p&gt;&lt;b&gt;UPD&lt;/b&gt;: А картинки тормозили потому, что размеры для них были указаны неверно. Браузерам на виндоусе приходилось эти картинки ресамплить налету, от чего все и тормозило. На маке странички отображались прекрасно.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/NwGpylJk_Us" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>GitHub — моё всё</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/03/01/github-is-my-entire-all" />
	<updated>2009-03-01T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/03/01/github-is-my-entire-all</id>
	<content type="html">&lt;p&gt;Вместо выкладывания редких и скучных тар-гзипов с альфаверсиями, решил залить проекты из репозитория &lt;a href="http://www.inshaker.ru/"&gt;нашей чудесной компании&lt;/a&gt; прямо &lt;a href="http://github.com/kung-fu-tzu/"&gt;на GitHub&lt;/a&gt;. Собственно, там лежит: &lt;a href="http://github.com/kung-fu-tzu/highlight.js.xpi/"&gt;расширение-обертка&lt;/a&gt; вокруг &lt;a href="http://softwaremaniacs.org/soft/highlight/"&gt;highlight.js&lt;/a&gt;; шуточная неработающяя программа конвертирующая руби в яваскрипт (ну не удержался); и горячо мною любимый &lt;a href="http://github.com/kung-fu-tzu/ngx_http_js_module/"&gt;модуль для нжинкса&lt;/a&gt; с яваскриптом.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD 2011-01-10&lt;/strong&gt;: теперь и весь блог &lt;a href="https://github.com/kung-fu-tzu/kung-fu-tzu.github.com"&gt;лежит на гитхабе&lt;/a&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/ZPeA3hbvGfU" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>О минифае</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/02/09/about-minify" />
	<updated>2009-02-09T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/02/09/about-minify</id>
	<content type="html">&lt;p&gt;Предварительное сжатие яваскрипта хорошо по двум причинам: во-первых, файл теряет в весе, а во-вторых, пожатый файл проще парсить и исполнять яваскриптовому движку. Конечно, если минифаить «неэффективный» код, то пожмется он незначительно, да и парсить его остается так же сложно.&lt;/p&gt;

&lt;p&gt;Чтобы избежать всех этих неэффективностей, можно выработать такой стиль программирования, при котором код и жмется хорошо, и исполняется быстро ;)&lt;/p&gt;

&lt;p&gt;
Можно начать с кеширования значений, которые часто используются в функции, но их получение из внешнего мира затратно. Здесь налицо выгода при сжатии: минифаер поймет, что переменная локальная и сожмет ее имя в один символ. Интерпретатор при исполнении такого кода  будет точно знать, что используется локальная копия далекого объекта. А локальная копия гораздо быстрее ветвистых поисков в объектах и массивах. Например,
&lt;pre&gt;&lt;code class="javascript"&gt;
var name = this.conf.name, defs = this.defs
this.save(defs[name] || (defs[name] = {}))
&lt;/code&gt;&lt;/pre&gt;
немного лучше чем
&lt;pre&gt;&lt;code class="javascript"&gt;
this.save(this.defs[this.conf.name] || (this.defs[this.conf.name] = {}))
&lt;/code&gt;&lt;/pre&gt;
Первое пожмется в
&lt;pre&gt;&lt;code class="javascript"&gt;
var n=this.conf.name,d=this.defs;this.save(d[n]||(d[n]={}))
&lt;/code&gt;&lt;/pre&gt;
а второе в
&lt;pre&gt;&lt;code class="javascript"&gt;
this.save(this.defs[this.conf.name]||(this.defs[this.conf.name]={}))
&lt;/code&gt;&lt;/pre&gt;
то есть не пожмется вообще. Можно еще &lt;code&gt;this&lt;/code&gt; закешировать, и будет еще на два байта меньше:
&lt;pre&gt;&lt;code class="javascript"&gt;
var t=this,n=t.conf.name,d=t.defs;t.save(d[n]||(d[n]={}))
&lt;/code&gt;&lt;/pre&gt;
но это мы предоставим гзипу — он автоматический :)
&lt;/p&gt;

&lt;p&gt;
Кеширование переменных гораздо лучше копипаста, встречающегося на вольных просторах интернета. Типа этого:
&lt;pre&gt;&lt;code class="javascript"&gt;
this.conf.name.first = "Vasya"
this.conf.name.second = "Poupkine"
&lt;/code&gt;&lt;/pre&gt;
преобразуется в
&lt;pre&gt;&lt;code class="javascript"&gt;
var name = this.conf.name
name.first = "Vasya"
name.second = "Poupkine"
&lt;/code&gt;&lt;/pre&gt;
и, соответственно, сжимается в
&lt;pre&gt;&lt;code class="javascript"&gt;
var n=this.conf.name;n.first="Vasya";n.second="Poupkine"
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;
Подобные оптимизации совсем не обязательны и могут отвлекать от процесса творческого программирования. Но если привыкнуть к ним и начать думать как оптимизатор кода (в яваскрипте же его нет), то писать такой код можно уже без напряжения. Если ваш код будет использоваться часто и интенсивно, но изменен будет нескоро, то такая оптимизация как раз для вас. Можно, конечно, подождать многопроходного компилятора с оптимизатором и кроссбраузерного байткода, да когда они еще будут-то…
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/60kWu6tsYa8" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Такие разные removeClassName'ы</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/02/02/much-different-removeclassnames" />
	<updated>2009-02-02T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/02/02/much-different-removeclassnames</id>
	<content type="html">&lt;p&gt;
Казалось бы, что может быть проще, чем написать функцию по удалению имени класса из свойства ноды. Наверное из-за того, что задачка легкая, ее решают все. Только все ее решают по-разному.
&lt;/p&gt;

&lt;p&gt;
Вот как у &lt;a href="http://github.com/sstephenson/prototype/blob/34ee2078f8c61272b69bc9166fd0bf7f94dde5fc/src/dom.js#L336"&gt;прототайпа 1.6&lt;/a&gt;:
&lt;pre&gt;&lt;code class="javascript"&gt;
removeClassName: function(element, className) {
  if (!(element = $(element))) return;
  element.className = element.className.replace(
    new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
  return element;
},
&lt;/code&gt;&lt;/pre&gt;
Тут мы видим два дополнительных вызова внешних функций: &lt;code&gt;$(element)&lt;/code&gt; и &lt;code&gt;.strip()&lt;/code&gt;. А еще строка собирается для каждого обращения. И еще регексп компилится каждый раз и при выполнении запоминает и заполняет &lt;code&gt;$1&lt;/code&gt; и &lt;code&gt;$2&lt;/code&gt;. А зачем?
&lt;/p&gt;

&lt;p&gt;
А вот так у джейквери:
&lt;pre&gt;&lt;code class="javascript"&gt;
remove: function( elem, classNames ) {
	if (elem.nodeType == 1)
		elem.className = classNames !== undefined ?
			jQuery.grep(elem.className.split(/\s+/), function(className){
				return !jQuery.className.has( classNames, className );
			}).join(" ") :
			"";
},
&lt;/code&gt;&lt;/pre&gt;
Этот код пожестче. Здесь и &lt;code&gt;split&lt;/code&gt; с &lt;code&gt;join&lt;/code&gt;'ом, и поиск &lt;code&gt;className&lt;/code&gt; в &lt;code&gt;jQuery&lt;/code&gt;, а в &lt;code&gt;className&lt;/code&gt;'е потом поиск &lt;code&gt;has&lt;/code&gt;. Ну, а про вызов замыкания на каждый имеющийся класс вообще умолчим. Задача укорачивания строки решена максимально помпезно.
&lt;/p&gt;

&lt;p&gt;
В обеих библиотеках эти функции оборачиваются во что-то еще. Так, в коде джейквери прямым текстом написано «internal only, use removeClass("class")». Такой подход действительно полезен, когда к библиотеке пишут плагины и расширения. Хуков можно надобавлять, потрейсить, попрофайлить или еще что-нибудь побочное прикрепить.
&lt;/p&gt;

&lt;p&gt;
На наших сайтах добавление и удаление классов происходит очень часто, а иногда очень-очень часто для целой кучи элементов (где именно? — как зарелизим — покажу; &lt;strong&gt;UPD 03.01.2009&lt;/strong&gt;: &lt;a href="http://moscow.retailstars.ru/"&gt;показываю&lt;/a&gt;). Когда пишешь такой код, не хочется думать о том, что вызов &lt;code&gt;removeClassName()&lt;/code&gt; может быть дорогим. Хочется простой и быстрый метод. Например, такой:
&lt;pre&gt;&lt;code class="javascript"&gt;
var rexCache = {}, R = RegExp
Element.prototype.removeClassName = function (cn)
{
	return this.className = this.className.replace
	(
		rexCache[cn] ||
		(rexCache[cn] = new R('(?:^| +)(?:' + cn + '(?:$| +))+', 'g')), ' '
	)
	.replace(/^\s+|\s+$/g, '')
}
&lt;/code&gt;&lt;/pre&gt;
Тут мы убрали все, что смогли, и ничего лишнего не добавили. Даже кеш, и тот закеширован в локальной переменной. Ах да: вы только, пожалуйста, не используйте эту функцию, не протестировав ее хорошенько на своих проектах. То, что она работает у нас, еще ничего не значит. Мы же не Резиг, и не мощная тусовка рельсовиков. Мы просто &lt;a href="http://www.inshaker.ru/"&gt;Иншейкер&lt;/a&gt; (а как же без ссылки на мой любимый коктейльный сайт).
&lt;/p&gt;

&lt;p&gt;
Понимаю, в новых браузерах и вызовы методов быстрые, и встроенное кеширование регэкспов есть, и строки там молниеносные, и вообще у них уже джит на дворе. Так что париться о таких вещах как-то неудобно. Но меня не покидает чувство, что усложнять не нужно.
&lt;/p&gt;

&lt;p&gt;
Вот так не по-доброму получилось. И это при том, что я фанат всего что делает Резиг, и обожаю рельсы целиком, вместе с прототайпом.
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/7Ssoc3JTnV4" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>JSON для Ruby</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/01/25/json-for-ruby" />
	<updated>2009-01-25T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/01/25/json-for-ruby</id>
	<content type="html">&lt;p&gt;Для руби есть три библиотеки работы с &lt;a href="http://www.json.org/"&gt;JSON&lt;/a&gt;. Быстрая &lt;a href="http://json.rubyforge.org/"&gt;JSON&lt;/a&gt;, в которой критичные по производительности части написаны на C. Медленная &lt;a href="http://sourceforge.net/projects/json"&gt;json/objects&lt;/a&gt; целиком на руби. А еще есть &lt;a href="http://api.rubyonrails.com/classes/ActiveSupport/JSON.html"&gt;ActiveSupport::JSON&lt;/a&gt;, которая, видимо, является развитием «json/objects» в мире рельс. Первая библиотека действительно в несколько раз шустрее остальных, но она не умеет отключать перевод юникодных символов (то есть всех наших с вами родных букв) в последовательности &lt;code&gt;\uXXXX&lt;/code&gt;. Вторая библиотека по умолчанию их не экранирует, но тормозит. А третья экранирует, тормозит, и это тоже не отключается. В общем поддержка JSON в руби, на мой вкус, сыровата.&lt;/p&gt;

&lt;p&gt;Меньше всех не понравилась первая библиотека. Она хотя бы шустрая. А отключить экранирование помог патч (&lt;strong&gt;UPD 2011-01-10&lt;/strong&gt;: который я посеял).&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/ZCk9e9dIwZg" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Главное — не дублировать</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/01/22/dont-duplicate" />
	<updated>2009-01-22T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/01/22/dont-duplicate</id>
	<content type="html">&lt;h3&gt;Издалека&lt;/h3&gt;

&lt;p&gt;
Перед тем как углубиться в подробности, хочу рассказать, как оно все устроено с высоты птичьего полета.
&lt;/p&gt;

&lt;p&gt;
У нас есть исходник нжинкса, который, строго говоря, доступен только для чтения. Но это не беда, так как Игорь выбрал для нжинкса микроядерную архитектуру, которая ведет к постоянному развитию API. Таким образом практически вся функциональность нжинкса разделена между модулями. Эван Миллер сравнивает нжинкс с Бэтменом, а модули нжинкса с поясом Бэтмена, в котором всегда все есть.
&lt;/p&gt;

&lt;p&gt;
Так, например, все фильтры являются самостоятельными модулями. Есть относительно простые модули, например, ngx_http_addition_filter_module, сложные, как ngx_http_gzip_filter_module и модули сравнимые по сложности со всем нжинксом, это, конечно, ngx_http_ssi_filter_module.
&lt;/p&gt;

&lt;p&gt;
С другой стороны выступает яваскриптовый движок. Мозила его обкатала на множестве своих проектов и его API, на мой взгляд, тоже хорошо спроектирован. А также написано достаточно документации для того, чтобы быстро и удобно добавить скриптов в любое большое приложение.
&lt;/p&gt;

&lt;p&gt;
Что надо знать, перед тем, как встаивать скриптовый движок? В принципе, ничего кроме того, какие именно задачи вы хотите перенести на скрипты. Если это ресурсоемкие вычисления, или работа с большим количеством логики поверх сложных абстракций, или большие и очень большие куски бизнес-логики, или еще что-то большущее, то скриптовый движок тут не поможет. Яваскрипт не сможет показать себя с хорошей стороны. Все функции, которые так или иначе связаны со скриптами, будут потихоньку начинать тупить. Приложение за счет гибкости языка разрастется очень быстро, и управлять им станет заметно сложнее, чем казалось вначале. Для больших проектов лучше подходят строгие и очень строгие языки, такие как Java, С++ или С. Они станут мешать вам сразу после начала работы, и вы привыкнете к этому. А яваскрипт (а так же руби, питон, перл и другие) не станут этого делать, и после будет очень обидно получать синяки и шишки работая с уже полюбившемся инструментом. Для быстрого нжинкса важно, чтобы яваскрипт тоже оставался быстрым и легким. Не мне вас учить выбирать технологи, но предупрежден — значит вооружен!
&lt;/p&gt;


&lt;h3&gt;К делу&lt;/h3&gt;

&lt;p&gt;
Итак, выберем конкретные задачи, применительно к нжинксу.
&lt;ul&gt;
	&lt;li&gt;Для начала было бы неплохо генерить простые ответы на простые запросы. Без парсинга данных форм, принятия файлов, и уж тем более без запуска CGI-скриптов или обращений к базе. Здесь SpiderMonkey нас сам и ограничит, так как модулей промышленного качества для него очень мало, и вся доступная функциональность будет предоставлена нжинксом.&lt;/li&gt;

	&lt;li&gt;На второе подойдет работа с локальными файлами, чтобы иметь возможность что-нибудь сохранить. Правда, это не очень здорово, так как асинхронно работать с диском сложно. Гугловский браузер Chrome, например выносит весь файловый ввод/вывод в отдельный поток, чтобы интерфейс не тупил, и все обращения к диску работают через него.&lt;/li&gt;

	&lt;li&gt;В качестве компота, будут подзапросы. Собственно говоря, это самый оптимальный способ получать содержимое файлов, так как нжинкс умеет кешировать дескрипторы файлов и читает их максимально быстро. Все это не надо изобретать для использования внутри яваскрипта, а достаточно сделать обертку для функций нжинска.&lt;/li&gt;

	&lt;li&gt;Для обеспечения связи с внешним миром, пришлось еще учить SpiderMonkey читать переменные окружения. Это оказалось очень просто, так как решение идет вместе с исходниками яваскриптового интерпретатора.&lt;/li&gt;
&lt;/ul&gt;
Для начала этого вполне достаточно. Основная идея ясна — не дублировать функционал нжинска, а наоборот, описать его в терминах яваскрипта.
&lt;/p&gt;


&lt;p&gt;Все это описывать буду частями (&lt;a href="http://kung-fu-tzu.ru/posts/2009/12/03/nginx-spidermonkey-memory-management/"&gt;про память&lt;/a&gt;, &lt;a href="http://kung-fu-tzu.ru/posts/2008/12/15/quick-response/"&gt;про строки и кипэлайв&lt;/a&gt;), иначе получится большущий пост через год, который и до середины еле дочитаешь. Статьи по мере написания перелинкую, чтобы не потеряться самому.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/m4JS-IaJwjA" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Обновляем виртуозную убунту</title>
	<link href="http://kung-fu-tzu.ru/posts/2009/01/06/upgrade-ubuntu-in-virtuozzo" />
	<updated>2009-01-06T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2009/01/06/upgrade-ubuntu-in-virtuozzo</id>
	<content type="html">&lt;p&gt;&lt;strong&gt;UPD&lt;/strong&gt;: Умные вещи &lt;a href="http://blog.antage.name/posts/бэкпортинг-на-ubuntu-610-edgy.html"&gt;про бэкпортинг&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt; strong &gt;UPD: При обновлении до харди не обязательно проходить всю цепочку edgy → feisty → gutsy → hardy (так как это уже невозможно), достаточно сразу записать в &lt;code&gt;/etc/apt/sources.list&lt;/code&gt; везде hardy. Если будет ругаться на что-то связанное с tzdata (например, &lt;code&gt;Could not perform immediate configuration (2) on tzdata&lt;/code&gt;), то достаточно удалить этот пакет: &lt;code&gt;apt-get remove tzdata&lt;/code&gt;. Он потом все равно установится в процессе обновления.&lt;/p&gt;

&lt;p&gt;
Решил переехать со старой ubuntu 6.10 (можно было и с 6.06, но уже поздно) на новую ubuntu 8.04 внутри контейнера Virtuozzo на мастерхосте. Конфигурация, как видите, мудреная, и без эксцессов не обошлось.
&lt;/p&gt;

&lt;p&gt;
Сначала проапгрейдил систему через смену репозиториев, запуская для каждого &lt;code&gt;apt-get update&lt;/code&gt; и &lt;code&gt;apt-get dist-upgrade&lt;/code&gt;. Это рисковано, но сработало. Перезагрузив VPS, обнаружил, что он не запускается и висит на стадии запуска &lt;code&gt;klogd&lt;/code&gt;. Поговорил с техподдержкой, и это, как ни странно, помогло: пока мы болтали с тетей из мастерхоста, сервак загрузился. Он, оказывается, не висел, а просто долго чего-то ждал. После отключения &lt;code&gt;/etc/init.d/klogd&lt;/code&gt; система стала стабильно загружаться за несколько секунд.
&lt;/p&gt;

&lt;p&gt;
Дальше — интереснее. Обнаружил, что убунта больше не использует &lt;code&gt;sysvinit&lt;/code&gt;, а вместо него все запускается с помощью их нового апстарта (&lt;a href="http://upstart.ubuntu.com/"&gt;upstart&lt;/a&gt;). Почитал про него и понял, что этот новый апстарт есть именно то, что нужно. Он умеет загружать все параллельно, построен на событиях и — что главное для боевого сервера — умеет красиво респавнить упавшие демоны.
&lt;/p&gt;

&lt;p&gt;
Как же это здорово, подумалось мне тогда. Вот сейчас заиспользую этот чудесный апстарт. Но не тут-то было. Он при апгрейде не установился. Команда &lt;code&gt;cat /etc/issue&lt;/code&gt; пишет &lt;code&gt;Ubuntu 8.04 \n \l&lt;/code&gt;, а апстарта нету. Как же так?
&lt;/p&gt;

&lt;p&gt;
Ну, ничего. Мы же на каникулах — сейчас и апстарт поставим.
&lt;pre&gt;&lt;code class="bash"&gt;
apt-get install startup-tasks system-services \
    upstart-compat-sysv upstart-logd
&lt;/code&gt;&lt;/pre&gt;
Поставил, и сервер напрочь отказался запускаться. После нажатия кнопки запуска в панели управления сервер работал около двух секунд и снова становился выключенным. Но это не беда, у меня остался полный бекап системы.
&lt;/p&gt;

&lt;p&gt;
Как отлаживать такой черный ящик — непонятно. Выход нашелся очень забавный. Если VPS выключен, то через панель управления смотреть и править его файлы нельзя. Режима восстановления у мастерхостовского виртуоза нету. Однако, если в начале файла &lt;code&gt;/etc/init.d/rc&lt;/code&gt; поставить &lt;code&gt;sleep 600&lt;/code&gt;, то можно 10 минут спокойно лазить по файлам на еще не загруженном сервере. Не очень удобно, но уже что-то. Дальше обнаружилось, что до сбоя логи не пишутся никакие, хотя, процессы инициализации запускаются.
&lt;/p&gt;

&lt;p&gt;
С помощью метода научного тыка, строки &lt;code&gt;echo OK &gt; my-debug.log&lt;/code&gt; и моральной поддержи кота Шарика (в пять утра не спит только кот и его программист) обнаружил, что сервак убивает одна единственная строка &lt;code&gt;kill -USR1 1&lt;/code&gt; в &lt;code&gt;/etc/init.d/mountall.sh&lt;/code&gt;. Видимо, новая версия апстарта из моего сервера от такой грубости падала, и виртуоз решал, что пора сервер выключать, раз инит уже свалился. Повторю, мастерхостовский виртуоз делал все это очень быстро и очень молча. Хитрец такой.
&lt;/p&gt;

&lt;p&gt;
Если вы знаете быстрый, надежный и не очень недорогой VPS-хостинг с адекватными версиями линуксов и возможностью отладки, поделитесь со мной пожалуйста… ну, пожалуйста, пожалуйста, пожалуйста, пожалуйста ;)
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/tkiXtok8_jk" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>noscript</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/12/18/noscript" />
	<updated>2008-12-18T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/12/18/noscript</id>
	<content type="html">&lt;p&gt;
Прикольно написать посетителю, что у него отрублен яваскрипт. Особенно, если без яваскрипта все сразу рушится. Но еще прикольнее отправить несчастного на специальную красивенькую страничку. Там можно написать всяческие извинения, мол, прости, нам без яваскрипта никак. Или наоборот — угрозы, типа, влючай яваксрипт, мы тебя запомнили!
&lt;/p&gt;

&lt;p&gt;
Сделать это проще простого. Создайте отвечающую вашему стилю ведения бизнеса страничку, а в &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; остальных страниц добавьте &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; с &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; внутри. Примерно вот так:
&lt;pre&gt;&lt;code class="html"&gt;
&amp;lt;head&amp;gt;
    &amp;lt;noscript&amp;gt;
        &amp;lt;meta http-equiv="refresh" content="0;url=/noscript.html"/&amp;gt;
    &amp;lt;/noscript&amp;gt;
&amp;lt;/head&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/HW5rjwa9QD4" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Правильная дата</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/12/17/valid-date" />
	<updated>2008-12-17T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/12/17/valid-date</id>
	<content type="html">&lt;p&gt;Создавая дату с помощью &lt;code&gt;new Date(…)&lt;/code&gt;, мы всегда получим новый объект даты. Если проверить его на истинность, результатом будет &lt;code&gt;true&lt;/code&gt;. А как тогда проверить, не ошиблись ли мы, и верна ли дата?&lt;/p&gt;

&lt;p&gt;
Есть множество способов. Мне понравился этот: &lt;code&gt;!isNaN(+date)&lt;/code&gt;. Конструкция работает очень просто: приводит дату к числу унарным плюсом и проверяет число на равенство &lt;code&gt;NaN&lt;/code&gt;. Для удобства этот код можно завернуть в метод самой даты:
&lt;pre&gt;&lt;code class="javascript"&gt;
Date.prototype.isValid = function ()
{
    return !isNaN(+this)
}
&lt;/code&gt;&lt;/pre&gt;
Есть только одна проблема. Получить невалидную дату в опере (9.2, 9.6) крайне сложно. Для этого необходимо дать на вход просто совершеннейший бред, иначе опера будет усердно угадывать дату даже там, где ее нет. Например, &lt;code&gt;new Date('1 2 3')&lt;/code&gt; вернет дату «Mon, 01 Dec 2008 04:57:00 GMT+0300». Как? — не понятно.
&lt;/p&gt;

&lt;p&gt;
Чаще всего в реальной жизни не бывает валидных дат, которые превращаются в &lt;code&gt;0&lt;/code&gt; (то есть 1 января 1970 в 0 часов и 0 минут: &lt;code&gt;new Date(0)&lt;/code&gt;). Поэтому можно обойтись простой проверкой:
&lt;pre&gt;&lt;code class="javascript"&gt;
alert(+date ? 'валидна' : 'невалидна')
&lt;/code&gt;&lt;/pre&gt;
Однако, в сафари &lt;code&gt;NaN&lt;/code&gt; почему-то есть истина, и в нем без &lt;code&gt;isNaN&lt;/code&gt; не обойтись. Спасибо вам, если вы учитываете особенности сафарьки при разработке сайтов ;)
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPD&lt;/strong&gt;: Ура! В четвертом сафари &lt;code&gt;NaN&lt;/code&gt; стал чушью! Опс, то есть стал ложью ;)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/H3rVZYzh0BQ" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Быстрый ответ</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/12/15/quick-response" />
	<updated>2008-12-15T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/12/15/quick-response</id>
	<content type="html">&lt;p&gt;&lt;strong&gt;UPD 2011-04-08&lt;/strong&gt;: Внимание! Эта статья хоть и стара, но еще очень даже ничего..&lt;/p&gt;
&lt;p&gt;Если генерировать ответ постепенно, не зная заранее его размер, то нжинкс попытается отдать его клиенту чанками. Если клиент не поддерживает HTTP/1.1, то нжинкс откажется от кипэлайва. Это совсем не здорово, так как после отправки придется разорвать соединение и устанавливать новое.&lt;/p&gt;

&lt;p&gt;Конечно, яваскрипт может перед отправкой заголовков сложить все данные в одну строку, вычислить длину строки и передать ответ. Проблема заключается в том, что &lt;code&gt;string.length&lt;/code&gt; вернет именно длину строки, а не количество байт в ней. Чтобы узнать длину той строки, которую нжинкс будет реально передавать, надо сначала попросить SpiderMonkey сконвертировать внутреннюю строку (UTF-16) в обычную (ASCII, UTF-8), а затем уже вычислить ее длину. К этой работе добавляется манипуляция заголовками из яваскрипта. Это еще один вызов метода и несколько проверок входных/выходных параметров.&lt;/p&gt;

&lt;p&gt;Все эти действия будут лишними, если единственная цель вычисления длины строки — это отправка заголовка &lt;code&gt;Content-length&lt;/code&gt;. Разумно будет завернуть все необходимые действия в один нативный метод и передать ему только строку с данными. В таком случае мы экономим немного времени, что полезно, если результат запроса уже закеширован в какой-либо переменной, и его надо очень быстро отправить.&lt;/p&gt;

&lt;p&gt;Вот пример яваскрипта:
&lt;pre&gt;&lt;code class="javascript"&gt;
function processRequest (r)
{
	r.sendString("Привет, Девелопер!")
	return Nginx.OK
}
&lt;/code&gt;&lt;/pre&gt;
Эта функция умеет отдавать строку «Привет, Девелопер!» 15 000 раз в секунду. Без кипэлайва будет всего около 6000. Если попытаться еще немного соптимизировать, можно кешировать и ascii-строку и ее длину между обращениями к &lt;code&gt;sendString()&lt;/code&gt;. Строки в яваскрипте изменять нельзя, поэтому такое кеширование легко реализовать. Для длинных строк это может быть очень полезно.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/CNe5ECYVxpE" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Простейший cgi-скрипт</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/12/09/simplest-cgi-script" />
	<updated>2008-12-09T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/12/09/simplest-cgi-script</id>
	<content type="html">&lt;p&gt;Ненадолго отвлекусь от устройства нжинкса. Решил, шутки ради, проверить, как быстро основные скриптовые языки умеют запускаться внутри cgi-скриптов. CGI — это уже старо, совсем не модно, но зато стабильно.&lt;/p&gt;

&lt;p&gt;Сравнил статический файл, скомпиленный C, перл, руби, питон и яваскрипт (SpiderMonkey). Ясное дело, что от языка тут зависит немного. Время в основном уходит на создание пайпа, выставление переменных окружения и форк. Затем каждый интерпретатор выставляет свои глобальные переменные, классы. И только потом уже собственно парсит и выполняет скрипт. Для C это неактуально, но все равно интересно было на него посмотреть.&lt;/p&gt;

&lt;p&gt;Вот результаты забега. Для сравнения: статический файл отдается 5201.02 раза в секунду, а встроенный в нжинс яваскрипт — около 15 000;
&lt;ul&gt;
&lt;li&gt;С: 600,07;&lt;/li&gt;
&lt;li&gt;перл: 419,53;&lt;/li&gt;
&lt;li&gt;руби: 271,65;&lt;/li&gt;
&lt;li&gt;питон: 32,45;&lt;/li&gt;
&lt;li&gt;яваскрипт: 403,00.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Питон, как видно, я готовить не умею. На самом деле он крутой, быстрый, и тут что-то не так. В остальном, все говорит в пользу кеширования. Бинарник на C, конечно, заметно шустрее, но не принципиально. Легко подобрать задачу под один из языков, и справится он лучше других.&lt;/p&gt;

&lt;p&gt;Тестил на рабочем компе: Intel Core 2 Duo, 2.16 ГГц, 4 Мб L2, 2 Гб оперативки.
Mac OS X 10.5.5 (9F33), Darwin 9.5.0.&lt;/p&gt;

&lt;p&gt;Версии интерпретаторов/компиляторов:
&lt;ul&gt;
&lt;li&gt;JavaScript-C 1.7.0 2007-10-03&lt;/li&gt;
&lt;li&gt;ruby 1.8.6 (2007-03-13 patchlevel 0) [i686-darwin8.9.1]&lt;/li&gt;
&lt;li&gt;python 2.5.2&lt;/li&gt;
&lt;li&gt;perl 5.8.8 (darwin-2level)&lt;/li&gt;
&lt;li&gt;gcc version 4.0.1 (Apple Inc. build 5488)&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/N2jQKLAOAdc" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>ngx_http_js_module-0.1</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/12/07/ngx_http_js_module-01" />
	<updated>2008-12-07T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/12/07/ngx_http_js_module-01</id>
	<content type="html">&lt;p&gt;&lt;strong&gt;UPD 2011-04-08&lt;/strong&gt;: Внимание! Всё это старье, и лежит здесь только для истории.&lt;/p&gt;

&lt;h3&gt;Подготовка&lt;/h3&gt;

&lt;p&gt;Чтобы не отвлекаться от процесса сборки нжинкса, заранее скачаем все необходимые файлы:
&lt;ul&gt;
	&lt;li&gt;последний &lt;a href="http://sysoev.ru/nginx/download.html"&gt;стабильный нжинкс&lt;/a&gt; (у меня был &lt;a href="http://sysoev.ru/nginx/nginx-0.6.34.tar.gz"&gt;nginx 0.6.34&lt;/a&gt;)&lt;/li&gt;
	&lt;li&gt;релиз &lt;a href="http://ftp.mozilla.org/pub/mozilla.org/js/"&gt;SpiderMonkey&lt;/a&gt; (на этот момент &lt;a href="http://ftp.mozilla.org/pub/mozilla.org/js/js-1.7.0.tar.gz"&gt;версия 1.7.0&lt;/a&gt;)&lt;/li&gt;
	&lt;li&gt;свежая версия &lt;a href="http://ftp.mozilla.org/pub/mozilla.org/nspr/releases/"&gt;NSPR&lt;/a&gt; (здесь использовалась &lt;a href="http://ftp.mozilla.org/pub/mozilla.org/nspr/releases/v4.7.3/src/nspr-4.7.3.tar.gz"&gt;4.7.3&lt;/a&gt;)&lt;/li&gt;
	&lt;li&gt;и собственно ngx_http_js_module &lt;a href="http://www.cmsbuilder.ru/files/nginx.js/ngx_http_js_module-0.1.tgz"&gt;версии 0.1&lt;/a&gt; (&lt;b&gt;UPD:&lt;/b&gt; теперь и на &lt;a href="http://github.com/kung-fu-tzu/ngx_http_js_module/"&gt;ГитХабе&lt;/a&gt; по тегу &lt;a href="http://github.com/kung-fu-tzu/ngx_http_js_module/tarball/0.1"&gt;0.1&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;



&lt;h3 id="SpiderMonkeyFile"&gt;Собираем SpiderMonkey с поддержкой файлов&lt;/h3&gt;

&lt;p&gt;
Сначала скомпилим и установим &lt;a href="http://www.mozilla.org/projects/nspr/reference/html/priofnc.html"&gt;NSPR&lt;/a&gt;. Без него у нас не будет поддержки класса &lt;code&gt;File&lt;/code&gt; внутри SpiderMonkey.
&lt;pre&gt;&lt;code class="bash"&gt;
cd nspr-4.7.3/mozilla/nsprpub/
./configure
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;
Затем скомпилим и установим SpiderMonkey. Официальный способ сборки требует для конфигурации autoconf &lt;a href="http://ftp.gnu.org/gnu/autoconf/autoconf-2.13.tar.gz"&gt;версии 2.13&lt;/a&gt; от 1999 года (говорят, версия 2.50 и выше не подойдет). Скажу честно, собрать SpiderMonkey официальным способом так и не получилось. Да еще в мануале не пишут, как здесь включить поддержку файлов. Но, на всякий случай, вот этот способ:
&lt;pre&gt;&lt;code class="bash"&gt;
cd js/src
autoconf-2.13
./configure
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;
Если все собралось и установилось — прекрасно. Но файлов не будет, а без них нельзя.
&lt;/p&gt;

&lt;p&gt;
Нам достаточно собрать SpiderMonkey по старинке:
&lt;pre&gt;&lt;code class="bash"&gt;
cd js/src
JS_HAS_FILE_OBJECT=1 \
CFLAGS="-I/usr/local/include/nspr" \
OTHER_LIBS="/usr/local/lib/libnspr4.dylib" \
make -f Makefile.ref
&lt;/code&gt;&lt;/pre&gt;
Вам, возможно, надо заменить значения переменных &lt;code&gt;CFLAGS&lt;/code&gt; и &lt;code&gt;OTHER_LIBS&lt;/code&gt; на значения принятые в вашей системе.
&lt;/p&gt;

&lt;p&gt;
Теперь проверим наличие рабочего класса &lt;code&gt;File&lt;/code&gt;:
&lt;pre&gt;&lt;code class="bash"&gt;
echo "print (new File('.').parent)" | ./Darwin_DBG.OBJ/js
#=&gt; /Users/developer/Downloads/js/src
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;
И установим SpiderMonkey. Так как не работает &lt;code&gt;make install&lt;/code&gt;, придется самим раскладывать все файлы по своим местам. На моем компе это делается так:
&lt;pre&gt;&lt;code class="bash"&gt;
# внутри js/src
OBJ="Darwin_DBG.OBJ"
sudo cp $OBJ/js /usr/local/bin
sudo cp $OBJ/libjs.a /usr/local/lib
sudo cp $OBJ/libjs.dylib /usr/local/lib

sudo mkdir -p /usr/local/include/js
sudo cp $OBJ/*.h /usr/local/include/js
sudo cp *.h /usr/local/include/js
sudo cp *.tbl /usr/local/include/js
&lt;/code&gt;&lt;/pre&gt;
Замените &lt;code&gt;Darwin_DBG.OBJ&lt;/code&gt; на то, что получилось у вашего компилятора. Исходники SpiderMonkey нам больше не нужны, их можно убрать, чтобы не мешали.
&lt;/p&gt;



&lt;h3&gt;Собираем nginx c ngx_http_js_module&lt;/h3&gt;

&lt;p&gt;
Распакуем &lt;code&gt;nginx_http_js_module.tgz&lt;/code&gt;. Для удобства предположим, что у нас есть директория &lt;code&gt;/stuff&lt;/code&gt; и в ней лежит папка с нашим модулем: &lt;code&gt;/stuff/ngx_http_js_module&lt;/code&gt;. &lt;!-- Относительные пути &lt;code&gt;configure нжинкса не приемлет. --&gt;
&lt;/p&gt;

&lt;p&gt;
Сконфигурируем и скомпилируем нжинк с яваскриптом:
&lt;pre&gt;&lt;code class="bash"&gt;
cd nginx-0.6.34
./configure --add-module=/stuff/ngx_http_js_module
make
&lt;/code&gt;&lt;/pre&gt;
Если у вас уже установлен нжинкс той версии, которую мы собрали, то выполнять &lt;code&gt;make install&lt;/code&gt; не нужно. При запуске нжинкс будет искать &lt;code&gt;mime.types&lt;/code&gt; и другие файлы в своей директории &lt;code&gt;/usr/local/nginx/&lt;/code&gt;. Если вы уже ставили нжинкс, то эта директория существует и в ней есть все необходимые файлы. Если нет, то после &lt;code&gt;make&lt;/code&gt; выполните &lt;code&gt;make install&lt;/code&gt;.
&lt;/p&gt;



&lt;h3&gt;Потестим&lt;/h3&gt;

&lt;p&gt;
Запустим:
&lt;pre&gt;&lt;code class="bash"&gt;
# внутри nginx-0.6.34
sudo mkdir -p /usr/local/nginx/js
sudo cp /stuff/ngx_http_js_module/js/* /usr/local/nginx/js/
export JSLIB="/stuff/ngx_http_js_module/lib"
./objs/nginx -c /stuff/ngx_http_js_module/nginx.conf
&lt;/code&gt;&lt;/pre&gt;
Опросим:
&lt;pre&gt;&lt;code class="bash"&gt;
curl http://localhost:8080/
#=&gt; ... Welcome to nginx! ...
curl -v http://localhost:8080/test/page
#=&gt; Привет, Девелопер!
#=&gt; Ты вызвал страницу /test/page, методом GET, с IP 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;
И небольшой тест на скорость:
&lt;pre&gt;&lt;code class="bash"&gt;
ab -kc 10 -n 500 http://127.0.0.1:8080/test/page
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="bash"&gt;
Concurrency Level:      10
Time taken for tests:   0.075 seconds
Complete requests:      500
Failed requests:        0
Write errors:           0
Keep-Alive requests:    0
Total transferred:      127000 bytes
HTML transferred:       58500 bytes
Requests per second:    &lt;strong&gt;6668.53&lt;/strong&gt; [#/sec] (mean)
Time per request:       1.500 [ms] (mean)
Time per request:       0.150 [ms] (mean, across all concurrent requests)
Transfer rate:          1654.11 [Kbytes/sec] received
&lt;/code&gt;&lt;/pre&gt;
Тестил на первом поколении интеловских маков: Intel Core Duo 2 ГГц, 2 Мб кеша второго уровня, 1 Гб оперативки.
&lt;/p&gt;



&lt;h3&gt;Ура!&lt;/h3&gt;

&lt;p&gt;
Яваскрипт оказался очень шустрым. Два месяца назад тестовая сборка дала всего около 3000 запросов в секунду. Сегодняшняя версия дала 6600. Тестировал на одном и том же маке. Могу найти только два объяснения: либо нжинкс так прибавил в скорости, либо SpiderMonkey серьезно вырос за последнее время. Возможно, и то и другое. Получается, что используя фаерфокс, мы не только укрепляем клиентскую сторону веба, но и заглядываем в будущее серверной стороны. Как-то так.
&lt;/p&gt;



&lt;h3&gt;Что дальше?&lt;/h3&gt;

&lt;p&gt;
Расскажу, как устроен модуль внутри. Как он крепится к нжинксу, и как нжинкс обменивается данными с яваскриптом. Это, на мой взгляд, будет самое интересное. Архитектура нжинкса захватывает сложностью. Яваскрипт очень элегантен в своей простоте. Они такие разные.
&lt;/p&gt;



&lt;h3&gt;Если не заводится&lt;/h3&gt;

&lt;p&gt;
Иногда бывает так, что используешь указанные в руководстве версии, все делаешь правильно, а ничего не получается. Вот и со мной так было со SpiderMonkey 1.7.0. Скачал для написания поста новый архив, версия не изменилась, а содержимое архива изменилось. И не завелось. Чтобы уберечь вас от этих проблем, припас именно те файлики, которыми пользовался сам. Предлагаю в случае чего скачивать вот эти архивы:
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.cmsbuilder.ru/files/nginx.js/autoconf-2.13.tar.gz"&gt;autoconf-2.13.tar.gz&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.cmsbuilder.ru/files/nginx.js/js-1.7.0.tar.gz"&gt;js-1.7.0.tar.gz&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.cmsbuilder.ru/files/nginx.js/nginx-0.6.34.tar.gz"&gt;nginx-0.6.34.tar.gz&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.cmsbuilder.ru/files/nginx.js/nspr-4.7.3.tar.gz"&gt;nspr-4.7.3.tar.gz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;



&lt;h3&gt;Смотрите также&lt;/h3&gt;

&lt;p&gt;
&lt;ul&gt;
	&lt;li&gt;Искать описание структур и функций удобно на &lt;a href="http://emiller.info/lxr/http/source/"&gt;lxr Эмиллера&lt;/a&gt;.&lt;/li&gt;
	&lt;li&gt;&lt;a href="http://www.clanmills.com/articles/spidermonkey/#Content"&gt;Интересная статья Робина Миллза&lt;/a&gt; о том, как собрать SpiderMonkey с поддержкой класса &lt;code&gt;File&lt;/code&gt;. Ее лучше читать с отключенными стилями и картинками ;)&lt;/li&gt;
	&lt;li&gt;Думаю, пригодятся &lt;a href="http://www.mozilla.org/js/js-file-object.html"&gt;доки по классу &lt;code&gt;File&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
	&lt;li&gt;Может понадобиться и &lt;a href="https://developer.mozilla.org/En/SpiderMonkey/File_object"&gt;другая версия&lt;/a&gt; доков про &lt;code&gt;File&lt;/code&gt;.&lt;/li&gt;
	&lt;li&gt;Firefox на сервере: &lt;a href="http://www.aptana.com/jaxer"&gt;Aptana Jaxer&lt;/a&gt;. Впечатлил, но о нем потом.&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/uHH2MBSsrf0" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>nginx.js</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/11/30/nginx-js" />
	<updated>2008-11-30T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/11/30/nginx-js</id>
	<content type="html">&lt;p&gt;&lt;strong&gt;UPD 2011-04-08&lt;/strong&gt;: Внимание! Всё это старье, и лежит здесь только для истории.&lt;/p&gt;
&lt;p&gt;На &lt;a href="http://www.inshaker.ru/"&gt;работе&lt;/a&gt; мы используем по большому счету только javascript и nginx (еще немножко ruby, но об этом в следующий раз). И мне всегда хотелось посмотреть, как они устроены внутри. Но достойной задачи не находилось, и пришлось ее придумывать. Что может быть лучше в данном случае, чем написать модуль для нжинкса по встраиванию яваскрипта? И правда, оказалось очень интересно. Сразу скажу, что практического применения связке nginx+js найти пока не удалось.&lt;/p&gt;

&lt;h3&gt;nginx&lt;/h3&gt;
&lt;p&gt;Стабильной версией нжинкса на данный момент является &lt;a href="http://sysoev.ru/nginx/nginx-0.6.34.tar.gz"&gt;0.6.34&lt;/a&gt;. В качестве мануала был выбран другой модуль: &lt;a href="http://sysoev.ru/nginx/docs/http/ngx_http_perl_module.html"&gt;ngx_http_perl_module&lt;/a&gt;. Встроить перл у меня однажды получилось — правда, с трудом — и этот модуль показался самым лучшим примером. Никакое другое руководство мне бы не помогло так, как помог исходник перлового модуля. Вот она сила опенсорса! Чуть позже встретилось «&lt;a href="http://emiller.info/nginx-modules-guide.html"&gt;Руководство Эмиллера по разработке модулей для Nginx&lt;/a&gt;». Это руководство мне очень понравилось. Оно дает общее представление о нжинксе и его механизмах. Я даже &lt;a href="http://www.cmsbuilder.ru/blog/emillers-nginx-modules-guide/"&gt;перевел половину&lt;/a&gt; на русский.&lt;/p&gt;

&lt;h3&gt;javascript&lt;/h3&gt;
&lt;p&gt;В качестве представителя яваскрипта был выбран &lt;a href="https://developer.mozilla.org/en/SpiderMonkey"&gt;SpiderMonkey&lt;/a&gt;. Потому, что мне нравится файрфокс, и у них еще на &lt;a href="http://www.mozilla.org/rhino/"&gt;рино&lt;/a&gt; сил хватает. Сначала почитал &lt;a href="https://developer.mozilla.org/en/SpiderMonkey_Build_Documentation"&gt;как его компилить&lt;/a&gt;, потом &lt;a href="https://developer.mozilla.org/En/SpiderMonkey/JSAPI_User_Guide"&gt;как его встраивать&lt;/a&gt;, а затем погрузился &lt;a href="https://developer.mozilla.org/en/SpiderMonkey/JSAPI_Reference"&gt;в апи&lt;/a&gt;. Механизмы в этих текстах изложены вполне доступно, так что страх перед неизведанным быстро пропал. Встраивался именно &lt;a href="http://ftp.mozilla.org/pub/mozilla.org/js/js-1.7.0.tar.gz"&gt;SpiderMonkey 1.7.0&lt;/a&gt;. Мозила готовит ему на смену &lt;a href="https://wiki.mozilla.org/JavaScript:TraceMonkey"&gt;TraceMonkey&lt;/a&gt; и обещает, что сохранит обратную совместимость. Верю им: Firefox 3.1 уже вовсю использует TraceMonkey, значит и нжинкс сможет. А это уже JIT.&lt;/p&gt;

&lt;h3&gt;nginx+javascript&lt;/h3&gt;
&lt;p&gt;Так как SpiderMonkey не несет с собой библиотеки модулей (как перл, руби или питон), все придется писать с нуля. Это тоже очень интересно. Так же есть возможность прикрутить поделки немногочисленных отважных джедаев. В случае нашего модуля  научить яваскрипт удалось немногому: выводить сообщения в лог и отвечать на простые GET запросы. Никаких инструментов для парсинга простых POST-запросов или, тем более, мультипартов на яваскрипте мной не написано. Даже файл открыть нельзя. Не удивляйтесь, это концепт, и нужен он пока только мне одному.&lt;/p&gt;

&lt;h3&gt;nginx.js&lt;/h3&gt;
&lt;p&gt;Итак, что мы умеем. Вот пример функции, которая отвечает на HTTP-запрос:
&lt;pre&gt;&lt;code class="javascript"&gt;
function processRequest (r)
{
    r.sendHttpHeader('text/html; charset=utf-8')
    r.printString("Привет, Девелопер!\n")
    r.printString("Ты вызвал страницу " + r.uri
        + ', методом ' + r.method + ', с IP ' + r.remoteAddr)
    return Nginx.HTTP_OK
}
&lt;/code&gt;&lt;/pre&gt;
&lt;dl&gt;
&lt;dt&gt;Небольшое пояснение:&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;r&lt;/code&gt; — это объект запроса, созданный внутри нжинкса;&lt;/dd&gt;
&lt;dd&gt;&lt;code&gt;r.sendHttpHeader(ctype)&lt;/code&gt; — это метод отправляет заголовки клиенту, попутно устанавливая &lt;code&gt;Content-type&lt;/code&gt; в значение &lt;code&gt;ctype&lt;/code&gt;;&lt;/dd&gt;
&lt;dd&gt;&lt;code&gt;r.printString(str)&lt;/code&gt; — метод копирует значение строки &lt;code&gt;str&lt;/code&gt; в очередь на отправку;&lt;/dd&gt;
&lt;dd&gt;&lt;code&gt;r.uri&lt;/code&gt;, &lt;code&gt;r.method&lt;/code&gt; и &lt;code&gt;r.remoteAddr&lt;/code&gt; — геттеры для соответствующих полей запроса;&lt;/dd&gt;
&lt;dd&gt;&lt;code&gt;Nginx.NGX_HTTP_OK&lt;/code&gt; — константа равная &lt;code&gt;200&lt;/code&gt;.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/p&gt;


&lt;p&gt;А вот отрывок файла конфигурации, который загружает &lt;code&gt;nginx.js&lt;/code&gt; и устанавливает хендлер &lt;code&gt;processRequest()&lt;/code&gt;:
&lt;pre&gt;&lt;code class="nginx"&gt;
http
{
    js_require 'nginx.js';
    # ...
    server
    {
        # ...
        location /test
        {
            js processRequest;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
В общем, все скопировано с &lt;code&gt;ngx_http_perl_module&lt;/code&gt;.
&lt;/p&gt;

&lt;h3&gt;Итог&lt;/h3&gt;
&lt;p&gt;Скоро должна накопиться критическая масса желающих использовать javascript как язык общего назначения. Они принесут с собой новые полезные модули, портируют уже существующие модули для похожих языков (например, модули для руби). И все станет еще интереснее. Но уже сейчас легкий и быстрый скриптовый язык внутри нашего любимого нжинкса выглядит интересно. Посмотрим что еще из этого получится.&lt;/p&gt;

&lt;h3&gt;Смотрите в следующих сериях&lt;/h3&gt;
&lt;p&gt;В ближайшем будущем постараюсь оформить этот модуль как &lt;a href="/posts/2008/12/07/ngx_http_js_module-01/"&gt;настоящий модуль&lt;/a&gt;, а не как патч ко всему дереву исходников. Затем опишу наиболее интересные места спайки конструкций &lt;code&gt;NGX_*&lt;/code&gt; и &lt;code&gt;JavaScript.*&lt;/code&gt;. Возможно, научу-таки яваскрипт открывать файлы (&lt;a href="/posts/2008/12/07/ngx_http_js_module-01/#SpiderMonkeyFile"&gt;научил&lt;/a&gt;, а потом разучил).&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/FpDIvReYZL8" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Versions.app зарелизили</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/11/24/versions-app-released" />
	<updated>2008-11-24T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/11/24/versions-app-released</id>
	<content type="html">&lt;p&gt;&lt;img src="i/versions.png" alt="Versions.app" class="aside"/&gt;Больше полугода пользовался бетами &lt;a href="http://www.versionsapp.com/"&gt;Versions.app&lt;/a&gt;, а теперь они выпустили релиз. Продают его за 49$, чему я очень рад, так как именно столько и готов отдать за клевый svn-клиент.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD 2009-10-10&lt;/strong&gt;: как же &lt;a href="/posts/2009/10/10/gitx/"&gt;теперь жаль&lt;/a&gt; эти баксы ;)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPD 2010-11-29&lt;/strong&gt;: как же теперь жаль годы, потраченные на SVN ;)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/FLfh8HgisBE" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>String.interpolateJS()</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/11/22/string-interpolate-js" />
	<updated>2008-11-22T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/11/22/string-interpolate-js</id>
	<content type="html">&lt;p&gt;Помогает запихнуть в строку немного яваскрипта. Функция очень полезна во всяких ленгвиджпаках. Весь яваскрипт выполняется без ограничений, так что будьте бдительны.&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;
"Welcome to ${company}!".interpolateJS({company:'Programica'})
//=&gt; "Welcome to Programica!"

"Your name is ${name}.".interpolateJS({name:prompt('What is your name?')})
//=&gt; "Your name is Dima."

// tru javascript inside :)
"Your name is ${prompt('What is your name?')}.".interpolateJS({})
//=&gt; "Your name is Dima."

"${this.interpolateJS({})}".interpolateJS({})
// too much recursion

("${another = 'another', 'Just'} ${another}"+
    " ${'tpircSavaJ'.split('').reverse().join('')}"+
    " ${[\"hacker\"] || ''},").interpolateJS({another:1})
//=&gt; "Just another JavaScript hacker,"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Компиляция нужна только при первой интерполяции строки, так как полученная после парсинга функция кешируется. Исходя из этого, первое обращение к &lt;code&gt;interpolateJS()&lt;/code&gt; будет медленнее последующих.&lt;/p&gt;

&lt;p&gt;Чтобы получить закешированную функцию по строке, обратитесь к объекту &lt;code&gt;String.prototype.interpolateJS.cache&lt;/code&gt;:
&lt;pre&gt;&lt;code class="javascript"&gt;
var string = "${x} + 2 = ${ x + 2 } :)"
string.interpolateJS({x:2})
//=&gt; "2 + 2 = 4 :)"

String.prototype.interpolateJS.cache[string]
//=&gt; function ($_$h) { with ($_$h) { return x + $_$s[1] + (x + 2) + $_$s[2]; } }
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;Исходник &lt;a href="https://github.com/kung-fu-tzu/liby/blob/master/src/modules/interpolate.js"&gt;в библиотеке&lt;/a&gt;.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/dDAVQPrbq8I" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Серенькие подсказки в текстовых инпутах</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/11/14/input-gray-tip" />
	<updated>2008-11-14T00:00:00-08:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/11/14/input-gray-tip</id>
	<content type="html">&lt;p&gt;Кладем инпут в лейбл. Кидаем &lt;code&gt;&amp;lt;small&amp;gt;&lt;/code&gt; с подсказкой в лейбл. Лейблу делаем &lt;code&gt;position: relative&lt;/code&gt;. Располагаем смол над инпутом. Делаем цвет смола прозрачным или просто сереньким.&lt;/p&gt;

&lt;p&gt;Получается, когда юзер кликает мышкой по подсказке, фокус переходит в инпут. Остается научить страничку прятать подсказку, когда в инпуте появляется текст.&lt;/p&gt;

&lt;p&gt;Чтобы все было красиво, надо чтобы подсказка пряталась сразу после появления текста в инпуте. Сделать это не так просто, как кажется. Если подписаться на &lt;code&gt;keydown&lt;/code&gt; , то мы не сможем узнать, какой текст сейчас в инпуте. Если подписаться на &lt;code&gt;keypress&lt;/code&gt; или &lt;code&gt;keyup&lt;/code&gt;, то мы сможем спрятать тип только после небольшой, но заметной задержки.&lt;/p&gt;

&lt;p&gt;Махинации с отлавливанием делита и бекспейса не проходят, так как юзер может выделить текст мышкой и удалить его разом. Дублировать функциональность инпута для того, чтобы узнать его значение — это не наш путь.&lt;/p&gt;

&lt;p&gt;Работает очень простой финт. Подписываемся на &lt;code&gt;keypress&lt;/code&gt;, откладываем событие с помощью &lt;code&gt;setTimeout()&lt;/code&gt;, и производим все проверки в отложенной функции, когда инпут уже определиться со своим состоянием.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/1gPnHTRJxkE" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Руководство Эмиллера по разработке модулей для Nginx</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/09/09/emillers-nginx-modules-guide" />
	<updated>2008-09-09T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/09/09/emillers-nginx-modules-guide</id>
	<content type="html">&lt;style type="text/css"&gt;
div.figure
{
	margin: 2em;
	text-align: center;
}
div.figure img { display: block; margin: 0 auto 1em; }
&lt;/style&gt;

&lt;section&gt;
&lt;small&gt;Оригинал &lt;a href="http://www.evanmiller.org/nginx-modules-guide.html"&gt;Emiller's Guide To Nginx Module Development&lt;/a&gt;, черновик от 14 июля 2008 г. (&lt;a href="http://www.evanmiller.org/nginx-modules-guide.html#changes"&gt;изменения&lt;/a&gt;).
Автор: &lt;a href="http://www.evanmiller.org/"&gt;Эван Миллер&lt;/a&gt;, перевод &lt;a href="/"&gt;Петра Леонова&lt;/a&gt;.&lt;/small&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style="font-variant: small-caps;"&gt;Брюс Уэйн&lt;/span&gt;: &lt;em&gt;Что это?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style="font-variant: small-caps"&gt;Люциус Фокс&lt;/span&gt;: &lt;em&gt;Бэтмобиль? Ох… лучше вам не знать.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;p&gt;Чтобы лучше разобраться в Nginx'е, web-сервере, надо сначала понять Бэтмена, персонажа комиксов.&lt;/p&gt;

&lt;p&gt;Бэтмен быстр. Nginx быстр. Batman борется с преступностью. Nginx борется с лишней нагрузкой на процессор и утечками памяти. Бэтмен хорошо держится под натиском врага. Nginx, в свою очередь, держится превосходно при очень большой нагузке на сервер.&lt;/p&gt;

&lt;p&gt;Но кем бы был Бэтмен без своего &lt;strong&gt;Пояса&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="figure"&gt;
	&lt;img src="i/utility-belt.jpg" alt="Пояс Бэтмена"/&gt; 
	&lt;strong&gt;Рисунок 1&lt;/strong&gt;: Пояс Бэтмена, в обнимку с пузом Кристиана Бэйла.
&lt;/div&gt;

&lt;p&gt;В любой момент в Поясе Бэтмена может найтись набор отмычек, пара бумерангов, бэт-наручники, бэт-маячок, бэт-дротик, прибор ночного видения, термитные гранаты, дымовая завеса, фонарик, обруч из криптонита, паяльная лампа или iPhone. Если Бэтмену надо кого-то усыпить, ослепить, оглушить, выследить, пристукнуть, притормозить, довести до слез или заэсэмэсить насмерть, то он тянется к своему Поясу. Для Бэтмена он так много значит, что он скорей забыл бы одеть штаны, чем пояс. А у Бэтмена штанов и нет, вместо них ему приходится носить резиновые бронеритузы (рис. 1).&lt;/p&gt;

&lt;p&gt;Вместо Пояса Бэтмена у Nginx имеется свой &lt;strong&gt;набор модулей&lt;/strong&gt;. Когда нужно сжать запрос или передать его по частям, Nginx запускает соответсвующий модуль. Когда Nginx блокирует доступ с какого-либо IP адреса или проверяет данные HTTP-авторизации, на самом деле это делает один из модулей. Если Nginx подсоединяется к Memcache или FastCGI-серверу, то именно модуль связывает их.&lt;/p&gt;

&lt;p&gt;Пояс Бэтмена напичкан всякими полезностями, но иногда Бэтмену нужно что-то новенькое. Например, обнаружился новый противник, которого не удержать бэт-наручниками и не сломить бумерангом. Или Бэтмену скоро пригодится новая способность, такая как способность дышать под водой. Вот тогда-то Бэтмен и зовет &lt;strong&gt;Люциуса Фокса&lt;/strong&gt;, чтобы тот придумал новую бэт-штуковину.&lt;/p&gt;

&lt;div class="figure"&gt;
	&lt;img src="i/lucius.jpg" alt="Люциус Фокс"/&gt; 
	&lt;strong&gt;Рисунок 2&lt;/strong&gt;: Брюс Уэйн (он же Бэтмен) со своим инженером Люциусом Фоксом.
&lt;/div&gt;


&lt;p&gt;Целью этого руководства является подробно рассказать вам о модулях Nginx, чтобы вы смогли стать как Люциус Фокс. После прочтения руководства вы сможете проектировать и реализовывать отличные модули, которые помогут Nginx делать то, чего он раньше не умел. Система модулей Nginx содержит много ньюансов и особенностей, так что вы, наверно, будете часто возвращаться к этому руководству. Я постарался изложить концепции настолько доступно, насколько это возможно. но, без сомнений, создание модулей для Nginx все равно остается трудной задачей.&lt;/p&gt;

&lt;p&gt;Но кто говорил, что создавать бэт-штуковины будет легко?&lt;/p&gt;
&lt;/section&gt;
	
&lt;section&gt;
&lt;h&gt;Содержание&lt;/h&gt;
&lt;ol&gt;
	&lt;li&gt;&lt;a href="#prerequisites"&gt;Для начала&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href="#overview"&gt;Устройство модулей в первом приближении&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="#components"&gt;Компоненты модуля Nginx&lt;/a&gt;
		&lt;ol&gt;
			&lt;li&gt;&lt;a href="#configuration-structs"&gt;Структуры конфигурации модуля&lt;/a&gt;&lt;/li&gt;
			&lt;li&gt;&lt;a href="#directives"&gt;Директивы модуля&lt;/a&gt;&lt;/li&gt;
			&lt;li&gt;
				&lt;a href="#context"&gt;Контекст модуля&lt;/a&gt;
				&lt;ol&gt;
					&lt;li&gt;&lt;a href="#create_loc_conf"&gt;create_loc_conf&lt;/a&gt;&lt;/li&gt;
					&lt;li&gt;&lt;a href="#merge_loc_conf"&gt;merge_loc_conf&lt;/a&gt;&lt;/li&gt;
				&lt;/ol&gt;
			&lt;/li&gt;
			&lt;li&gt;&lt;a href="#definition"&gt;Описание модуля&lt;/a&gt;&lt;/li&gt;
			&lt;li&gt;
				&lt;a href="#installation"&gt;Установка модуля&lt;/a&gt;
				&lt;ol&gt;
					&lt;li&gt;&lt;a href="#handler-installation"&gt;Установка обработчика&lt;/a&gt;&lt;/li&gt;
					&lt;li&gt;&lt;a href="#filter-installation"&gt;Установка фильтра&lt;/a&gt;&lt;/li&gt;
				&lt;/ol&gt;
			&lt;/li&gt;
		&lt;/ol&gt;
	&lt;/li&gt;
	&lt;li&gt;
		&lt;a href="#types"&gt;Обработчики, фильтры и балансировщики нагрузки&lt;/a&gt;
		&lt;ol&gt;
			&lt;li&gt;
				&lt;a href="#non-proxying"&gt;Устройство обработчиков (не проксирующих)&lt;/a&gt;
				&lt;ol&gt;
					&lt;li&gt;&lt;a href="#non-proxying-config"&gt;Получение конфигурации локейшна&lt;/a&gt;&lt;/li&gt;
					&lt;li&gt;&lt;a href="#non-proxying-response"&gt;Генерация ответа&lt;/a&gt;&lt;/li&gt;
					&lt;li&gt;&lt;a href="#non-proxying-header"&gt;Отправка заголовка ответа&lt;/a&gt;&lt;/li&gt;
					&lt;li&gt;&lt;a href="#non-proxying-body"&gt;Отправка тела ответа&lt;/a&gt;&lt;/li&gt;
				&lt;/ol&gt;
			&lt;/li&gt;
		&lt;/ol&gt;
	&lt;/li&gt;
&lt;/ol&gt;

&lt;section&gt;
&lt;a name="prerequisites"&gt;&lt;/a&gt;
&lt;h&gt;Для начала&lt;/h&gt;
&lt;p&gt;Вы должны неплохо знать Си. Не просто его синтаксис, а то, как работать со структурами и не бояться указателей и ссылок на функции. А также иметь представление о препроцессоре и макросах. Если вам надо немного освежить знания, то ничто не сможет сравниться с &lt;a href="http://en.wikipedia.org/wiki/The_C_Programming_Language_(book)"&gt;K&amp;amp;R&lt;/a&gt;(англ.).&lt;/p&gt;

&lt;p&gt;Полезно понимать основы HTTP. Мы же, вообще-то, собираемся работать с web-сервером.&lt;/p&gt;

&lt;p&gt;Пригодятся знания структуры конфигурационного файла Nginx'а. Вот основные моменты: существуют четыре &lt;em&gt;контекста&lt;/em&gt; (называеются они &lt;em&gt;main&lt;/em&gt; — главный, &lt;em&gt;server&lt;/em&gt; — сервер, &lt;em&gt;upstream&lt;/em&gt; — апстрим, и &lt;em&gt;location&lt;/em&gt; — локейшн) в которых могут быть директивы с одним и более параметрами. Директивы в главном контексте применяются ко всему-всему; директивы из котекста сервера применяются к конкретному хосту/порту; директивы в апстриме описывают набор бэкендов; а директивы в контексте локешна применяются к разным путям запроса (например, "/", "/images" и т.д.) Локешн наследует конфигурацию содержащему его серверному контексту, а сервер наследует главному контексту. Контекст апстрима не наследует никому, у него собственные директивы, которых больше нигде не используются. Я буду иногда упоминать эти четыре контекста, так что… не забывайте про них.&lt;/p&gt;

&lt;p&gt;Ну что же, начнем!&lt;/p&gt;

&lt;section&gt;
&lt;a name="overview"&gt;&lt;/a&gt;
&lt;h&gt;1. Устройство модулей в первом приближении&lt;/h&gt;
У модулей Nginx'a могут быть три роли, которые мы рассмотрим:
&lt;ul&gt;
	&lt;li&gt;&lt;em&gt;обработчики&lt;/em&gt; обрабатывают запрос и генерируют данные ответа&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;фильтры&lt;/em&gt; обрабатывают данные, полученные от обработчика&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;балансировщики&lt;/em&gt; выбирают бэкенд, которому передать запрос, если определено несколько бэкендов&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Модули делают реальную работу, которую обычно делают web-серверы: когда Nginx отправляет файл или проксирует запрос к другому серверу, то это делает модуль-обработчик. Когда Nginx гзипит данные или обрабатывает SSI-директивы, он делает это с помощью модуля-фильтра. Ядро Nginx'а берет на себя работу с сетью и реализацию протоколов, а также запускает модули, которые необходимы для обработки запроса. Децентрализованная архитектура позволяет нам создавать отдельные компоненты, которые делают что-то, что нам нужно.&lt;/p&gt;

&lt;p&gt;Замечание: в отличие от модулей Apache, модули Nginx'а &lt;em&gt;не&lt;/em&gt; подгружаются динамически (другими словами, модули вкомпилированы прямо в бинарник Nginx'а).&lt;/p&gt;

&lt;p&gt;Как же тогда модули задействуются? Обычно, на стадии загрузки сервера каждый обработчик получает шанс прикрепиться к каким-либо локейшнам из конфигурационного файла. Если несколько обработчиков попробуют занять один локейшн, то победит только один (в хорошем конфиге такого не произойдет). Обработчик может завершиться с тремя результатами: все хорошо, произошла ошибка, или он может отказаться от обработки локешна в пользу обработка по умолчанию (обычно, это выдача статических файлов).&lt;/p&gt;

&lt;p&gt;Если обработчик является реверс-прокси, то ему понадобится помощь балансировщика нагрузки. Балансировщик получает запрос вместе с набором бэкендов и принимает решение, какому серверу передать запрос. Nginx поставлется с двумя модулями балансировки: round-robin, который выбирает серверы по очереди, и модуль с методом хеширования IP адреса, который гарантирует, что запрос конкретного клиента каждый раз будет передаваться одному и тому же бэкенду.&lt;/p&gt;

&lt;p&gt;Если обработчик не вернул ошибку, управление перейдет к фильтрам. Один локешн могут фильтровать несколько модулей, так, например, ответ может быть сжат, а потом выдаваться chunk'ами. Порядок запуска фильтров определяется на этапе компиляции. Фильры используют классический паттерн «&lt;a href="http://ru.wikipedia.org/wiki/Chain_of_Responsibility"&gt;цепочка обязанностей&lt;/a&gt;»: запускается один фильтр, делает свою работу, потом запускается второй, и так далее, пока не выполнится последний фильтр, и Nginx завершит обработку запроса.&lt;/p&gt;

&lt;p&gt;Самая вкусная особенность цепочки фильтров заключается в том, что один фильтр не должен ждать, пока другой завершит свою работу целиком. Можно начать обрабатывать результат работы предыдущего фильтра по мере поступления, почти как птоки (пайпы) в юниксе. Фильры оперируют &lt;em&gt;буферами&lt;/em&gt;, размер которых, обычно, равен размеру страницы (4 Кб), но размер всегда можно задать в nginx.conf. Это означает, например, то, что что модуль может начать сжимать ответ и отправлять его клиенту еще до того, как бэкенд полностью передат данные ответа. Чудесно!&lt;/p&gt;

Чтобы увидеть картину в целом, рассмотрим типичный цикл обработки запроса:
&lt;ol&gt;
	&lt;li&gt;клиент посылает HTTP-запрос;&lt;/li&gt;
	&lt;li&gt;Nginx выбирает подходящий обработчик на основе конфига;&lt;/li&gt;
	&lt;li&gt;балансировщик (если необходимо) выбирет бэкенд;&lt;/li&gt;
	&lt;li&gt;обработчик делает свое дело и передает каждый буфер с данными результата первому фильтру;&lt;/li&gt;
	&lt;li&gt;фильтр передает результаты второму фильтру;&lt;/li&gt;
	&lt;li&gt;второй — третьему, третий — четвертому, и так далее;&lt;/li&gt;
	&lt;li&gt;получившийся ответ отправляется клиенту.&lt;/li&gt;
&lt;/ol&gt;

Я сказал «типичный» цикл потому, что обработку в Nginx'е можно настраивать как угодно. Определить когда и как должен запускать модуль может оказаться непростой задачей для разработчика (я бы сказал очень даже не простой задачей). Настройка модуля проходит в следствии вызова ряда колбеков, и их не так уж мало. Конкретно, можно определить функцию, которая будет запущена:
&lt;ul&gt;
	&lt;li&gt;Прямо перед чтением конфигурационного файла&lt;/li&gt;
	&lt;li&gt;Для каждой директивы конфигурации локешна или сервера по мере их поступления&lt;/li&gt;
	&lt;li&gt;Когда Nginx инициализирует главную конфигурацию&lt;/li&gt;
	&lt;li&gt;Когда Nginx инициализирует конфигурацию сервера (хост/порт)&lt;/li&gt;
	&lt;li&gt;Когда Nginx мерджит конфигурацию сервера с главной конфигурацией&lt;/li&gt;
	&lt;li&gt;Когда Nginx инициализирует конфигурацию локешна&lt;/li&gt;
	&lt;li&gt;Когда Nginx мерджит конфигурацией сервера с вложенной конфигурацией локешна&lt;/li&gt;
	&lt;li&gt;Когда запускается главный процесс Nginx'а&lt;/li&gt;
	&lt;li&gt;Когда запускается новый рабочий процесс&lt;/li&gt;
	&lt;li&gt;Когда рабочий процесс завершается&lt;/li&gt;
	&lt;li&gt;Когда главный процесс завершается&lt;/li&gt;
	&lt;li&gt;Для обработки запроса&lt;/li&gt;
	&lt;li&gt;Для фильтрации заголовка ответа&lt;/li&gt;
	&lt;li&gt;Для фильтрации тела ответа&lt;/li&gt;
	&lt;li&gt;Для выбора бэкенда&lt;/li&gt;
	&lt;li&gt;В момент инициализации запроса к бэкенда&lt;/li&gt;
	&lt;li&gt;В момент &lt;em&gt;пере&lt;/em&gt;инициализации запроса к бэкенду&lt;/li&gt;
	&lt;li&gt;Для обработки ответа от бэкенда&lt;/li&gt;
	&lt;li&gt;В момент завершения работы с бэкендом&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Боже мой! Это может смутить. В вашем распоряжении большая мощь, но можно начать делать что-то полезное, используя всего несколько хуков и соответсвующих функций. Время погрузиться в модули Nginx'а.&lt;/p&gt;

&lt;section&gt;
&lt;a name="components"&gt;&lt;/a&gt;
&lt;h&gt;2. Компоненты модуля Nginx&lt;/h&gt;

&lt;p&gt;Как я уже сказал, в вашем распоряжении &lt;em&gt;огромный&lt;/em&gt; запас гибкости для разработки модуля для Nginx'а. В этом разделе те части, которые есть практически в любом модуле. Это моможет нам лучше понять устройство модуля. А так же вы сможете оценить, когда можно будет перейти к написанию собственного модуля.&lt;/p&gt;

&lt;section&gt;
&lt;a name="configuration-structs"&gt;&lt;/a&gt;
&lt;h&gt;2.1. Структуры конфигурации модуля&lt;/h&gt;

Модуль можно описать с помощью трех разных конфигурационных структур, по одной для главного контекста, контекста сервера и контекста локешна. Большинству модулей достаточно конфигурации локешна. Для структур принято такое именование: &lt;code&gt;ngx_http_&amp;lt;название_модуля&amp;gt;_(main|srv|loc)_conf_t&lt;/code&gt;. Вот пример, взятый из модуля &lt;code&gt;dav&lt;/code&gt;:
&lt;code&gt;
&lt;pre&gt;&lt;code class="cpp"&gt;
typedef struct {
    ngx_uint_t  methods;
    ngx_flag_t  create_full_put_path;
    ngx_uint_t  access;
} ngx_http_dav_loc_conf_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;/code&gt;

&lt;p&gt;Заметьте, что в Nginx'е используются специальные типы данных (&lt;code&gt;ngx_uint_t&lt;/code&gt; и &lt;code&gt;ngx_flag_t&lt;/code&gt;). Это просто алиасы для простых типов данных, которые мы все знаем и любим (см. &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/core/ngx_config.h#L79"&gt;core/ngx_config.h&lt;/a&gt;, если есть сомнения).&lt;/p&gt;


Элементы конфигурационной структуры заполняются значениями, описывающими модуль.

&lt;a name="directives"&gt;&lt;/a&gt;
&lt;h3&gt;2.2. Директивы модуля&lt;/h3&gt;
Директивы описыватся в статическом массиве элементов типа &lt;code&gt;ngx_command_t&lt;/code&gt;. Вот пример того, как их определять (взят из маленького модуля, который я написал):

&lt;code&gt;
&lt;pre&gt;&lt;code class="cpp"&gt;
static ngx_command_t  ngx_http_circle_gif_commands[] = {
    { ngx_string("circle_gif"),
      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
      ngx_http_circle_gif,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

    { ngx_string("circle_gif_min_radius"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_num_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_circle_gif_loc_conf_t, min_radius),
      NULL },
      ...
      ngx_null_command
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/code&gt;

 

 
А вот определение структуры &lt;code&gt;ngx_command_t&lt;/code&gt; (той, что мы сейчас заполняем), оно взято из файла &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/core/ngx_conf_file.h#L77"&gt;core/ngx_conf_file.h&lt;/a&gt;:

&lt;code&gt;
&lt;pre&gt;&lt;code class="cpp"&gt;
struct ngx_command_t {
    ngx_str_t             name;
    ngx_uint_t            type;
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    ngx_uint_t            conf;
    ngx_uint_t            offset;
    void                 *post;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/code&gt;

 

 
На первый взгляд слишком много, но у каждого поля свое назначение.

В &lt;code&gt;name&lt;/code&gt; хранится имя директивы, обязательно без пробелов. Используется тип &lt;code&gt;ngx_str_t&lt;/code&gt;, значение которого чаще всего создаются с помощью макроса &lt;code&gt;ngx_str("proxy_pass")&lt;/code&gt;. Замечание: структура &lt;code&gt;ngx_str_t&lt;/code&gt; состоит из поля &lt;code&gt;data&lt;/code&gt;, которое содержит саму строку, и поля &lt;code&gt;len&lt;/code&gt;, в котором хранится длина строки. В большинстве случаев Nginx использует эту структуру взамен обычных строк.

Значение &lt;code&gt;type&lt;/code&gt; задается с помощью набора флагов, которые определяют, где можно использовать эту директиву, и сколько она принимает параметров. Значение получается с помощью бинарного или:
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;NGX_HTTP_MAIN_CONF&lt;/code&gt;: разрешает использовать директиву в главном контексте&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;NGX_HTTP_SRV_CONF&lt;/code&gt;: в контексте сервера (хоста)&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;NGX_HTTP_LOC_CONF&lt;/code&gt;: в контексте локешна&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;NGX_HTTP_UPS_CONF&lt;/code&gt;: в контексте апстрима&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;NGX_CONF_NOARGS&lt;/code&gt;: сообщает, что директива не принимает аргументы&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;NGX_CONF_TAKE1&lt;/code&gt;: принимает ровно 1 аргументы&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;NGX_CONF_TAKE2&lt;/code&gt;: принимает ровно 2 аргумента&lt;/li&gt;
	&lt;li&gt;…&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;NGX_CONF_TAKE7&lt;/code&gt;: принимает ровно 7 аргументов&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;NGX_CONF_FLAG&lt;/code&gt;: тип аргумента должен быть булев??? ("on" или "off")&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;NGX_CONF_1MORE&lt;/code&gt;: директиве принимает 1 или более аргументов&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;NGX_CONF_2MORE&lt;/code&gt;: директиве принимает 2 или более аргументов&lt;/li&gt;
&lt;/ul&gt;
Есть еще несколько опций, спотрите &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/core/ngx_conf_file.h#L1"&gt;core/ngx_conf_file.h&lt;/a&gt;.

В элементе структуры &lt;code&gt;set&lt;/code&gt; хранится указатель на функцию, вызываемую для настройки какой-то части модуля; обычно, эта функция преводит данные из аргументов в удобный формат и сохраняет их в соответстующей структуре конфигурации модуля. Функция принимает три аргумента:
&lt;ol&gt;
	&lt;li&gt;указатель на структуру &lt;code&gt;ngx_conf_t&lt;/code&gt;, которая содержит переданные директиве аргументы&lt;/li&gt;
	&lt;li&gt;указатель на текущую структуру &lt;code&gt;ngx_command_t&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;указатель на собственную структуру конфигурации модуля&lt;/li&gt;
&lt;/ol&gt;
Функция будет вызвана тогда, когда соответствующая директива будет встречена в конфигурационном файле. Nginx предосталяет набор функций для перевода разных типов параметров в структуру для последующей обработки. Среди этих функций хочу виделить следуюющие:
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;ngx_conf_set_flag_slot&lt;/code&gt;: переводит "on" или "off" в 1 или 0&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;ngx_conf_set_str_slot&lt;/code&gt;: певодит строку параметра в &lt;code&gt;ngx_str_t&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;ngx_conf_set_num_slot&lt;/code&gt;: парсит число и возвращает его как &lt;code&gt;int&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code&gt;ngx_conf_set_size_slot&lt;/code&gt;: парсит размер ("8k", "1m" и т.д.) и возвращает его как &lt;code&gt;size_t&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
Есть еще несколько функций, и они тоже очень удобны (посотрите &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/core/ngx_conf_file.h#L329"&gt;core/ngx_conf_file.h&lt;/a&gt;). Модули могут описывать здесь и свои функции, если по каким-то причинам не достаточно встроенных.

Откуда эти функции узнают, куда сохранять данные? В этом им помогают следующие два свойстваструктуры &lt;code&gt;ngx_command_t&lt;/code&gt;: &lt;code&gt;conf&lt;/code&gt; и &lt;code&gt;offset&lt;/code&gt;. &lt;code&gt;conf&lt;/code&gt; указывае Nginx'у куда сохранить данные: в главную, серверную или конфигурационную структуру локешна (задается с помощью &lt;code&gt;NGX_HTTP_MAIN_CONF_OFFSET&lt;/code&gt;, &lt;code&gt;NGX_HTTP_SRV_CONF_OFFSET&lt;/code&gt;, or &lt;code&gt;NGX_HTTP_LOC_CONF_OFFSET&lt;/code&gt;). &lt;code&gt;offset&lt;/code&gt; указывает в какую часть структуры записать значение.

И,&lt;em&gt;наконец&lt;/em&gt;, &lt;code&gt;post&lt;/code&gt; это еще одна штукенция, которая может пригодится модулю на этапе конфигурации. Чаще всего равна &lt;code&gt;NULL&lt;/code&gt;.

Набор директив заканчивается &lt;code&gt;ngx_null_command&lt;/code&gt;.

&lt;a name="context"&gt;&lt;/a&gt;
&lt;h3&gt;2.3. Контекст модуля&lt;/h3&gt;
Это статическая структура типа &lt;code&gt;ngx_http_module_t&lt;/code&gt;, в которой определяются несколько указателей на функции для создание трех конфигураций и сливания их вместе. Называют ее &lt;code&gt;ngx_http_&amp;lt;имя_модуля&amp;gt;_module_ctx&lt;/code&gt;. Вот назначение этих функций по порядку:
&lt;ul&gt;
	&lt;li&gt;перед конфигурацией&lt;/li&gt;
	&lt;li&gt;после конфигурации&lt;/li&gt;
	&lt;li&gt;создание главной конфигурации (то есть выделение памяти и задание значений по умолчанию)&lt;/li&gt;
	&lt;li&gt;инициализация главной конфигурации (переопределение данных на взятые из nginx.conf)&lt;/li&gt;
	&lt;li&gt;создание конфигурации сервера&lt;/li&gt;
	&lt;li&gt;сливание ее с главной конфигурацие&lt;/li&gt;
	&lt;li&gt;создание конфигурации локешна&lt;/li&gt;
	&lt;li&gt;сливание ее с конфигурацие сервера&lt;/li&gt;
&lt;/ul&gt;
Функции принимают разные аргументы в зависимости от своего назначения. Вот описание этой структуры, взятое из &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/http/ngx_http_config.h#L22"&gt;http/ngx_http_config.h&lt;/a&gt;, в котором видны разные сигнатуры функций-колбеков:

&lt;code&gt;
&lt;pre&gt;&lt;code class="cpp"&gt;
typedef struct {
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);

    void       *(*create_main_conf)(ngx_conf_t *cf);
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);

    void       *(*create_srv_conf)(ngx_conf_t *cf);
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);

    void       *(*create_loc_conf)(ngx_conf_t *cf);
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;/code&gt;

 

 
Указатели на те функции, которые вам не пригодятся, можете заполнить &lt;code&gt;NULL&lt;/code&gt;, и Nginx сделает вид, что не заметил их.

Большинство обработчиков используют только две последние: чтобы выделить память для структуры конфигурации (называется &lt;code&gt;ngx_http_&amp;lt;имя_модуля&amp;gt;_create_loc_conf&lt;/code&gt;), и слить ее с конфигурацией выше (называется &lt;code&gt;ngx_http_&amp;lt;имя_модуля&amp;gt;_merge_loc_conf&lt;/code&gt;). Функция, сливающая вместе конфиги, также может вернуть ошибку, что остановит загрузку сервера.

Вот пример пример структуры контекста модуля:

&lt;pre&gt;&lt;code class="cpp"&gt;
static ngx_http_module_t  ngx_http_circle_gif_module_ctx = {
    NULL,                          /* перед конфигурацией */
    NULL,                          /* после конфигурации */

    NULL,                          /* создание главной конфигурации */
    NULL,                          /* инициализация главной конфигурации */

    NULL,                          /* создание конфигурации сервера */
    NULL,                          /* сливание ее с главной конфигурацие */

    ngx_http_circle_gif_create_loc_conf,  /* создание конфигурации локешна */
    ngx_http_circle_gif_merge_loc_conf /* сливание ее с конфигурацие сервера */
};
&lt;/code&gt;&lt;/pre&gt;

 

 
Пришло время разобраться со всем этим подробнее. Эти конфигурационные колбеки очень похожи во всех модулях и используют одну часть Nginx API, так что их надо хорошо знать.

&lt;a name="create_loc_conf"&gt;&lt;/a&gt;
&lt;h4&gt;2.3.1. create_loc_conf&lt;/h4&gt;
Вот так выглядит минимальная реализация функции &lt;code&gt;create_loc_conf&lt;/code&gt;, взятая из моего модуля circle_gif (за подробностями прошу &lt;a href="http://www.riceonfire.org/emiller/ngx_http_circle_gif_module.c.txt"&gt;в исходник&lt;/a&gt;). Она получает структуру (&lt;code&gt;ngx_conf_t&lt;/code&gt;) и возвращает вновь созданную структуру конфигурации модуля (в этом примере &lt;code&gt;ngx_http_circle_gif_loc_conf_t&lt;/code&gt;).

&lt;pre&gt;&lt;code class="cpp"&gt;static void *
ngx_http_circle_gif_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_circle_gif_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf-&amp;gt;pool, sizeof(ngx_http_circle_gif_loc_conf_t));
    if (conf == NULL) {
        return NGX_CONF_ERROR;
    }
    conf-&amp;gt;min_radius = NGX_CONF_UNSET_UINT;
    conf-&amp;gt;max_radius = NGX_CONF_UNSET_UINT;
    return conf;
}&lt;/code&gt;&lt;/pre&gt;

 

 
Прошу заметить важную особенность управления памятью в Nginx'е: он сам позаботится о вызове &lt;code&gt;free&lt;/code&gt; только тогда, когда для выделения памяти вы используете &lt;code&gt;ngx_palloc&lt;/code&gt; (умную обертку для &lt;code&gt;malloc&lt;/code&gt;) или &lt;code&gt;ngx_pcalloc&lt;/code&gt; (умную обертку для &lt;code&gt;calloc&lt;/code&gt;).

Возможными вариантами задания UNSET являются: &lt;code&gt;NGX_CONF_UNSET_UINT&lt;/code&gt;, &lt;code&gt;NGX_CONF_UNSET_PTR&lt;/code&gt;, &lt;code&gt;NGX_CONF_UNSET_SIZE&lt;/code&gt;, &lt;code&gt;NGX_CONF_UNSET_MSEC&lt;/code&gt;, и для всех случаев &lt;code&gt;NGX_CONF_UNSET&lt;/code&gt;. UNSET показывает функции сливания конфигов, что это значение надо переопределить.

&lt;a name="merge_loc_conf"&gt;&lt;/a&gt;
&lt;h4&gt;2.3.2. merge_loc_conf&lt;/h4&gt;
Вот так выглядит функция сливания конфигов в модуле circle_gif:

&lt;pre&gt;&lt;code class="cpp"&gt;static char *
ngx_http_circle_gif_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_circle_gif_loc_conf_t *prev = parent;
    ngx_http_circle_gif_loc_conf_t *conf = child;

    ngx_conf_merge_uint_value(conf-&amp;gt;min_radius, prev-&amp;gt;min_radius, 10);
    ngx_conf_merge_uint_value(conf-&amp;gt;max_radius, prev-&amp;gt;max_radius, 20);

    if (conf-&amp;gt;min_radius &amp;lt; 1) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
            "min_radius должен быть больше или равен 1");
        return NGX_CONF_ERROR;
    }
    if (conf-&amp;gt;max_radius &amp;lt; conf-&amp;gt;min_radius) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
            "max_radius должен быть больше или равен min_radius");
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}
&lt;/code&gt;&lt;/pre&gt;

 

 
Приятной особенностью Nginx'а является набор функций для сливания разных типов данных (&lt;code&gt;ngx_conf_merge_&amp;lt;тип_данных&amp;gt;_value&lt;/code&gt;); их аргументами являются
&lt;ol&gt;
	&lt;li&gt;значение &lt;em&gt;текущего&lt;/em&gt; локешна&lt;/li&gt;
	&lt;li&gt;значение, задаваемое, если #1 не установлено&lt;/li&gt;
	&lt;li&gt;задаваемое по умолчанию, если не установлено ни #1, ни #2&lt;/li&gt;
&lt;/ol&gt;
Результат будет записан в первый аргумент. Примерами таких функций являются &lt;code&gt;ngx_conf_merge_size_value&lt;/code&gt;, &lt;code&gt;ngx_conf_merge_msec_value&lt;/code&gt; и другие. Обратитесь к &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/core/ngx_conf_file.h#L254"&gt;core/ngx_conf_file.h&lt;/a&gt; за полным списком.
&lt;div class="aside"&gt;

А вот вопрос: как же этим функциям удается записать данные в первый аргумент, если он передается по значению?
Ответ прост: это макросы (они разворачиваются в несколько конструкций &lt;code&gt;if&lt;/code&gt;).&lt;/div&gt;
Интересно так же то, как эти функции обрабатывают ошибки. В случае неудачи в лог выводится сообщение, и функция возвращает &lt;code&gt;NGX_CONF_ERROR&lt;/code&gt;. В этом случает запуск сервера прекращается. Так как сообщение выводится с уровнем &lt;code&gt;NGX_LOG_EMERG&lt;/code&gt;, оно будет продублировано в поток ошибок. Кстати, &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/core/ngx_log.h#L1"&gt;core/ngx_log.h&lt;/a&gt; содержит полный список уровнй вывода.)

&lt;a name="definition"&gt;&lt;/a&gt;
&lt;h3&gt;2.4. Описание модуля&lt;/h3&gt;

Теперь добавим еще один уровель абстракции, структуру &lt;code&gt;ngx_module_t&lt;/code&gt;. Переменную назовем &lt;code&gt;ngx_http_&amp;lt;имя_модуля&amp;gt;_module&lt;/code&gt;. В ней описываются указатели на контекст и директивы модуля вместе с остальными колбеками(завершение треда, завершение процесса и т.д.). Определение модуля иногда используется, чтобы найти какие-либо данные, связанные с этим модулем. Определение модуля часто выглядит так:

&lt;pre&gt;&lt;code class="cpp"&gt;
ngx_module_t  ngx_http_&amp;lt;имя_модуля&amp;gt;_module = {
    NGX_MODULE_V1,
    &amp;amp;ngx_http_&amp;lt;имя_модуля&amp;gt;_module_ctx, /* контекст модуля */
    ngx_http_&amp;lt;module name&amp;gt;_commands,   /* директивы модуля */
    NGX_HTTP_MODULE,               /* тип модуля */
    NULL,                          /* инициализация мастера */
    NULL,                          /* инициализация модуля */
    NULL,                          /* инициализация процесса */
    NULL,                          /* инициализация треда */
    NULL,                          /* завершение треда */
    NULL,                          /* завершение процесса */
    NULL,                          /* завершение мастера */
    NGX_MODULE_V1_PADDING
};
&lt;/code&gt;&lt;/pre&gt;

 

 

…замените &amp;lt;имя_модуля&amp;gt; на что-нибудь полезное. Модули погут определять колбеки для моментов создания и уничтожения процессов и тредов, но большинство модулей стараются не усложнять себе жизнь. Чтобы посмотреть список аргументов, обратитесь к &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/core/ngx_conf_file.h#L110"&gt;core/ngx_conf_file.h&lt;/a&gt;.)

&lt;a name="installation"&gt;&lt;/a&gt;
&lt;h3&gt;2.5. Установка модуля&lt;/h3&gt;
Модули устанавливаются двумя способами: обработчики чаще всего устанавливаются колбеком директивы, а фильтры устанавливаются в постконфигурационном колбеке в структуре контекста модуля. &lt;em&gt;Наконец&lt;/em&gt;, мы собираемся указать Nginx'у, где искать наш код. Балансировщики в этом вопросе особенные, их мы рассмотрим позже в разделе &lt;a href="#load_balancers"&gt;Устройство балансировщиков нагрузки&lt;/a&gt;.

&lt;a name="handler-installation"&gt;&lt;/a&gt;
&lt;h4&gt;2.5.1. Установка обработчика&lt;/h4&gt;

Обработчики устанавливаются с помощью кода внутри колбеков, вызываемых директивами, которые относятся к модулю. Например, моя структура &lt;code&gt;ngx_command_t&lt;/code&gt; из модуля circle_gif выглядит примерно так:

&lt;pre&gt;&lt;code class="cpp"&gt;
    { ngx_string("circle_gif"),
      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
      ngx_http_circle_gif,
      0,
      0,
      NULL }
&lt;/code&gt;&lt;/pre&gt;




Третьим аргументом как раз является колбек, в примере это &lt;code&gt;ngx_http_circle_gif&lt;/code&gt;. Вспомним, что аргументами этого колбека являются: структура директивы (&lt;code&gt;ngx_conf_t&lt;/code&gt;, в кторой сохранены параметры из конфигурационного файла), соответствующая структура &lt;code&gt;ngx_command_t&lt;/code&gt; и указатель на структуру конфигурации модуля. В моем модуле circle_gif эта функция выглядит так:

&lt;pre&gt;&lt;code class="cpp"&gt;
static char *
ngx_http_circle_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t  *clcf;

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf-&amp;gt;handler = ngx_http_circle_gif_handler;

    return NGX_CONF_OK;
}
&lt;/code&gt;&lt;/pre&gt;

 

 
Она выполняет работу в два этапа. Во-первых, получает внутреннюю структуру описывающую этот локешн. Во-вторых, устанавливает в ней обработчик (тоже колбек). Просто, не правда ли?

&lt;a name="filter-installation"&gt;&lt;/a&gt;
&lt;h4&gt;2.5.2. Установка фильтра&lt;/h4&gt;
Фльтры устанавливаются на этапе постконфигурации. Бывает два типа фильтров: &lt;em&gt;фильтры заголовков&lt;/em&gt; которые обрабатывают HTTP-заголовки, и &lt;em&gt;фильтры тела ответа&lt;/em&gt;, которые обрабатывают собственно данные. Мы устанавливаем оба за один раз.

В качестве простого примера посмотрим на chunked-фильтр, его контекст выглядит так:

&lt;pre&gt;&lt;code class="cpp"&gt;
static ngx_http_module_t  ngx_http_chunked_filter_module_ctx = {
    NULL,                                  /* preconfiguration */
    ngx_http_chunked_filter_init,          /* postconfiguration */
  ...
};
&lt;/code&gt;&lt;/pre&gt;

 

 
Вот что происходит в &lt;code&gt;ngx_http_chunked_filter_init&lt;/code&gt;:

&lt;pre&gt;&lt;code class="cpp"&gt;static ngx_int_t
ngx_http_chunked_filter_init(ngx_conf_t *cf)
{
    ngx_http_next_header_filter = ngx_http_top_header_filter;
    ngx_http_top_header_filter = ngx_http_chunked_header_filter;

    ngx_http_next_body_filter = ngx_http_top_body_filter;
    ngx_http_top_body_filter = ngx_http_chunked_body_filter;

    return NGX_OK;
}
&lt;/code&gt;&lt;/pre&gt;

 

 
Что это значит? Если вы помните, фильтры работают по принципу цепочки обязанностей. Когда обработчик сгенерирует ответ, он вызывает две функции: &lt;code&gt;ngx_http_output_filter&lt;/code&gt;, которая вызывает глобальную &lt;code&gt;ngx_http_top_body_filter&lt;/code&gt;; и &lt;code&gt;ngx_http_send_header&lt;/code&gt;, вызывает другую глобальную &lt;code&gt;ngx_top_header_filter&lt;/code&gt;.

Функции &lt;code&gt;ngx_http_top_body_filter&lt;/code&gt; и &lt;code&gt;ngx_http_top_header_filter&lt;/code&gt; являются гловными в цепочках фильтров соответственно заголовка и тела ответа. Каждое звено в цепи содержит ссылку на следующее звено (ссылки называются &lt;code&gt;ngx_http_next_body_filter&lt;/code&gt; и &lt;code&gt;ngx_http_next_header_filter&lt;/code&gt;). Когда фильтр закончит выполнение, он просто вызывает следующий, пока не будет вызван специальный фильтр, который уже заворачивает данные в HTTP-ответ. Все что делает функция filter_init, это добавляет свой модуль в обе эти цепи. Эта функция сохраняет ссылку на бывший первым фильтр в своей собственной переменной и определяет &lt;em&gt;свои&lt;/em&gt; колбеки, как первые в цепочках. Цепочка действует по принципу LIFO (Last In Frist Out): последним добавлен — первым обработан.
&lt;div class="aside"&gt;

Заметка на полях: а как точно это работает?
Каждый фильтр возвращает либо ошибку, либо результат специального выражения:
&lt;code&gt;return ngx_http_next_body_filter();&lt;/code&gt;


Таким обазом, если очередь в цепочке фильтров дошла до последнего («специального») фильтра, просто возвращается "OK", но если проихошла ошибка, оставшаяся цепочка пропускается и Nginx выводит соответствующее сообщение об ошибке. Это простой однонаправленный список с бастрой обработкой ошибок, выполненный в виде указателей на функции. Превосходно.&lt;/div&gt;
&lt;a name="types"&gt;&lt;/a&gt;
&lt;h2&gt;3. Обработчики, фильтры и балансировщики нагрузки&lt;/h2&gt;
Теперь рассмотрим пару простейших модулей под микроскопом и разберемся как они работает

&lt;a name="non-proxying"&gt;&lt;/a&gt;
&lt;h3&gt;3.1. Устройство обработчиков (не проксирующих)&lt;/h3&gt;
Обработчики, обычно, выполняют четыре шага: получают конфигурацию локешна, генерируют соответствующий ответ, отправляют заголовки и отправляют тело ответа. Хентдлер получает один аргумент: структуру запроса. В структуре запроса хранится много молезной информации о запросе клиента. Такой как, метод запроса, URI и загловки. Мы рассмотрим эти четыре шага один за другим.

&lt;a name="non-proxying-config"&gt;&lt;/a&gt;
&lt;h4&gt;3.1.1. Получение конфигурации локейшна&lt;/h4&gt;
Это простая часть. Все, что надо сделать, это вызвать &lt;code&gt;ngx_http_get_module_loc_conf&lt;/code&gt; и передать параметрами текущий запрос и описание модуля. Вот соответствующая часть хендлера из моего модуля circle_gif:

&lt;pre&gt;&lt;code class="cpp"&gt;
static ngx_int_t
ngx_http_circle_gif_handler(ngx_http_request_t *r)
{
    ngx_http_circle_gif_loc_conf_t  *circle_gif_config;
    circle_gif_config = ngx_http_get_module_loc_conf(r, ngx_http_circle_gif_module);
    ...
&lt;/code&gt;&lt;/pre&gt;

 

 
Вот так я получил доступ ко всем переменным, сохраненным на этапе сливания конфигов.

&lt;a name="non-proxying-response"&gt;&lt;/a&gt;
&lt;h4&gt;3.1.2. Генерация ответа&lt;/h4&gt;
Это та самая интересная часть, где модули делают полезную работу.
Нам поможет стуктура запроса, а именно эти ее свойства:

&lt;pre&gt;&lt;code class="cpp"&gt;
typedef struct {
...
/* пул памяти, используемый в функциях типа ngx_palloc */
    ngx_pool_t                       *pool;
    ngx_str_t                         uri;
    ngx_str_t                         args;
    ngx_http_headers_in_t             headers_in;

...
} ngx_http_request_t;
&lt;/code&gt;&lt;/pre&gt;

 

 

&lt;code&gt;uri&lt;/code&gt; это путь запроса, например "/query.cgi".

&lt;code&gt;args&lt;/code&gt; содержит нераспарсенные параметры запроса (идущая за знаком вопроса часть), например "name=john".

&lt;code&gt;headers_in&lt;/code&gt; хранит много полезной информации, такой как куки и информация о браузере, но большинству модулей эти данные не пригождаюися. Посмотрите &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/http/ngx_http_request.h#L158"&gt;http/ngx_http_request.h&lt;/a&gt;, если интересно.

Этого вполне достаточно, чтобы смочь составить какой-нибудь полезный ответ. Полное описание структуры &lt;code&gt;ngx_http_request_t&lt;/code&gt; можно найти в &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/http/ngx_http_request.h#L316"&gt;http/ngx_http_request.h&lt;/a&gt;.

&lt;a name="non-proxying-header"&gt;&lt;/a&gt;
&lt;h4&gt;3.1.3. Отправка заголовков&lt;/h4&gt;
Заголовки ответа живут в структуре называемой &lt;code&gt;headers_out&lt;/code&gt;. Она в свою очередь хранится в структуре запроса. Обработчик хапроса выставляет те заголовки, которые ему надо и вызывает &lt;code&gt;ngx_http_send_header(r)&lt;/code&gt;. Вот некоторые из самых полезный элементов &lt;code&gt;headers_out&lt;/code&gt;:

&lt;pre&gt;
&lt;code class="cpp"&gt;typedef stuct {
...
    ngx_uint_t                        status;
    size_t                            content_type_len;
    ngx_str_t                         content_type;
    ngx_table_elt_t                  *content_encoding;
    off_t                             content_length_n;
    time_t                            date_time;
    time_t                            last_modified_time;
..
} ngx_http_headers_out_t;
&lt;/code&gt;&lt;/pre&gt;

 

 

Остальное можно найти в &lt;a class="source" href="http://www.riceonfire.org/lxr/http/source/http/ngx_http_request.h#L220"&gt;http/ngx_http_request.h&lt;/a&gt;.

Так, например, если модуль должен выставить Content-Type в "image/gif", Content-Length в 100 и вернуть код ответа 200 OK, то следующий код поможет ему сделать это:

&lt;pre&gt;&lt;code class="cpp"&gt;
    r-&amp;gt;headers_out.status = NGX_HTTP_OK;
    r-&amp;gt;headers_out.content_length_n = 100;
    r-&amp;gt;headers_out.content_type.len = sizeof("image/gif") - 1;
    r-&amp;gt;headers_out.content_type.data = (u_char *) "image/gif";
    ngx_http_send_header(r);
&lt;/code&gt;&lt;/pre&gt;

 

 
Большинство стандартных загловоков HTTP доступны (где-либо) для изменения вами. Однако, некоторые хаголовки задать немного сложнее чем те, которые вы видели выше. На пример, &lt;code&gt;content_encoding&lt;/code&gt; имеет тип &lt;code&gt;(ngx_table_elt_t*)&lt;/code&gt;, поэтому модуль должен сам выделить память для этого заголовка. Это можно сделать с помощью функции &lt;code&gt;ngx_list_push&lt;/code&gt;, которая принимает &lt;code&gt;ngx_list_t&lt;/code&gt; (похож на массив) и возвращает указатель на вновь созданный элемен в этом списке (типа &lt;code&gt;ngx_table_elt_t&lt;/code&gt;). Код ниже устанавливает заголовок Content-Encoding в значение "deflate" и отправляет заголовки:

&lt;pre&gt;&lt;code class="cpp"&gt;
    r-&amp;gt;headers_out.content_encoding = ngx_list_push(&amp;amp;r-&amp;gt;headers_out.headers);
    if (r-&amp;gt;headers_out.content_encoding == NULL) {
        return NGX_ERROR;
    }
    r-&amp;gt;headers_out.content_encoding-&amp;gt;hash = 1;
    r-&amp;gt;headers_out.content_encoding-&amp;gt;key.len = sizeof("Content-Encoding") - 1;
    r-&amp;gt;headers_out.content_encoding-&amp;gt;key.data = (u_char *) "Content-Encoding";
    r-&amp;gt;headers_out.content_encoding-&amp;gt;value.len = sizeof("deflate") - 1;
    r-&amp;gt;headers_out.content_encoding-&amp;gt;value.data = (u_char *) "deflate";
    ngx_http_send_header(r);
&lt;/code&gt;&lt;/pre&gt;

 

 
Этот механизм, обычно, используется тогда, когда заголовок может иметь более одного значения одновременно. Этот прием (теоретически) позволяет фильтрам легче добавлять или удалять соответствующие значения, не изменяя другие, так как им не приходится заниматься работой со строками.

&lt;a name="non-proxying-body"&gt;&lt;/a&gt;
&lt;h4&gt;3.1.4. Отправка тела ответа&lt;/h4&gt;

Теперь, когда модуль сгенерировал ответ и записал его в память, ему необходимо присвоить овет специальному буферу и затем, передать буфер в специальное &lt;em&gt;звено чепочки&lt;/em&gt;, а &lt;em&gt;потом&lt;/em&gt; вызвать «отправку ответа» на этом звене.

Зачем нужна звенья и цепочка? Nginx позволяет обработчикам генерировать (а фильтрам обрабатывать) ответ по одному буферу за раз. Каждое звено цепи хранит ссылку на следующие звен или &lt;code&gt;NULL&lt;/code&gt; если оно последнее. Чтобы не усложнять пример, предстваим, что у нас есть только один буфер (и одно звено цепи).

Сначала модуль должен объявить буфер и звено цепи.

&lt;pre&gt;&lt;code class="cpp"&gt;
    ngx_buf_t    *b;
    ngx_chain_t   out;
&lt;/code&gt;&lt;/pre&gt;


Следующим шагом надо выделить память для буфера и добавить его в данные ответа:

&lt;pre&gt;&lt;code class="cpp"&gt;
    b = ngx_pcalloc(r-&amp;gt;pool, sizeof(ngx_buf_t));
    if (b == NULL) {
        ngx_log_error(NGX_LOG_ERR, r-&amp;gt;connection-&amp;gt;log, 0,
            "Не удалось выделить буфер ответа.");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    b-&amp;gt;pos = some_bytes; /* позиция первого байта в блоке данных */
    b-&amp;gt;last = some_bytes + some_bytes_length; /* последняя позиция */

    b-&amp;gt;memory = 1; /* данные храняться в памяти только для чтения */
    /* (то есть фильтры должны скопировать эти данные перед обработкой, вместо того, чтобы изменять их) */

    b-&amp;gt;last_buf = 1; /* буферов в запросе больше не будет */
&lt;/code&gt;&lt;/pre&gt;

 

 
А здесь модуль присваивает буфер звену цепи:

&lt;pre&gt;&lt;code class="cpp"&gt;
    out.buf = b;
    out.next = NULL;
&lt;/code&gt;&lt;/pre&gt;

 

 

И наконец, мы отправляем ответ и возвращаем статут вызова отправки за один раз:

&lt;pre&gt;&lt;code class="cpp"&gt;
    return ngx_http_output_filter(r, &amp;amp;out);
&lt;/code&gt;&lt;/pre&gt;

 

 
Цепочки буферов — это критически важная часть модели ввода/вывода в Nginx'е, так что вы должны ими хорошо овладеть.
&lt;div class="aside"&gt;

Очевидный вопрос: зачем буферу флаг &lt;code&gt;last_buf&lt;/code&gt;, если мы можем определить, что он последний проверив "next" на &lt;code&gt;NULL&lt;/code&gt;?

Ответ: цепь может быть незавершенной, то есть состоять из множества буферов, не не все буферы уже подготовлены в запросе или ответе. Таким образом, некоторые буферы будут в конце цепи, но не в конце запроса. И это приводит нас к…&lt;/div&gt;
&lt;h3&gt;3.2. Устройство апстримов (они же прокси)&lt;/h3&gt;
Продолжение следует.&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/zFI2C9K9P0o" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>CMS Builder жив</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/09/06/cms-builder-alive" />
	<updated>2008-09-06T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/09/06/cms-builder-alive</id>
	<content type="html">&lt;p&gt;По запросу «cms builder» держу 4, 5 и 7 места &lt;a href="http://www.google.com/search?ie=UTF-8&amp;amp;hl=ru&amp;amp;q=cms%20builder"&gt;в гугле &lt;/a&gt;, и второе &lt;a href="http://yandex.ru/yandsearch?rpt=rad&amp;amp;text=cms%20builder"&gt;в яндексе&lt;/a&gt;. И люди ходят даже. Приятно, что кто-то еще не забыл нашего гиганта. Полезный был. Мы Builder сейчас нигде не используем. Мы и CMS вообще не используем :)&lt;/p&gt;
&lt;p&gt;Однако, что-то из билдера все-таки пригодится еще, чует мое сердце…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPD 2010-11-29&lt;/strong&gt;: пока пригодился только опыт серверного программирования вообще.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/d0Km0jSbupw" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>highlight.js как расширение для Firefox</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/09/05/highlightjs-as-firefox-extension" />
	<updated>2008-09-05T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/09/05/highlightjs-as-firefox-extension</id>
	<content type="html">&lt;p&gt;Около года назад заинтересовался библиотекой &lt;a href="http://softwaremaniacs.org/soft/highlight/"&gt;highlight.js&lt;/a&gt;, написанной &lt;a href="http://softwaremaniacs.org/about/"&gt;Иваном Сагалаевым&lt;/a&gt;. Да так заинтересовался, что решил обернуть highlight.js в расширение для фаерфокса. Пусть она озаряет код на непросвещенных сайта, подумалось мне тогда. Спустя год вышел Firefox 3.0, который весь чуть-чуть другой. Многие расширения в нем сломались, в том числе и это.&lt;/p&gt;

&lt;p&gt;Основное различие между версиями расширения для Firefox 2.0 и 3.0 в том, как подключаются стили и библиотека. Подключать к страничке файлы по протоколу &lt;code&gt;chrome://&lt;/code&gt; теперь нельзя, а надо использовать новый способ с протоколом &lt;code&gt;resources://&lt;/code&gt;. Это должно укрепить безопасность, стабильность и вообще. А еще встраивать highlight.js стало значительно удобнее, так как Иван сделал его самостоятельным модулем. Подробнее можете посмотреть в исходниках.&lt;/p&gt;

&lt;p&gt;Новое расширение &lt;a href="i/highlighter-4.2.1.xpi"&gt;highlighter-4.2.1.xpi&lt;/a&gt; для Firefox 3.0.&lt;/p&gt;
&lt;p&gt;Старое расширение &lt;a href="i/highlighter-3.5.xpi"&gt;highlighter-3.5.xpi&lt;/a&gt; для Firefox 2.0.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/EL97oRbp3GE" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Новенькое в Google AJAX API</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/09/03/new-in-google-ajax-api" />
	<updated>2008-09-03T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/09/03/new-in-google-ajax-api</id>
	<content type="html">&lt;p&gt;Теперь можно загружать любой апи по желанию на любом этапе работы скриптов: &lt;a href="http://code.google.com/apis/ajax/documentation/#Dynamic"&gt;Dynamic Loading&lt;/a&gt;. Раньше можно было только в head, на этапе загрузки (если не подхачить маленько). Даже загрузку самого загрузчика можно отложить.&lt;/p&gt;

&lt;p&gt;А еще появилась (&lt;strong&gt;UPD 2011-01-10&lt;/strong&gt;: и куда-то пропала) возможность узнать &lt;a href="http://code.google.com/apis/ajax/documentation/#ClientLocation"&gt;географическое расположение&lt;/a&gt; клиента. Приблизительно, конечно, но зато просто, быстро и бесплатно.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/ALk8mxN8HZE" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>Замена meta imagetoolbar</title>
	<link href="http://kung-fu-tzu.ru/posts/2008/09/03/meta-imagetoolbar-replacement" />
	<updated>2008-09-03T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2008/09/03/meta-imagetoolbar-replacement</id>
	<content type="html">&lt;p&gt;Не хотите ставить &lt;code&gt;meta http-equiv="imagetoolbar" content="no"&lt;/code&gt; на всех страницах? Не срабатывает &lt;code&gt;set_header&lt;/code&gt;? Тогда поможет экспрешн.&lt;/p&gt;

&lt;p&gt;&lt;pre&gt;&lt;code&gt;
img
{
  scrollbar-highlight-color: expression
  (
    (runtimeStyle.scrollbarHighlightColor = "transparent"),
    (this.galleryImg='no')
  )
}

&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Ключевой момент — это &lt;code&gt;this.galleryImg='no'&lt;/code&gt;. По строкам я разбил для красоты, перед использованием их надо объединить в одну.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/ms533774(VS.85).aspx"&gt;MSDN про galleryImg&lt;/a&gt;&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/Hx9NJVF2xzs" height="1" width="1"/&gt;</content>
</entry>

<entry>
	<title>XHTML шепчу…</title>
	<link href="http://kung-fu-tzu.ru/posts/2007/10/12/xhtml-whisper" />
	<updated>2007-10-12T00:00:00-07:00</updated>
	<id>http://kung-fu-tzu.ru/posts/2007/10/12/xhtml-whisper</id>
	<content type="html">&lt;p&gt;Сегодня утром вернулся к выводу RSS-ленты &lt;a href="http://www.programica.ru/"&gt;на сайте Програмики&lt;/a&gt;. И обнаружил, что &lt;code&gt;innerHTML&lt;/code&gt; меня не спасает. Что же делать? Решение есть, но сначала хорошенько опишу проблему.&lt;/p&gt;

&lt;h3&gt;XHTML&lt;/h3&gt;
&lt;p&gt;С недавних пор мы решили верстать сайты в XHTML. Затея не нова, скажете вы, — сейчас каждый уважающий себя верстальщик все делает по стандартам и о валидности не забывает. Однако, как мы можем узнать из статьи Ивана Сагалаева «&lt;a href="http://softwaremaniacs.org/blog/2005/12/19/xhtml-you-say/"&gt;XHTML, говорите?&lt;/a&gt;», не все так просто, и многие разработчики не подозревают, что верстают вовсе не в режиме XHTML, а в старом добром HTML–режиме. Дело в HTTP-заголовке Content-type, и вся свистопляска начинается только тогда, когда вы вместо привычного &lt;code&gt;text/html&lt;/code&gt; дадите браузеру нюхнуть &lt;code&gt;application/xhtml+xml&lt;/code&gt;. Или &lt;code&gt;text/xml&lt;/code&gt; на худой конец. Браузер сразу обнаружит, что вы, наконец, одумались и выбрали самый современный отформатированный формат документов и сопутствующие ему самые стандартные стандарты. Однако&amp;nbsp; прежней свою страницу вы не увидите. Делов-то, сейчас слеши в конце &amp;lt;br&amp;gt; и &amp;lt;img&amp;gt; поставим, и все. А не все. Когда вы начнете программировать, предварительно исправив разметку, указав пространство имен и разобравшись с кодировкой, вас настигнут бонусные неприятности с передовым форматом. Самое время вернуться к теме поста.&lt;/p&gt;

&lt;p&gt;Классическим примером разницы в движках HTML и XHTML было отсутствие&amp;nbsp; у XHTML-элементов свойства &lt;code&gt;innerHTML&lt;/code&gt;. То есть загрузить что-то аморфное аяксом и вывести в div стало нельзя. Свойство присваивается, JavaScript ведь этого не запрещает, но реакции никакой нет. Народ стал мучаться с интерфейсами &lt;code&gt;DOMParser&lt;/code&gt; и &lt;code&gt;DOMSerializer&lt;/code&gt;, чтобы эмулировать любимое свойство. И, честно говоря, некоторый успех был. Позже браузеры научились и сами имитировать &lt;code&gt;innerHTML&lt;/code&gt; в режиме XHTML (вероятнее всего тем же способом) и сейчас можно не опасаясь писать нечто вроде &lt;code&gt;$('content').innerHTML = '&amp;lt;h1&amp;gt;Сильно &amp;lt;i&amp;gt;лень&amp;lt;/i&amp;gt; городить createElement() и appendChild()&amp;lt;/h1&amp;gt;'&lt;/code&gt;. Хотя свойство и называется &lt;code&gt;innerHTML&lt;/code&gt; на самом деле HTML ему скормить нельзя. Это скорее &lt;code&gt;innerXHTML&lt;/code&gt;. То есть такое дело не пройдет: &lt;code&gt;mailDiv.innerHTML = '&amp;lt;p&amp;gt;забудем закрыть "p", поставим неразрывный&amp;amp;nbsp;пробел, а еще картинку вставим &amp;lt;img alt="постринке"&amp;gt;'&lt;/code&gt;. И что произойдет? В обычном режиме браузер напряжет свои HTML-извилины и догадается о том, что вы имели ввиду. В новом мире XML умен должен быть не браузер, а мы. А сам браузер, как ему и положено, откажется парсить такую гадость и выбросит исключение.&lt;/p&gt;

&lt;p&gt;&lt;dl&gt;
	&lt;dt&gt;Вот какие исключения бывают:&lt;/dt&gt;
	&lt;dd&gt;Firefox 2: An invalid or illegal string was specified&lt;/dd&gt;
	&lt;dd&gt;Firefox 3: Component returned failure code: 0x80004003 (NS_ERROR_INVALID_POINTER) [nsIDOMNSHTMLElement.innerHTML]&lt;/dd&gt;
	&lt;dd&gt;Safari 3: NO_MODIFICATION_ALLOWED_ERR: DOM Exception 7&lt;/dd&gt;
&lt;/dl&gt;&lt;/p&gt;

&lt;p&gt;Opera 9 умеет парсить HTML сама. За что нижайший ей поклон.&lt;/p&gt;

&lt;h3&gt;Великий браузер&lt;/h3&gt;
&lt;p&gt;Первое решение, которое пришло мне на ум, это поправить разметку простенькими регулярками.&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;
rex = /&amp;lt;(img|br|hr|link|input)(.+?)\/?&amp;gt;/g
markup = markup.replace(rex, '&amp;lt;$1$2/&amp;gt;').replace(/(&amp;amp;nbsp;)/g, '&amp;nbsp;')
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;После этого чать кода заработала. Потом из любопытства поглядел на список сущностей в HTML. А потом попался фрагмент кода с перемешанными тегами. Писать парсер перехотелось. Помучавшись в раздумьях, решил попробовать распарсить HTML на сервере, а потом выдать его как HTML. Идея мне понравилась, по натуре&amp;nbsp; я серверный программист и раньше воспринимал браузер только как дисплей. Потом, правда, одумался.&lt;/p&gt;

&lt;p&gt;Одуматься удалось и на этот раз. Зачем, спрашиввается, мне парсить HTML на сервере, если у меня в руках самый совершенный парсер HTML — собственно браузер. Не найдя рационального ответа на этот вопрос, решил парсить кривой HTML на клиенте. Вот только куда тиранический XML-режим сослал этот самый парсер — не понятно.&lt;/p&gt;

&lt;p&gt;Отступлю немного и отвечу на вопрос, ответ на который вас, возможно, интересует. Почему у меня HTML весь такой кривой и неправильный. Ответ прост как шланг — на нашем блоге мы решили парсить сырую RSS-выдачу. А получаем мы очень вольно составленный HTML.&lt;/p&gt;

&lt;p&gt;Так вот. Где же cпрятан парсер HTML, когда у нас все кругом в XHTML? Сегодня получить парсер в лисе, скриптом невозможно. Про вебкит и оперу по запросу "new HTMLDocument()" тоже ничего найти не удалось (искал, правда, спустя рукава — любимчик-то мой уже облажался). &lt;code&gt;XMLParser&lt;/code&gt;, как видно из названия, парсит только XML (правда это может быть и XUL, и MathML, и SVG и…). Создать новый HTML-документ с помощью &lt;code&gt;document.implementation.createDocument(…)&lt;/code&gt; можно, но будет он XHTML. Конструкция &lt;code&gt;(new DOMParser()).parseFromString("...", "text/html")&lt;/code&gt; не сработает, так как &lt;code&gt;DOMParser&lt;/code&gt; не поддерживает &lt;code&gt;text/html&lt;/code&gt;. Прямо так и пишет: not implemented.&lt;/p&gt;

&lt;p&gt;Промучавшись полдня с «крутыми» технологиями, решил попробовать самую простую — спрятать iframe и в него загрузить html-файл. Как мы и рассчитываем &lt;code&gt;window.onload&lt;/code&gt; срабатывает когда все загрузится, в том числе и фрейм. Добавлять фрейм можно&amp;nbsp; динамически, но тогда &lt;code&gt;onload&lt;/code&gt; может произойти раньше, чем iframe будет готов. Так как я успел и не забыл, браузеры меня вознаградили. Наградой явился объект &lt;code&gt;document&lt;/code&gt;, который умеет парсить HTML. Ура!&lt;/p&gt;

&lt;p&gt;Как это выгладит в коде? Как всегда было в HTML:
&lt;pre&gt;&lt;code class="javascript"&gt;
var html = $('htmliframe').contentDocument.firstChild
html.innerHTML = '&amp;lt;div&amp;gt;' + markup + '&amp;lt;/div&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;
Родные, милые черты...&lt;/p&gt;

&lt;p&gt;Осталось извлечь полученное дерево и доставить его на место назначения.
&lt;pre&gt;&lt;code class="javascript"&gt;
// почистим ноду назначения
while (destination.firstChild)
  destination.removeChild(destination.firstChild)
// склонируем HTML-дерево (в нашем случае див с детьми)
clone = html.firstChild.cloneNode(true)
// вставим див куда надо
content.appendChild(clone)
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;В будущих версиях браузеров, возможно, надо будет адаптировать ноды &lt;code&gt;clone = document.adoptNode(clone)&lt;/code&gt;, но пока они на adoptNode ругаются.&lt;/p&gt;

&lt;h3&gt;Итог&lt;/h3&gt;
&lt;p&gt;HTML парсится и выводится в Firefox 2, 3 и Safari 3. Opera 9 все умеет сама. IE и Safari 2 не умеют XHTML совсем, но все же умеют клиентский XSL через xml-stylesheet. Получается, мостик между HTML и XML переброшен.
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/cmsbuilder/~4/-fJpyV954Yw" height="1" width="1"/&gt;</content>
</entry>


</feed>

