<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Механический мир</title>
	<atom:link href="http://kuroikaze85.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://kuroikaze85.wordpress.com</link>
	<description>Прибежище техножреца</description>
	<lastBuildDate>Thu, 16 May 2013 08:07:21 +0000</lastBuildDate>
	<language>ru</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='kuroikaze85.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://1.gravatar.com/blavatar/b6882b384c701fa54a4711c58ed9db0b?s=96&#038;d=http%3A%2F%2Fs2.wp.com%2Fi%2Fbuttonw-com.png</url>
		<title>Механический мир</title>
		<link>http://kuroikaze85.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://kuroikaze85.wordpress.com/osd.xml" title="Механический мир" />
	<atom:link rel='hub' href='http://kuroikaze85.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Mercurial_keyring</title>
		<link>http://kuroikaze85.wordpress.com/2012/08/07/mercurial_keyring/</link>
		<comments>http://kuroikaze85.wordpress.com/2012/08/07/mercurial_keyring/#comments</comments>
		<pubDate>Tue, 07 Aug 2012 13:04:59 +0000</pubDate>
		<dc:creator>kuroikaze85</dc:creator>
				<category><![CDATA[Дневник программиста]]></category>
		<category><![CDATA[Mercurial]]></category>
		<category><![CDATA[mercurial_keyring]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://kuroikaze85.wordpress.com/?p=1476</guid>
		<description><![CDATA[Столкнулся недавно с неприятной проблемой &#8211; mercurial_keyring не хотел сохранять пароли. Mercurial_keyring &#8211; плагин к mercurial (системе контроля версий), позволяющий не вводить пароль при каждой операции с удалённым репозиторием. Мне это потребовалось для организации автодеплоя прямо из репо. Когда я прошелся по коду mercurial_keyring (он написан на python), оказалось что проблема лежит в функции get_http_password, [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1476&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Столкнулся недавно с неприятной проблемой &#8211; mercurial_keyring не хотел сохранять пароли. <a href="https://bitbucket.org/eblood66/mercurial_keyring">Mercurial_keyring</a> &#8211; плагин к mercurial (системе контроля версий), позволяющий не вводить пароль при каждой операции с удалённым репозиторием. Мне это потребовалось для организации автодеплоя прямо из репо.</p>
<p>Когда я прошелся по коду mercurial_keyring (он написан на python), оказалось что проблема лежит в функции get_http_password, точнее в том, что модуль keyring не может сохранить/получить пароль, если в ключе присутствует знак <tt>:</tt>. Скрипт mercurial_keyring.py генерирует ключ из логина и url-а, соответственно если URL начинается с <tt>http:</tt> или <tt>https:</tt>, плагин просто не находит сохраненного пароля.</p>
<p>Я это у себя исправил просто правкой <tt>mercurial_keyring.py</tt>. Было:</p>
<pre class="brush: python; first-line: 74; title: ; notranslate">
    def _format_http_key(self, url, username):
        return &quot;%s@@%s&quot; % (username, url)
</pre>
<p>Стало:</p>
<pre class="brush: python; first-line: 74; title: ; notranslate">
    def _format_http_key(self, url, username):
        return &quot;%s@@%s&quot; % (username, url.replace(':', '#'))
</pre>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kuroikaze85.wordpress.com/1476/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kuroikaze85.wordpress.com/1476/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1476&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://kuroikaze85.wordpress.com/2012/08/07/mercurial_keyring/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6e18f1f7becbfe66248caa034417b759?s=96&#38;d=&#38;r=X" medium="image">
			<media:title type="html">kuroikaze85</media:title>
		</media:content>
	</item>
		<item>
		<title>nude.js и CORS</title>
		<link>http://kuroikaze85.wordpress.com/2012/01/31/nude-js-and-cors/</link>
		<comments>http://kuroikaze85.wordpress.com/2012/01/31/nude-js-and-cors/#comments</comments>
		<pubDate>Tue, 31 Jan 2012 15:51:45 +0000</pubDate>
		<dc:creator>kuroikaze85</dc:creator>
				<category><![CDATA[Дневник программиста]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://kuroikaze85.wordpress.com/?p=1467</guid>
		<description><![CDATA[После случайного обнаружения в ленте поста про nude.js, у меня сразу появилась идея о том что можно с этим сделать. Nude.js &#8211; это javascript-библиотека для обнаружения обнаженного тела на фото. Я захотел сделать автоматическое скрытие NSFW-постов в ленте Tumblr, но, к сожалению, не получилось. Но всё по порядку. Nude.js подключается к странице как любой другой [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1467&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>После случайного обнаружения в ленте <a href="http://davidwalsh.name/nudejs">поста</a> про <a href="https://github.com/pa7/nude.js">nude.js</a>, у меня сразу появилась идея о том что можно с этим сделать. Nude.js &#8211; это javascript-библиотека для обнаружения обнаженного тела на фото. Я захотел сделать автоматическое скрытие NSFW-постов в ленте Tumblr, но, к сожалению, не получилось. Но всё по порядку.<br />
<span id="more-1467"></span></p>
<p>Nude.js подключается к странице как любой другой скрипт:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!--[if IE]&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;excanvas_r3/excanvas.compiled.js&quot;&gt;&lt;/script&gt;  
&lt;![endif]--&gt;
&lt;script src=&quot;nude.js/compressed/nude.min.js&quot;&gt;&lt;/script&gt;
</pre>
<p>Для работы nude.js требуется canvas, поэтому для IE требуется &#8220;эмулятор&#8221; <tt>iecanvas.js</tt>. После подключения можно анализировать DOM node изображения:</p>
<pre class="brush: jscript; title: ; notranslate">
 nude.load(node);
  nude.scan(function(result){ 
    console.log(result ? &quot;Nudity found in &quot; + node.id + &quot;!&quot; : &quot;Not nude&quot;);
  });
</pre>
<p>Однако после подключения изображения к странице Tumblr и отладки сообщений об ошибках, обнаружился интересный факт. А именно: если на canvas помещён элемент с домена, не совпадающего с доменом скрипта, canvas считаются &#8220;загрязнёнными&#8221; и информацию об изображении с них получить уже нельзя. Единственный способ это обойти &mdash; убедиться что сервер отдаёт с запрашиваемым изображением заголовок CORS (<a href="http://en.wikipedia.org/wiki/Cross-Origin_Resource_Sharing">cross-origin resource sharing</a>), а именно <tt>Access-Control-Allow-Origin</tt>. Пока этот заголовок использует в основном Flickr. В других случаях, когда домены скрипта и изображения не совпадают, загрузить и обработать его уже нельзя.</p>
<p>После этого я отложил эту идею до нормального внедрения CORS.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kuroikaze85.wordpress.com/1467/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kuroikaze85.wordpress.com/1467/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1467&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://kuroikaze85.wordpress.com/2012/01/31/nude-js-and-cors/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6e18f1f7becbfe66248caa034417b759?s=96&#38;d=&#38;r=X" medium="image">
			<media:title type="html">kuroikaze85</media:title>
		</media:content>
	</item>
		<item>
		<title>AI, Minecraft и Node.js</title>
		<link>http://kuroikaze85.wordpress.com/2011/10/14/ai-minecraft-and-node-js/</link>
		<comments>http://kuroikaze85.wordpress.com/2011/10/14/ai-minecraft-and-node-js/#comments</comments>
		<pubDate>Fri, 14 Oct 2011 12:22:36 +0000</pubDate>
		<dc:creator>kuroikaze85</dc:creator>
				<category><![CDATA[Дневник программиста]]></category>
		<category><![CDATA[blackstone]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[minecraft]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[эксперименты]]></category>

		<guid isPermaLink="false">http://kuroikaze85.wordpress.com/?p=1457</guid>
		<description><![CDATA[Пару месяцев назад я записался на онлайн-курс Artificial Intellingence от Stanford University. И пока курс не начался (он начался только в понедельник 10 октября) пришла ко мне мысль сделать какую нибудь &#8220;песочницу&#8221; для тестирования разных алгоритмов, которые будут встречаться в курсе. Захотелось сделать такую песочницу из Minecraft. Вначале я думал о том чтобы просто модифицировать [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1457&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p><a href="https://plus.google.com/113094886364217274430/posts"><img src="https://lh5.googleusercontent.com/-raNL5HGR138/Tpd891r-HoI/AAAAAAAAEjo/8iTJ3WgYUt0/w402/2011-10-14_00.31.27.png" title="Первый моб на сервере" /></a></p>
<p>Пару месяцев назад я записался на онлайн-курс <a href="http://ai-class.com">Artificial Intellingence</a> от Stanford University. И пока курс не начался (он начался только в понедельник 10 октября) пришла ко мне мысль сделать какую нибудь &#8220;песочницу&#8221; для тестирования разных алгоритмов, которые будут встречаться в курсе. Захотелось сделать такую песочницу из Minecraft.</p>
<p>Вначале я думал о том чтобы просто модифицировать клиент или написать к нему плагин. Но т.к. при модификации пришлось бы работать с декомпилированным (плохо читаемым) Java-кодом, а система плагинов пока оставляет желать лучшего, я переключил своё внимание на сервер. Тут дела были несколько лучше. Для Minecraft есть несколько серверов с открытым исходным кодом на разных языках &mdash; C, Java, Python, .NET и т.д. Покопавшись в них, а также покопавшись в <a href="https://github.com/welterde/nodecraft">nodecraft</a> &mdash; заброшенной попытке написать minecraft-сервер на JavaScript, я решил все таки попробовать написать сервер самостоятельно, благо опыт работы с двоичными протоколами после разработки <a href="http://github.com/kurokikaze/limestone">limestone</a> у меня есть, да и во внутренности Minecraft я уже немного <a href="/2010/11/19/minecraft-python-cartograph/">забирался</a> (правда с Питоном). Протокол Minecraft описан (пусть даже немного неточно) в <a href="http://mc.kev009.com/Main_Page">Minecraft Coalition wiki</a>.</p>
<p>За первые пару дней я разобрался с последовательностью авторизации клиента и перешёл непосредственно к логину. Для логина сервер должен выслать клиенту местность вокруг места появления и всякие стартовые параметры типа времени сервера, содержимого инвентаря, точки респауна в случае если игрок погибнет и т.д. В IRC на канале #mcdevs меня сразу предупредили что отправка чанков клиенту это довольно сложная часть разработки и многие авторы серверов срезаются именно здесь. В результате двухдневных попыток подбора библиотеки сжатия и экспериментов с порядком пакетов отправить местность всё таки получилось. На следующий день после этого я прикрутил отправку keepalive-пакетов (без этого время пребывания на сервере ограничивалось одной минутой) и добавил респаун мобов и их движение. Теперь осталось только добавить определение столкновений, чтобы делать лабиринты, и можно приступать к заданиям второй главы курса &mdash; алгоритмам поиска пути.</p>
<p>Когда доделаю сервер будет видимо отдельный большой пост о протоколе Minecraft, написании сервера и насколько он на самом деле подходит для тестирования AI-алгоритмов. Пока мои наработки можно найти на Github. Сервер я назвал <a href="http://github.com/kurokikaze/blackstone">blackstone</a> (на #mcdevs предлагали node-minecraft, но по моему это скорее название для какой нибудь библиотеки утилит).</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kuroikaze85.wordpress.com/1457/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kuroikaze85.wordpress.com/1457/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1457&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://kuroikaze85.wordpress.com/2011/10/14/ai-minecraft-and-node-js/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6e18f1f7becbfe66248caa034417b759?s=96&#38;d=&#38;r=X" medium="image">
			<media:title type="html">kuroikaze85</media:title>
		</media:content>

		<media:content url="https://lh5.googleusercontent.com/-raNL5HGR138/Tpd891r-HoI/AAAAAAAAEjo/8iTJ3WgYUt0/w402/2011-10-14_00.31.27.png" medium="image">
			<media:title type="html">Первый моб на сервере</media:title>
		</media:content>
	</item>
		<item>
		<title>Яндекс.Метрика</title>
		<link>http://kuroikaze85.wordpress.com/2011/07/26/yandex-metrika/</link>
		<comments>http://kuroikaze85.wordpress.com/2011/07/26/yandex-metrika/#comments</comments>
		<pubDate>Tue, 26 Jul 2011 13:55:40 +0000</pubDate>
		<dc:creator>kuroikaze85</dc:creator>
				<category><![CDATA[Дневник программиста]]></category>
		<category><![CDATA[Заметки]]></category>

		<guid isPermaLink="false">http://kuroikaze85.wordpress.com/?p=1451</guid>
		<description><![CDATA[Последнее время с интересом слежу за развитием истории с проиндексированными смс, заказами (а теперь ещё и билетами). На Хабре (да и в Интернете вообще, по моему) все горой стоят за Яндекс &#8212; дескать, кто не закрыл нужные страницы с помощью robots.txt, тот сам себе злобный Буратино. Я не совсем согласен с такой точкой зрения. Как [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1451&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Последнее время с интересом слежу за развитием истории с проиндексированными смс, заказами (а теперь ещё и билетами). На Хабре (да и в Интернете вообще, по моему) все горой стоят за Яндекс &mdash; дескать, кто не закрыл нужные страницы с помощью <tt>robots.txt</tt>, тот сам себе злобный Буратино. Я не совсем согласен с такой точкой зрения. Как пользователь Яндекс.Метрики лично я пропустил момент когда она начала по тихому сдавать страницы в очередь краулера (и, соответственно, в индекс). Я использую кое где Метрику для статистики, и для меня такое открытие было неприятной неожиданностью. Причём я сейчас проверил в <a href="http://metrika.yandex.ru/news/">новостях метрики</a> &mdash; о таком нововведении там ни слова. Хотя мне скрывать какие то страницы смысла не имеет, подобное изменение всё равно вызывает вопросы. Имхо, раз уж метрика привязывается к почтовому аккаунту, могли бы и разослать предупреждение.</p>
<p>Впрочем, это не означает что владельцы сайтов тут совсем ни при чём. При защите пользовательских данных подход <a href="http://en.wikipedia.org/wiki/Security_through_obscurity">security through obscurity</a> ни к чему хорошему привести не может. Особенно если данные действительно личные (номера телефонов, адреса + состав заказа, данные о поездке), лучше защищать их явно, чем просто надеяться на то что никто и никогда этих страниц не увидит. И неожиданно изменившиеся правила игры не обернулись бы такими конфузами.</p>
<p>В общем, этот случай &mdash; хороший повод задуматься о безопасности пользовательских данных в своих проектах, не полагаясь на поведение поисковиков &#8220;по умолчанию&#8221;. Обновить robots.txt, поставить авторизацию на страницах, которые содержат личные данные.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kuroikaze85.wordpress.com/1451/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kuroikaze85.wordpress.com/1451/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1451&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://kuroikaze85.wordpress.com/2011/07/26/yandex-metrika/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6e18f1f7becbfe66248caa034417b759?s=96&#38;d=&#38;r=X" medium="image">
			<media:title type="html">kuroikaze85</media:title>
		</media:content>
	</item>
		<item>
		<title>Kue — очереди с приоритетами на основе Redis</title>
		<link>http://kuroikaze85.wordpress.com/2011/07/05/kue/</link>
		<comments>http://kuroikaze85.wordpress.com/2011/07/05/kue/#comments</comments>
		<pubDate>Tue, 05 Jul 2011 20:20:43 +0000</pubDate>
		<dc:creator>kuroikaze85</dc:creator>
				<category><![CDATA[Дневник программиста]]></category>
		<category><![CDATA[Kue]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[Redis]]></category>

		<guid isPermaLink="false">http://kuroikaze85.wordpress.com/?p=1441</guid>
		<description><![CDATA[Если в Вашем проекте необходимо выполнять какие то задачи в фоновом режиме, будь то отправка почты или перекодирование видео, Вам следует присмотреться к Kue &#8212; инструменту для управления очередями задач с поддержкой приоритетов, метаданных и удобным веб-интерфейсом. Установка Для работы Kue требуется Redis версии не меньше чем 2 (с поддержкой команды WATCH). Я использовал 2.2.11, [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1441&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Если в Вашем проекте необходимо выполнять какие то задачи в фоновом режиме, будь то отправка почты или перекодирование видео, Вам следует присмотреться к <a href="http://learnboost.github.com/kue/">Kue</a> &mdash; инструменту для управления очередями задач с поддержкой приоритетов, метаданных и удобным веб-интерфейсом.</p>
<p><span id="more-1441"></span></p>
<h2>Установка</h2>
<p>Для работы Kue требуется Redis версии не меньше чем 2 (с поддержкой команды <tt>WATCH</tt>). Я использовал 2.2.11, последнюю стабильную версию. Об установке и настройке Redis можно подробнее прочитать в <a href="/2010/08/10/node-js-redis/">другой статье</a>.</p>
<p>Сам Kue ставится из npm:</p>
<pre class="brush: bash; title: ; notranslate">
npm install kue
</pre>
<p>Ничего настраивать после установки не нужно &mdash; Kue сам подключится к Redis по стандартному порту. Если Redis у Вас настроен на нестандартный порт, нужные параметры можно передать при подключении модуля в коде.</p>
<h2>Использование</h2>
<p>Попробуем сразу создать задачу:</p>
<pre class="brush: jscript; title: ; notranslate">
var kue = require('kue'),
    jobs = kue.createQueue();

jobs.create('email', {
    title: 'welcome email for tj'
  , to: 'tj@learnboost.com'
  , template: 'welcome-email'
}).save();
</pre>
<p>Здесь <tt>"email"</tt> это тип задачи, объект содержит метаданные, которые будут доступны при выполнении. Свойство <tt>title</tt> особое: оно будет использоваться в веб-интерфейсе как имя задачи. Если требуется указать приоритет задачи, перед <tt>save()</tt> в цепочку идёт <tt>.priority()</tt> с указанием приоритета.</p>
<p>Приоритет может быть задан либо числом (чем меньше, тем раньше задача попадёт в очередь), либо строкой. Строки служат указателями наиболее типичных приоритетов:</p>
<ul>
<li><tt>low</tt>: 10</li>
<li><tt>normal</tt>: 0</li>
<li><tt>medium</tt>: -5</li>
<li><tt>high</tt>: -10</li>
<li><tt>critical</tt>: -15</li>
</ul>
<p>Получить задачу для выполнения можно с помощью <tt>jobs.process</tt>:</p>
<pre class="brush: jscript; title: ; notranslate">
jobs.process('email', function(job, done) {
  console.log('Processing &quot;' + job.data.title + '&quot;');
  done();
});
</pre>
<p>При выполнении в обработчик передаётся объект задачи <tt>job</tt> (метаданные доступны в свойстве <tt>data</tt>) и callback <tt>done</tt> &mdash; если он будет вызван без передачи ошибки, задача будет отмечена как успешно выполненная.</p>
<p>Маленькие задачи, которые нет смысла выполнять поодиночке, можно обрабатывать по несколько штук параллельно. Для этого после типа задачи надо передать количество задач для одновременного выполнения. Например, чтобы отправить сразу 20 писем, можно использовать такой код:</p>
<pre class="brush: jscript; title: ; notranslate">
jobs.process('email', 20, function(job, done) {
  console.log('Processing &quot;' + job.data.title + '&quot;');
  done();
});
</pre>
<p>Если надо следить за выполнением отдельных задач, можно использовать индикаторы выполнения и индивидуальные логи. Чтобы отметить степень выполнения долгой задачи, используйте <tt>job.progress</tt>:</p>
<pre class="brush: jscript; title: ; notranslate">
job.progress(done, total);
</pre>
<p>Здесь <tt>total</tt> это общее количество шагов выполнения (или элементов для обработки), done &mdash; количество уже выполненных шагов. Если при выполнении что то пойдет не так, можно записать сообщение в лог задачи:</p>
<pre class="brush: jscript; title: ; notranslate">
job.log('Error rendering slide %d: %s, slide.id, render.err);
</pre>
<h2>Веб-интерфейс</h2>
<p>У Kue есть веб-интерфейс, представляющий собой приложение на основе Express. Чтобы его запустить надо вызвать <tt>kue.app.listen</tt> и передать ему порт:</p>
<pre class="brush: jscript; title: ; notranslate">
kue.app.listen(3003);
</pre>
<p>В веб-интерфейсе можно следить за выполнением задач.</p>
<p><img src="http://learnboost.github.com/kue/images/ui.png" title="Веб-интерфейс Kue"></p>
<p>Также можно просматривать метаданные и индивидуальные логи задач.</p>
<p><img src="http://learnboost.github.com/kue/images/details.png" title="Метаданные и статистика задач в Kue"><br />
<img src="http://learnboost.github.com/kue/images/log.png" title="Индивидуальные логи задач в Kue"></p>
<p>Kue показывает индикаторы степени выполнения той или иной задачи, задаваемые <tt>job.progress</tt>.</p>
<p><img src="http://learnboost.github.com/kue/images/progress.png" title="Индикаторы выполнения задач"></p>
<h2>Ссылки по теме</h2>
<ul>
<li><a href="http://learnboost.github.com/kue/">Официальный сайт Kue</a></li>
<li><a href="http://github.com/learnboost/kue">Репозиторий Kue на Github</a></li>
</ul>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kuroikaze85.wordpress.com/1441/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kuroikaze85.wordpress.com/1441/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1441&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://kuroikaze85.wordpress.com/2011/07/05/kue/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6e18f1f7becbfe66248caa034417b759?s=96&#38;d=&#38;r=X" medium="image">
			<media:title type="html">kuroikaze85</media:title>
		</media:content>

		<media:content url="http://learnboost.github.com/kue/images/ui.png" medium="image">
			<media:title type="html">Веб-интерфейс Kue</media:title>
		</media:content>

		<media:content url="http://learnboost.github.com/kue/images/details.png" medium="image">
			<media:title type="html">Метаданные и статистика задач в Kue</media:title>
		</media:content>

		<media:content url="http://learnboost.github.com/kue/images/log.png" medium="image">
			<media:title type="html">Индивидуальные логи задач в Kue</media:title>
		</media:content>

		<media:content url="http://learnboost.github.com/kue/images/progress.png" medium="image">
			<media:title type="html">Индикаторы выполнения задач</media:title>
		</media:content>
	</item>
		<item>
		<title>Случайная находка — dev-сервер Хабрахабра</title>
		<link>http://kuroikaze85.wordpress.com/2011/07/05/habrahabr-dev-server/</link>
		<comments>http://kuroikaze85.wordpress.com/2011/07/05/habrahabr-dev-server/#comments</comments>
		<pubDate>Tue, 05 Jul 2011 13:00:25 +0000</pubDate>
		<dc:creator>kuroikaze85</dc:creator>
				<category><![CDATA[Заметки]]></category>
		<category><![CDATA[Хабрахабр]]></category>

		<guid isPermaLink="false">http://kuroikaze85.wordpress.com/?p=1432</guid>
		<description><![CDATA[Сегодня совершенно случайно обнаружил один из development-серверов Хабрахабра. Так получилось что как раз сегодня прошло 3 года с моей регистрации на ресурсе. Узнал я об этом, зайдя с утра в почту и увидев письмо от Хабрахабра с темой &#8220;Вам доступен значок &#8216;Старожил&#8217;&#8220;. Точнее, таких писем было два. При более внимательном рассмотрении оказалось что во втором [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1432&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Сегодня совершенно случайно обнаружил один из development-серверов <a href="http://habrahabr.ru">Хабрахабра</a>. Так получилось что как раз сегодня прошло 3 года с моей регистрации на ресурсе. Узнал я об этом, зайдя с утра в почту и увидев письмо от Хабрахабра с темой &#8220;<em>Вам доступен значок &#8216;Старожил&#8217;</em>&#8220;.</p>
<p>Точнее, таких писем было два.</p>
<p><span id="more-1432"></span></p>
<p>При более внимательном рассмотрении оказалось что во втором письме ссылки ведут не на основной сайт Хабра, а на другой домен (хотя в самом письме написано что отправлено оно от mailer@habrahabr.ru). Немного пошарившись по второму сайту, сделал вывод что это какая то dev или staging площадка для ремонта багов. Никакого нового функционала там нет, зато например с утра не открывались топики если я был залогинен (я смог зайти, да), в обед это уже починили, зато потом снова сломали. И в собственный либо чей нибудь другой хабрацентр зайти невозможно. Кстати, самой новой фичи Хабра (возможности подписаться на комментарии к топику/отписаться от них) я там тоже не увидел.</p>
<p>Судя по топикам и комментариям, база в проекте актуальна на 4 июня 2011, время последнего комментария 18:29. Соответственно, срок голосования за всё уже истёк, так что проверить систему патронов не получилось. Из исследовательских побуждений сделал комментарий к одному из топиков. Комментарий появился на dev-сайте, но не на основном.</p>
<p>Очевидно что база используется настоящая (иначе я не смог бы зайти со своим паролем, да и почта бы не пришла), и крон работает исправно. Ради эксперимента попробовал зайти под неправильным паролем/другим пользователем — не получается. Судя по всему, оба сайта по крону проверили мою дату регистрации, убедились что я зарегистрирован более 3-х лет назад и сижу без значка, и оба выслали мне по письму (автоматически сформированному со ссылками).</p>
<p>Что подводит меня к интересной мысли что и на нашем проекте может быть такая же засада. Мы тоже используем частичную живую базу, крон тоже работает и данные пользователям периодически рассылаются. Правда у нас все dev/stage сервера закрыты HTTP Auth, так что просто так туда не зайти. Но проверить на всякий случай надо.</p>
<p>Ради ещё одного эксперимента (да и просто из интереса) запостил этот топик на Dev-Хабр. Хотя при публикации топика мне выдало 500 ошибку, он тем не менее появился в &#8220;новых&#8221;. В пост вставил картинку для отслеживания посещаемости — нашёл фотографию по запросу dev server, залил на habrastorage и сократил URL через <a href="http://bit.ly">bit.ly</a>. В bit.ly есть простенькая статистика по переходам (в нашем случае — по просмотрам картинки). В результате топик пока не получил ни просмотров, ни оценок.</p>
<p>В конце концов отписал Чипу и Дейлу по этому поводу. Пришёл ответ, но dev-сайт так и не закрыли (поэтому домен из записи я убрал).</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kuroikaze85.wordpress.com/1432/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kuroikaze85.wordpress.com/1432/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1432&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://kuroikaze85.wordpress.com/2011/07/05/habrahabr-dev-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6e18f1f7becbfe66248caa034417b759?s=96&#38;d=&#38;r=X" medium="image">
			<media:title type="html">kuroikaze85</media:title>
		</media:content>
	</item>
		<item>
		<title>Операции с однородными потоками данных</title>
		<link>http://kuroikaze85.wordpress.com/2011/05/03/uniform-streams-operations/</link>
		<comments>http://kuroikaze85.wordpress.com/2011/05/03/uniform-streams-operations/#comments</comments>
		<pubDate>Tue, 03 May 2011 17:11:08 +0000</pubDate>
		<dc:creator>kuroikaze85</dc:creator>
				<category><![CDATA[Дневник программиста]]></category>
		<category><![CDATA[потоки]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[эксперименты]]></category>

		<guid isPermaLink="false">http://kuroikaze85.wordpress.com/?p=1422</guid>
		<description><![CDATA[Недавно задумал написать вспомогательный модуль для node.js, чтобы облегчить работу с потоками для чтения. Например, быстро собрать по кускам ответ от сервера и представить в виде одной строки или JSON. То, есть, чтобы вместо вот этого: &#8230;можно было писать как нибудь так: В результате получился модуль streamops (Stream Operations) в таком монадическом стиле а-ля jQuery. [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1422&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Недавно задумал написать вспомогательный модуль для node.js, чтобы облегчить работу с потоками для чтения. Например, быстро собрать по кускам ответ от сервера и представить в виде одной строки или JSON.</p>
<p><span id="more-1422"></span></p>
<p>То, есть, чтобы вместо вот этого:</p>
<pre class="brush: jscript; title: ; notranslate">
var call = http.request(options, function(res) {
	var data = '';
	res.on('data', function(chunk) {
		data += chunk;
	});
	
	res.on('end', function(chunk) {
		data += chunk;
		// Сделать что нибудь с полным ответом сервера
	});
});
call.end();
</pre>
<p>&#8230;можно было писать как нибудь так:</p>
<pre class="brush: jscript; title: ; notranslate">
var call = http.request(options, function(res) {
	s(res).collect().on('data', function(data)
		// Сделать что нибудь с полным ответом сервера
	});
});
call.end();
</pre>
<p>В результате получился модуль <a href="https://github.com/kurokikaze/streamops">streamops</a> (Stream Operations) в таком монадическом стиле а-ля jQuery. Потихоньку модуль оброс (и продолжает обрастать) разными полезными функциями, например: разрезание потока по разделителю, фильтрация событий по регулярному выражению и т.д.</p>
<pre class="brush: jscript; title: ; notranslate">
var test = new events.EventEmitter();

// Преобразуем текст входящих событий в JSON, оставляем только поле test
// и отфильтровываем все значения которые не начинаются на &quot;a&quot;
sop.sop(test).json().only('test').filter(/a.+/).log();

test.emit('data', JSON.stringify({&quot;test&quot;: &quot;a1&quot;}));
test.emit('data', JSON.stringify({&quot;test&quot;: &quot;b1&quot;}));
test.emit('data', JSON.stringify({&quot;test&quot;: &quot;a2&quot;}));
test.emit('data', JSON.stringify({&quot;test&quot;: &quot;b2&quot;}));

test.emit('end');
</pre>
<p>В дальнейшем я хочу немного поэкспериментировать с представлением периодических соединений с сайтами как read-only потоков, чтобы можно было фильтровать результаты нескольких последовательных вызовов страницы (например, периодический опрос API). Также хочется посмотреть на потоковые JSON-парсеры.</p>
<p><b>P.S.:</b> нашёл похожий проект &mdash; <tt><a href="https://github.com/fjakobs/async.js">async.js</a></tt> от Fabian Jakobs</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kuroikaze85.wordpress.com/1422/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kuroikaze85.wordpress.com/1422/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1422&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://kuroikaze85.wordpress.com/2011/05/03/uniform-streams-operations/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6e18f1f7becbfe66248caa034417b759?s=96&#38;d=&#38;r=X" medium="image">
			<media:title type="html">kuroikaze85</media:title>
		</media:content>
	</item>
		<item>
		<title>Использование runit вместо Init и вывод логов в файл</title>
		<link>http://kuroikaze85.wordpress.com/2011/04/04/runit-nodejs/</link>
		<comments>http://kuroikaze85.wordpress.com/2011/04/04/runit-nodejs/#comments</comments>
		<pubDate>Mon, 04 Apr 2011 10:54:21 +0000</pubDate>
		<dc:creator>kuroikaze85</dc:creator>
				<category><![CDATA[Дневник программиста]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[управление сервером]]></category>

		<guid isPermaLink="false">http://kuroikaze85.wordpress.com/?p=1416</guid>
		<description><![CDATA[Я достаточно долгое время использовал связку Init+Monit для запуска скриптов node, но со временем стал натыкаться на минусы такой связки. Одна из самых серьёзных проблем &#8212; отсутствие возможности вывести лог в разные файлы в ранних версиях start-stop-daemon. Версия, поставляемая с Debian Lenny не поддерживает такой функционал, а сборка новой версии тянет за собой уйму непонятного [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1416&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Я достаточно долгое время использовал связку Init+Monit для запуска скриптов node, но со временем стал натыкаться на минусы такой связки. Одна из самых серьёзных проблем &mdash; отсутствие возможности вывести лог в разные файлы в ранних версиях <tt>start-stop-daemon</tt>. Версия, поставляемая с Debian Lenny не поддерживает такой функционал, а сборка новой версии тянет за собой уйму непонятного барахла. В итоге, попробовав разные варианты, я остановился на runit как замене Init-скриптам.</p>
<p><span id="more-1416"></span></p>
<h2>Установка и настройка</h2>
<p>Для большинства систем runit ставится из стандартного менеджера пакетов (<tt>apt-get install runit</tt> для Debian). Если в Вашем менеджере нужного пакета нет, можно скачать исходники для сборки с <a href="http://smarden.org/runit/install.html" target="">официальной страницы</a>.</p>
<p>Каждому сервису в runit соответствует директория в <tt>/etc/sv</tt>. В этой директории создаём файл запуска <tt>run</tt>:</p>
<pre class="brush: bash; title: ; notranslate">
mkdir /etc/sv/testservice
vim /etc/sv/testservice/run
</pre>
<p>В файле <tt>run</tt> пишем строку для запуска нашего скрипта, с перенаправлениями ввода-вывода, <em>без</em> демонизации (<tt>&amp;</tt>), но с <tt>exec</tt>:</p>
<pre class="brush: bash; title: ; notranslate">
exec node /etc/mysite/server.js 1&gt;&gt;/var/log/mysite/output 2&gt;&gt;/var/log/mysite/error
</pre>
<p>Ставим этому файлу разрешение на выполнение:</p>
<pre class="brush: bash; title: ; notranslate">
chmod +x run
</pre>
<p>Теперь чтобы запустить сервис надо передать runit команду и имя директории внутри <tt>/etc/sv</tt>:</p>
<pre class="brush: bash; title: ; notranslate">
sv start testservice
</pre>
<p>Чтобы остановить сервис, вторым параметром передаётся <tt>stop</tt>, чтобы перезапустить — <tt>restart</tt>.</p>
<p>Чтобы проверить runit я использовал немного модифицированный &#8220;Hello World&#8221; с официального сайта:</p>
<pre class="brush: jscript; title: ; notranslate">
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
  console.log('Log message #' + i);
  console.error('Err message #' + i);
  i++;
}).listen(8124, &quot;192.168.175.128&quot;);
console.log('Server running at http://127.0.0.1:8124/')
</pre>
<p><tt>console.log</tt> выводит строку в стандартный поток вывода (<tt>stdout</tt>), <tt>console.error</tt> выводит строку в <tt>stderr</tt>. Строки, переданные в <tt>console.log</tt> будут выводиться в <tt>/var/log/mysite/output</tt>, то что передано в <tt>console.error</tt> будет записано в <tt>/var/log/mysite/error</tt>.</p>
<p>(Кстати, если сделаете то же самое и увидите что с каждым обновлением в лог попадают сразу две строки с последовательными номерами, не спешите искать баг в коде. Скорее всего Ваш браузер запрашивает вместе со страницей файл <tt>favicon.ico</tt> для сайта.)</p>
<p>Чтобы прикрутить runit к monit&#8217;у, можно создать симлинк в директории /etc/init.d/ на /usr/bin/sv. При таком вызове runit будет запускать/останавливать сервис, соответствующий имени симлинка:</p>
<pre class="brush: bash; title: ; notranslate">
ln -s /usr/bin/sv /etc/init.d/testservice
</pre>
<p>Теперь при вызове <tt>/etc/init.d/testservice start</tt> Ваш скрипт будет запущен. Так проще будет перейти с Init на runit если Вы всё таки задумаете это сделать <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h2>Ссылки по теме</h2>
<ul>
<li><a href="http://smarden.org/runit/" title="" target="_blank">Официальный сайт Runit</a></li>
<li><a href="http://habrahabr.ru/blogs/sysadm/83775/" title="" target="_blank">Руководство по установке и использованию runit (Хабрахабр)</a></li>
<li><a href="http://kuroikaze85.wordpress.com/2010/04/27/using-nodejs-with-init-and-monit/" title="" target="_blank">Использование node.js с Init + Monit</a></li>
</ul>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kuroikaze85.wordpress.com/1416/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kuroikaze85.wordpress.com/1416/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1416&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://kuroikaze85.wordpress.com/2011/04/04/runit-nodejs/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6e18f1f7becbfe66248caa034417b759?s=96&#38;d=&#38;r=X" medium="image">
			<media:title type="html">kuroikaze85</media:title>
		</media:content>
	</item>
		<item>
		<title>Mantis + GraphViz</title>
		<link>http://kuroikaze85.wordpress.com/2011/03/21/mantis-graphviz/</link>
		<comments>http://kuroikaze85.wordpress.com/2011/03/21/mantis-graphviz/#comments</comments>
		<pubDate>Mon, 21 Mar 2011 18:22:37 +0000</pubDate>
		<dc:creator>kuroikaze85</dc:creator>
				<category><![CDATA[Дневник программиста]]></category>
		<category><![CDATA[эксперименты]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://kuroikaze85.wordpress.com/?p=1399</guid>
		<description><![CDATA[После предыдущего эксперимента мне понравилось работать с GraphViz, к тому же я давно хотел понять как именно использовать его для отрисовки графов, поэтому я решил сделать небольшой скриптик с использованием dot. Получение данных Я решил найти API нашей системы багтрекинга Mantis чтобы взять оттуда список багов и построить граф зависимостей. Я помню что когда то [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1399&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>После <a href="/2011/03/17/big-files-cachegrind/">предыдущего эксперимента</a> мне понравилось работать с GraphViz, к тому же я давно хотел понять как именно использовать его для отрисовки графов, поэтому я решил сделать небольшой скриптик с использованием <tt>dot</tt>.</p>
<p><span id="more-1399"></span></p>
<h2>Получение данных</h2>
<p>Я решил найти API нашей системы багтрекинга Mantis чтобы взять оттуда список багов и построить граф зависимостей. Я помню что когда то читал об этом API. Оказалось что Mantis предоставляет SOAP API для получения всей необходимой информации о багах (<a href="http://www.futureware.biz/mantisconnect/">MantisConnect</a>). Так как из Node работать с SOAP оказалось не очень удобно, я переключился на PHP. Я использовал класс <a href="http://sourceforge.net/projects/nusoap/">NuSOAP</a> чтобы не изобретать велосипед лишний раз. Обращение к Мантису с помощью NuSOAP делается примерно так:</p>
<pre class="brush: php; title: ; notranslate">
require_once('nusoap-0.9.5/lib/nusoap.php');
$WSDL_POINT = &quot;https://mantis.example.com/api/soap/mantisconnect.php&quot;;
$username = 'username';
$password = 'password';

$proxyhost = isset($_POST['proxyhost']) ? $_POST['proxyhost'] : '';
$proxyport = isset($_POST['proxyport']) ? $_POST['proxyport'] : '';
$proxyusername = isset($_POST['proxyusername']) ? $_POST['proxyusername'] : '';
$proxypassword = isset($_POST['proxypassword']) ? $_POST['proxypassword'] : '';
$useCURL = isset($_POST['usecurl']) ? $_POST['usecurl'] : '0';

$client = new nusoap_client($WSDL_POINT, false,
					$proxyhost, $proxyport, $proxyusername, $proxypassword);
</pre>
<p>Теперь можно вызывать методы. Я решил просто создать в Мантисе фильтр на нужные баги и получать его результаты:</p>
<pre class="brush: php; title: ; notranslate">
$params = array(
    'username' =&gt; $username,
    'password'         =&gt; $password,
    'project_id'         =&gt; 42, // Проект, который нас интересует (можно посмотреть в веб-интерейсе Mantis)
    'filter_id'          =&gt; 987, // ID фильтра, можно посмотреть на странице управления фильтрами
    'page_number'         =&gt; 1,
    'per_page'       =&gt; 400 // получить 400 записей
);
$result = $client-&gt;call('mc_filter_get_issues', $params,  'https://mantis.example.com:443/api/soap/mantisconnect.php', 'http://soap.amazon.com');
</pre>
<p>В <tt>$result</tt> окажется результат обращения к API &mdash; массив багов с соответствующими полями. Теперь надо перевести его в формат, подходящий для построения графика.</p>
<h2>Graphviz и DOT-файлы</h2>
<p>По примеру <a href="/2011/03/17/big-files-cachegrind/">cg2dot</a> я решил выводить данные в виде DOT-файла. <a href="http://en.wikipedia.org/wiki/DOT_language">Описание формата</a> можно найти в Wikipedia &mdash; он довольно простой. Я просто прошёл по массиву багов и вывел все описания в виде <tt>12345 [label="24430: example issue description"];</tt> и связи parent-child в виде <tt>12345 -&gt; 12346</tt>. К сожалению, я не нашёл способа одновременно рисовать направленные и ненаправленные ребра, потому для related to я использовал стрелки в обоих направлениях. Все строки записываются внутри конструкции <tt>digraph issues {</tt> / </tt>}</tt>. Я ещё добавил строку <tt>rankdir=LR</tt> чтобы узлы отрисовывались сверху вниз. По умолчанию dot отрисовывает их в строку, и с длинными описаниями багов получается довольно неудобная картина.</p>
<p>В результате у меня получился граф зависимостей багов. Я ещё немного дописал код чтобы баги разного приоритета выводились разными цветами (<tt>12345 [color="green"];</tt>). Получившийся код я запускаю прямо из командной строки:</p>
<pre class="brush: bash; title: ; notranslate">
C:\xampp\htdocs\issues&gt;php index.php | &quot;c:\Program Files\Graphviz2.24\bin\dot.exe&quot; -Tpng -o21-mar-11_19-41.png
</pre>
<p>В имя выходного файла я добавляю текущее время чтобы было удобно различать разные даты. В результате получается неплохое наглядное представление текущего положения дел в релизе. Я хочу запускать этот скрипт периодически чтобы потом можно было посмотреть прогресс по дням.</p>
<p><b>UPD:</b> испытал код на <a href="http://bay12games.com/dwarves/mantisbt/">мантисе для Dwarf Fortress</a>, получился <a href="http://zoom.it/pNiU">огромный граф</a>.</p>
<h2>Ссылки по теме</h2>
<ul>
<li><a href="http://www.futureware.biz/mantisconnect/">API MantisConnect</a></li>
<li><a href="http://en.wikipedia.org/wiki/DOT_language">Краткое описание формата DOT</a></li>
<li><a href="http://www.graphviz.org/content/documentation">Полное описание на сайте GraphViz</a></li>
<li><a href="https://gist.github.com/35722fd505f112facc01">Получившийся код</a></li>
</ul>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kuroikaze85.wordpress.com/1399/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kuroikaze85.wordpress.com/1399/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1399&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://kuroikaze85.wordpress.com/2011/03/21/mantis-graphviz/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6e18f1f7becbfe66248caa034417b759?s=96&#38;d=&#38;r=X" medium="image">
			<media:title type="html">kuroikaze85</media:title>
		</media:content>
	</item>
		<item>
		<title>Большие файлы cachegrind</title>
		<link>http://kuroikaze85.wordpress.com/2011/03/17/big-files-cachegrind/</link>
		<comments>http://kuroikaze85.wordpress.com/2011/03/17/big-files-cachegrind/#comments</comments>
		<pubDate>Thu, 17 Mar 2011 14:38:04 +0000</pubDate>
		<dc:creator>kuroikaze85</dc:creator>
				<category><![CDATA[Дневник программиста]]></category>
		<category><![CDATA[производительность]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[XDebug]]></category>

		<guid isPermaLink="false">http://kuroikaze85.wordpress.com/?p=1387</guid>
		<description><![CDATA[Сегодня пришлось столкнуться с необходимостью сделать профилирование для скрипта который исполнялся слишком долго. Для таких целей у меня стоит связка из XDebug + WinCacheGrind. XDebug отработал как положено, но файл cachegrind.out получился размером ~200 метров. WinCachegrind по каким то причинам каждый раз зависал, пытаясь его переварить (подозреваю что из за большого размера). В результате пришлось [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1387&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Сегодня пришлось столкнуться с необходимостью сделать профилирование для скрипта который исполнялся слишком долго. Для таких целей у меня стоит связка из XDebug + WinCacheGrind. XDebug отработал как положено, но файл <tt>cachegrind.out</tt> получился размером ~200 метров. WinCachegrind по каким то причинам каждый раз зависал, пытаясь его переварить (подозреваю что из за большого размера). В результате пришлось пойти другим путём.</p>
<p>Проблема решилась с помощью <a href="http://code.google.com/p/xdebugtoolkit/">xdebugtoolkit</a> и <a href="http://www.graphviz.org/">Graphviz</a>. Graphviz и Python (необходимый для работы тулкита) у меня уже стояли. Оставалось скачать сам xdebugtoolkit. В него входит утилита <a href="http://code.google.com/p/xdebugtoolkit/wiki/cg2dot">cg2dot</a>, преобразующая cachegrind-файл в граф вызовов (с указанием какой вызов сколько времени занял). Потом по получившимся данным утилита dot из graphviz просто строит график в формате PNG, на котором неплохо видно где же застревает PHP. Сам вызов выглядит примерно так:</p>
<pre class="brush: bash; title: ; notranslate">
cg2dot.py &quot;D:\cachegrind\stage\cachegrind.out.16084&quot; | &quot;C:\Program Files\Graphviz2.24\bin\dot.exe&quot; -Tpng -ograph.png
</pre>
<p>Я ввел эту команду и ушёл на час гулять. В результате получился файл <tt><a href="http://i.imgur.com/8NG1p.png">graph.png</a></tt> по которому уже было понятно где и что работает медленно.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kuroikaze85.wordpress.com/1387/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kuroikaze85.wordpress.com/1387/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kuroikaze85.wordpress.com&#038;blog=3649962&#038;post=1387&#038;subd=kuroikaze85&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://kuroikaze85.wordpress.com/2011/03/17/big-files-cachegrind/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/6e18f1f7becbfe66248caa034417b759?s=96&#38;d=&#38;r=X" medium="image">
			<media:title type="html">kuroikaze85</media:title>
		</media:content>
	</item>
	</channel>
</rss>
