<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>web-dev.info</title>
	
	<link>http://web-dev.info</link>
	<description>Информация о web-разработке для web-разработчиков.</description>
	<pubDate>Tue, 02 Sep 2008 19:55:08 +0000</pubDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/web-dev-info-summary" type="application/rss+xml" /><feedburner:browserFriendly></feedburner:browserFriendly><item>
		<title>BareTail &amp; FirePHP</title>
		<link>http://web-dev.info/2008/08/baretail-and-firephp/</link>
		<comments>http://web-dev.info/2008/08/baretail-and-firephp/#comments</comments>
		<pubDate>Sun, 31 Aug 2008 15:20:24 +0000</pubDate>
		<dc:creator>wd</dc:creator>
		
		<category><![CDATA[Tools]]></category>

		<category><![CDATA[baretail]]></category>

		<category><![CDATA[debug]]></category>

		<category><![CDATA[extension]]></category>

		<category><![CDATA[firebug]]></category>

		<category><![CDATA[firefox]]></category>

		<category><![CDATA[firephp]]></category>

		<category><![CDATA[log]]></category>

		<category><![CDATA[tail]]></category>

		<guid isPermaLink="false">http://web-dev.info/2008/09/baretails-and-firephp/</guid>
		<description><![CDATA[По натуре своей, являясь перфекционистом, стараюсь разложить все по своим (не моим, а именно своим) полочкам. Удается не всегда, но с BareTail и FirePHP определенно повезло&#160;&#8212; уровень осведомленности при отладке возрос необычайно и это при полном отсутствии всякого вывода напрямую в код страницы.
Задом наперед или начнем с FirePHP
FirePHP является расширением для известного практически всем отладчика [...]]]></description>
			<content:encoded><![CDATA[<p>По натуре своей, являясь перфекционистом, стараюсь разложить все по своим (не моим, а именно своим) полочкам. Удается не всегда, но с <a href="http://www.baremetalsoft.com/baretail/index.php">BareTail</a> и <a href="http://www.firephp.org/">FirePHP</a> определенно повезло&nbsp;&mdash; уровень осведомленности при отладке возрос необычайно и это при полном отсутствии всякого вывода напрямую в код страницы.</p>
<h3 class="floatBar">Задом наперед или начнем с FirePHP</h3>
<p>FirePHP является расширением для известного практически всем отладчика <a href="http://www.getfirebug.com">Firebug</a> (его <a href="http://getfirebug.com/lite.html">лайт-версии</a> привносят функционал этого чудесного расширения и в другие браузеры).</p>
<p>Принцип его работы прост как все гениальное&nbsp;&mdash; отлавливать все, что подпадает под определенный content-type и выводить силами Firebug. Для разработчика все выглядит прозрачно&nbsp;&mdash; выводим отладочную информацию посредством ОО интерфейса предоставляемого серверной частью FirePHP и на дополнительной закладке Firebug <cite class="pppImage" src="/wp-content/uploads/2008/08/firephp.gif" width="750" height="406">видим</cite> ее в цвете, а иногда и с салютом. Страничка не искажается.</p>
<p>Наибольшее удовольствие расширение доставит разработчикам, использующим распространенные фреймворки типа CodeIgniter, Symfony или ZendFramework для которых серверная часть расширения написана с соблюдением доктрин и соглашений этих фреймворков. Это значит, что работа по выводу отладочной информации будет более привычной. Например, для <acronym title="Zend Framework">ZF</acronym> backend FirePHP выполнен в виде писателя <span class="phpClass">Zend_Log_Writer_Firebug</span>, передав который в конструктор логгера можно одним махом изменить принцип логирования.</p>
<p>Подводный камень работы FirePHP кроется в почти обязательном включении буферизации вывода, иначе могут быть коллизии в выводе или его полное отсутствие.</p>
<p>Более подробно с расширением можно познакомиться на его официальном сайте <a href="http://www.firephp.org">www.firephp.org</a>.</p>
<h3 class="floatBar">BareTail</h3>
<p><a href="http://www.baremetalsoft.com/baretail/index.php">Эта условно-бесплатная утилита</a> предоставляет возможности отслеживать часто изменяющуюся информацию вроде логов. Это Win32-аналог команды <span class="command">tail</span> из мира *nix&nbsp;&mdash; т.е. реализация возможности просмотра файл с конца. Кроме просмотра последних строк файла есть возможность отслеживать содержимое по мере его обновления т.е. в реальном времени, что очень подходит для просмотра логов («если о у вас есть» &copy;).</p>
<p>BareTail также предоставляет подсветку строк при вхождении в нее некой сигнальной части. К примеру, можно настроить просмотр так, что <span class="command">UPDATE</span>/<span class="command">INSERT</span> запросы будут желтыми, <span class="command">DELETE</span>&nbsp;&mdash; красными.</p>
<p>В случае если лог обновляется не очень интенсивно, как это часто бывает с тест-системами, можно свободно читать запросы, сравнивая ожидаемое с получившимся.</p>
<p>BareTail имеет старшего брата в виде аналогичной программы с возможностью подсветки по регулярному выражению, фильтрации строк и прочим изыскам.</p>
<p>Кроме BareTail существует полностью функциональная, свободная версия программы с аналогичными BareTail Pro функциями. Это <a href="http://www.flogviewer.com/">fLogViewer</a>. Однако, как показало использование обеих утилит, в подавляющем большинстве случаев хватает возможностей младшей версии BareTail.</p>
]]></content:encoded>
			<wfw:commentRss>http://web-dev.info/2008/08/baretail-and-firephp/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Использование пространств имен для организации JavaScript-кода</title>
		<link>http://web-dev.info/2008/06/javascript-namespaces/</link>
		<comments>http://web-dev.info/2008/06/javascript-namespaces/#comments</comments>
		<pubDate>Tue, 03 Jun 2008 14:59:37 +0000</pubDate>
		<dc:creator>wd</dc:creator>
		
		<category><![CDATA[ExtJS]]></category>

		<category><![CDATA[JavaScript]]></category>

		<category><![CDATA[Перевод]]></category>

		<category><![CDATA[namespace]]></category>

		<guid isPermaLink="false">http://web-dev.info/2008/06/javascript-namespaces/</guid>
		<description><![CDATA[ На текущий момент большинство web-приложений состоят из большого числа библиотек, виджетов и сниппетов из многих и многих источников. Следует помнить, что код других разработчиков может взаимодействовать с вашим кодом в случае, если происходит подключение обоих их на одной странице. А если вы оперируете глобальными переменными, то это и вовсе небезопасно.
Почему необходимо использовать пространства имен?
Возьмем, [...]]]></description>
			<content:encoded><![CDATA[<p> На текущий момент большинство web-приложений состоят из большого числа библиотек, виджетов и сниппетов из многих и многих источников. Следует помнить, что код других разработчиков может взаимодействовать с вашим кодом в случае, если происходит подключение обоих их на одной странице. А если вы оперируете глобальными переменными, то это и вовсе небезопасно.</p>
<h3 class="floatBar">Почему необходимо использовать пространства имен?</h3>
<p>Возьмем, в качестве примера, форум Ext JS, который использует три совершенно различных набора скриптов, созданных разными производителями: Ext JS используется нами для расширения функционала, Google Analytics для отслеживания использования сайта плюс обычные скрипты форума vBulletin. На рисунке представлено каким образом весь этот код из различных источников включается в тело страницы. Вообразите себе количество возможных противоречий с еще большим ростом подключаемых файлов.</p>
<p class="image"><img src="http://web-dev.info/wp-content/uploads/2008/06/diffsources.png" alt="Включаемые файлы" /></p>
<p>Если взглянуть на закладку DOM отладчика Firebug, можно увидеть сотни переменных контекста window, созданных подключенными скриптами. В тоже самое время Ext JS объединяет все свои классы в едином пространстве имен Ext и далее организует их в виде отдельных пакетов.</p>
<p class="image"><img src="http://web-dev.info/wp-content/uploads/2008/06/domwindow.png" alt="Обзор DOM" /></p>
<p>Когда вы пишете собственный срипт, вам следует помещать все свои классы и синглтоны (singletone) в некие пространства имен чтобы предотвратить противоречия с кодом других разработчиков. Термин «пространство имен» определяется в Википедии (<a href="http://en.wikipedia.org/wiki/Namespace">EN</a>, <a href="http://ru.wikipedia.org/wiki/Namespace">RU</a>) следующим образом: «… абстрактный контейнер предоставляющий контекст для содержащихся в нем элементов (имена, термины или слова) и позволяющий предотвратить возникновение неоднозначностей в случае существования элементов с одинаковыми именами…».</p>
<p>Пространства имен это важный инструмент разработчика, гарантирующий невозможность перезаписи одного кода другим. Ведь если иной разработчик определит переменную с таким же, как у вас именем, то существовавшее до этого определение будет перезаписано. Последний подключенный, в таком случае, фрагмент кода будет всегда одерживать верх.</p>
<p>Так как JavaScript является языком с функциональными областями видимости<sup>*</sup>, то создание функции или/и переменной не «обернутых» в функцию приводит к появлению их в глобальной области видимости (в контексте window). Чтобы предотвратить это разработчики помещают свои классы в объекты.</p>
<h6><sup>*</sup> примечание переводчика:</h6>
<p class="notice">вероятно, автор имеет ввиду создание различных областей видимости, что достижимо в этом языке лишь с помощью функций (прим. пер.)</p>
<h3 class="floatBar">Пространства имен без Ext JS</h3>
<p>Без Ext JS вы можете создать пространство имен следующим образом:</p>
<pre name="code" class="js">
if (!App) App = {};
if (!App.form) App.form = {};
if (!App.data) App.data = {};</pre>
<h3 class="floatBar">Ext.namespace</h3>
<p>Объект Ext предоставляет метод <a href="http://extjs.com/deploy/dev/docs/?class=Ext&amp;member=namespace">Ext.namespace</a> (или его шоткат Ext.ns), который проверяет создаваемое пространство имен на существование и создает его, если такового еще нет. Сначала следует определить первоначальный уровень пространства, а затем можно создавать различные подуровни-пакеты. Например, создадим пространство имен <em>App</em> и входящие в него пакеты <em>form</em> и <em>data</em>:</p>
<pre name="code" class="js">
/* Ext.namespace создаст объекты с переданными именами, если они еще не существуют */
Ext.namespace('App', 'App.form', 'App.data');

/* Теперь можно определять новый класс, например SampleForm, внутри пакета App.form */
App.form.SampleForm = Ext.extend(Ext.form.FormPanel, {
    initComponent: function() {
        /* код настройки компонента */
       App.form.SampleForm.superclass.call(this);
   }
});
/* Определение MySingleton внутри пакета App.data */
App.data.MySingleton = function() {
    return {
        sampleStaticMethod: Ext.emptyFn
    };
}();</pre>
<h3 class="floatBar">В завершение</h3>
<p>Используемый в web-приложениях на стороне клиента код JavaScript становится все более сложным и сложным. Поэтому важность правильной организация совместной работы вашего кода и кода третьих сторон также вырастает. Использование пространств имен гарантирует защиту вашему коду от перезаписи другим, находящимся в глобальной области видимости, кодом.</p>
<h3>От переводчика</h3>
<p>Автор оригинала: Aaron Conran</p>
<p>Оригинал статьи: <a href="http://extjs.com/blog/2008/05/28/use-namespaces/">Use Namespaces to organize your JavaScript code</a></p>
]]></content:encoded>
			<wfw:commentRss>http://web-dev.info/2008/06/javascript-namespaces/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Pre-Post диспетчеризация, авторизация и Zend_Controller_Plugin_ErrorHandler</title>
		<link>http://web-dev.info/2008/05/pre-post-dispatch-auth-error-handling/</link>
		<comments>http://web-dev.info/2008/05/pre-post-dispatch-auth-error-handling/#comments</comments>
		<pubDate>Tue, 13 May 2008 23:34:12 +0000</pubDate>
		<dc:creator>wd</dc:creator>
		
		<category><![CDATA[Zend Framework]]></category>

		<category><![CDATA[Zend_Acl]]></category>

		<category><![CDATA[Zend_Controller_Plugin_ErrorHandler]]></category>

		<guid isPermaLink="false">http://web-dev.info/2008/05/pre-post-dispatch-auth-error-handling/</guid>
		<description><![CDATA[ Хороший подводный камень затаился в механизме работы ZF с плагинами когда вопрос авторизации разрешается именно с их помощью.
Вводная
Общеизвестно, что кроме прочего плагины позволяют выполнять некий код до и после действия (action) контроллера. Именно тут и притаилась логическая бомба размеров чуть меньше среднего.
Обычно авторизация (определение возможности выполнения тех или иных деяний в зависимости от прав) [...]]]></description>
			<content:encoded><![CDATA[<p> Хороший подводный камень затаился в механизме работы ZF с плагинами когда вопрос авторизации разрешается именно с их помощью.</p>
<h3 class="floatBar">Вводная</h3>
<p>Общеизвестно, что кроме прочего плагины позволяют выполнять некий код до и после действия (action) контроллера. Именно тут и притаилась логическая бомба размеров чуть меньше среднего.</p>
<p><a href="http://devzone.zend.com/node/view/id/1665">Обычно</a> авторизация (определение возможности выполнения тех или иных деяний в зависимости от прав) помещается в Pre-обработчик:</p>
<pre name="code" class="php">class Application_Controller_Plugin_Auth extends Zend_Controller_Plugin_Abstract{
    public function preDispatch(Zend_Controller_Request_Abstract $request){
        /*
        ...
        */
    }
}</pre>
<h6>о выборе:</h6>
<p class="notice">конечно, можно было бы поместить проверку в обработчик dispatchLoopStartup, но если в ходе выполнения запроса будут вызываться несколько контроллеров и действий, то авторизация будет неполной</p>
<p>Код  preDispatch представляет собой проверку возможности выполнения запрашиваемого действия с вынесением вердикта в виде перезаписи значений модуля, контроллера и  действия. В случае когда перезапись нужно произвести как в случае отказа, так и благоприятного исхода и возникает неловкость.</p>
<p>Плагин <span class="phpClass">Zend_Controller_Plugin_ErrorHandler</span>, включенный по-умолчанию, регистрирует Post-обработчик и выполняет аналогичные действия с перезаписью значений модуля, контроллера и действия, но в случае возникновения исключений.</p>
<h6>об исключениях:</h6>
<p class="notice">обычно в задачу этому плагину ставится отлов отсутствия вызываемого контроллера или действия, что можно рассматривать как ошибку №404, в то время как исключения внутри существующих действий как ошибку №500</p>
<h3 class="floatBar">Суть</h3>
<p>Авторизация, в подавляющем большинстве конфигураций, пропустит нас к несуществующему действию. Однако обработчик ошибок чутко перенаправит на контроллер и действие (по-умолчанию error и error соответственно) по обработке ошибки, что в цикле диспетчеризации опять вызовет проверку авторизации. Если забыть прописать правила на доступ к контроллеру и действию обработки ошибок, мы будем повторно брошены на поиски несуществующего со всеми печальными последствиями: плагин ErrorHandler, почуяв мистический вызов себя второй раз, бросит неотлавливаемое уже никем (ясное дело, их отловом он и занимается) исключение и все будет потеряно.</p>
<h6>к слову о перестраховке:</h6>
<p class="notice">можно обернуть в try...catch вызов <span class="code">Zend_Controller_Front::getInstance() -&gt;dispatch()</span> чтобы ловить неотлавливаемое и там</p>
<h3 class="floatBar">Вывод и возможные решения</h3>
<p>Необходимо предотвратить натаптывание дорожки с граблями т.е. не давать коду авторизации перезаписывать значения модуля, контроллера и действия, если они ведут в место, где производится обработка ошибок.</p>
<p>Сделать это можно как прописыванием правил <span class="phpClass">Zend_Acl</span> для обработчика ошибок, так и проверкой на существование исключений за обработкой которого мы и обратились к некому контроллеру:</p>
<pre name="code" class="php">public function preDispatch(Zend_Controller_Request_Abstract $request){
    if ($this-&gt;_response-&gt;isException()) return;
    /*
    ...
    */
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://web-dev.info/2008/05/pre-post-dispatch-auth-error-handling/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Zend Framework: фильтры и mbstring в версии 1.5</title>
		<link>http://web-dev.info/2008/04/zf-filters-mbstring/</link>
		<comments>http://web-dev.info/2008/04/zf-filters-mbstring/#comments</comments>
		<pubDate>Sun, 13 Apr 2008 12:46:07 +0000</pubDate>
		<dc:creator>wd</dc:creator>
		
		<category><![CDATA[Zend Framework]]></category>

		<category><![CDATA[mbstring]]></category>

		<category><![CDATA[Zend_Filter]]></category>

		<category><![CDATA[Zend_Filter_Alnum]]></category>

		<category><![CDATA[ZF]]></category>

		<guid isPermaLink="false">http://web-dev.info/2008/04/zf-filers-mbstring/</guid>
		<description><![CDATA[С выходом ZF 1.5.0 фильтры Zend_Filter_Alnum и Zend_Filter_Alpha могут перестать корректно работать, если вы используете в своих проектах utf-8 и включенный модуль mbstring. Ошибка проявляет себя при фильтрации строк в которых содержатся русские (а возможно, что и все не входящие в английский алфавит) символы:
$filter = new Zend_Filter_Alnum(true);
Zend_Debug::dump($filter->filter('это странненько - mbstring enabled'));
//string(19) "   mbstring [...]]]></description>
			<content:encoded><![CDATA[<p>С выходом ZF 1.5.0 фильтры Zend_Filter_Alnum и Zend_Filter_Alpha могут перестать корректно работать, если вы используете в своих проектах utf-8 и включенный модуль mbstring. Ошибка проявляет себя при фильтрации строк в которых содержатся русские (а возможно, что и все не входящие в английский алфавит) символы:</p>
<pre name="code" class="php">$filter = new Zend_Filter_Alnum(true);
Zend_Debug::dump($filter->filter('это странненько - mbstring enabled'));
//string(19) "   mbstring enabled"</pre>
<pre name="code" class="php">$filter = new Zend_Filter_Alnum(true);
Zend_Debug::dump($filter->filter('это странненько - mbstring disabled'));
//string(48) "это странненько  mbstring disabled"</pre>
<p>Данное поведение очень тесно перекликается с ошибочной фильтрацией умляутов, о которой было <a href="http://framework.zend.com/issues/browse/ZF-2929">заявлено</a> порядка месяца назад.</p>
]]></content:encoded>
			<wfw:commentRss>http://web-dev.info/2008/04/zf-filters-mbstring/feed/</wfw:commentRss>
		</item>
		<item>
		<title>ExtJS: компонент выбора местонахождения</title>
		<link>http://web-dev.info/2008/03/location-select/</link>
		<comments>http://web-dev.info/2008/03/location-select/#comments</comments>
		<pubDate>Fri, 14 Mar 2008 01:43:05 +0000</pubDate>
		<dc:creator>wd</dc:creator>
		
		<category><![CDATA[ExtJS]]></category>

		<category><![CDATA[ajax]]></category>

		<category><![CDATA[extension]]></category>

		<category><![CDATA[JavaScript]]></category>

		<category><![CDATA[location]]></category>

		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://web-dev.info/2008/03/location-select/</guid>
		<description><![CDATA[Многие проекты на данный момент используют информацию о местонахождении своих клиентов. К таким относятся интернет-магазины, сайты знакомств, банковские операционные ресурсы и прочее. Именно об элементе указания такого рода информации и будет данная статья: Ext.ux.locationSelect реализованный в поле фреймворка ExtJS 2.
Маленькая демка поможет ответить на вопрос о необходимости вчитываться в дальнейшее.
Synopsis
Так сложилось исторически, что управляющим элементом [...]]]></description>
			<content:encoded><![CDATA[<p>Многие проекты на данный момент используют информацию о местонахождении своих клиентов. К таким относятся интернет-магазины, сайты знакомств, банковские операционные ресурсы и прочее. Именно об элементе указания такого рода информации и будет данная статья: <span class="extComponent">Ext.ux.locationSelect</span> реализованный в поле фреймворка ExtJS 2.</p>
<p>Маленькая <a href="/wp-content/themes/special/demos/locationSelect/">демка</a> поможет ответить на вопрос о необходимости вчитываться в дальнейшее.</p>
<h3 class="floatBar">Synopsis</h3>
<p>Так сложилось исторически, что управляющим элементом по выбору локации (будем пользоваться этим словом для определения месторасположения, местонахождения и иного) является некоторое количество взаимосвязанных списков <span class="code">&lt;select&gt;</span>, позволяющих последовательно уточнять локацию часть за частью. Выглядеть это может примерно <cite class="pppImage" width="855" height="474" src="/wp-content/uploads/2008/03/lmr.gif">следующим образом</cite>. Контрол в сумме удобен, малопротиворечив, но несколько устарел. Вот первые, бросающиеся в глаза, минусы решения:</p>
<ul>
<li>popup окно для донесения до посетителя всего контрола;</li>
<li>отсутствие кеширования данных селектов;</li>
<li>слабая расширяемость и гибкость — любой функционал необходимо реализовывать самостоятельно.</li>
</ul>
<h3 class="floatBar">Что необходимо получить</h3>
<p>В одном из проектов мне понадобилось обойти всё вышеперечисленное и ко всему прочему соблюсти следующее:</p>
<ul>
<li>window based дизайн — ресурс интенсивно редактируется и интерфейс решено было сделать оконным;</li>
<li>т. к. страница могла не перезагружаться при работе на ней часами, то вопрос кеширования данных стоит очень остро;</li>
<li>необходимо не только позволять выбирать локации, но и верно отображать их, в такой, например, ситуации как редактирование, когда все селекты уже означены, а соответствующие списки в них уже загружены.</li>
</ul>
<h3 class="floatBar">Инструментарий</h3>
<p>Нам потребуется:</p>
<ul>
<li>Zend Framework;</li>
<li>ExtJS JavaScript Library;</li>
<li>Класс <span class="phpClass">Application_Db_Table_Nestedset</span>, описанный в <a href="/2008/03/zf-nestedset/">предыдущей статье</a>.</li>
</ul>
<p>Использование ExtJS обусловлено требованием №1 — интерфейс window based. Только этот фреймворк способен был справиться со всеми требованиями, которые были предъявлены к процессу работы с данными.</p>
<p><acronym title="Zend Framework">ZF</acronym> и базирующийся на его составляющих <span class="phpClass">Application_Db_Table_Nestedset</span> это ответ на вопрос стандартизации кода, ответ в ряде мест противоречивый, но  все же ответ.</p>
<h6>кстати:</h6>
<p class="notice">данные <acronym title="Zend Framework">ZF</acronym>-элементы дизайна контрола можно исключить и заменить на что-либо более привычное буквально в течении получаса.</p>
<h3 class="floatBar">Контрол в действии</h3>
<p>Использование, благодаря мастерству и прозорливости разработчиков ExtJS, практически ничем не отличается от использования стандартных компонент этого пакета — создать и применить <span class="jsComponent">Ext.ux.locationSelect</span> также легко как и создать обычную панель.</p>
<h3 class="floatBar indent">Подключение</h3>
<p>Подключение реализуется в обычном порядке. Если ExtJS уже используется, то необходимо подключить только само расширение:</p>
<pre name="code" class="html">&lt;head&gt;
    &lt;script type="text/javascript" src="/lib/ext/adapter/ext/ext-base.js"&gt;&lt;/script&gt;
    &lt;script type="text/javascript" src="/lib/ext/ext-all.js"&gt;&lt;/script&gt;
    &lt;script type="text/javascript" src="/lib/ext/ux/locationSelect.js"&gt;&lt;/script&gt;
    &lt;link href="/lib/ext/resources/css/ext-all.css" rel="stylesheet" type="text/css" /&gt;
&lt;/head&gt;</pre>
<h6>для галочки:</h6>
<p class="notice">используйте правильный DOCTYPE документа, часто многие нетривиальности можно решить раз и навсегда только начав работать в правильном режиме.</p>
<h3 class="floatBar indent">Конфигурирование и создание</h3>
<p>Ввиду того, что контрол расширяет <span class="extComponent">Ext.form.FieldSet</span>, то видеть себя он предполагает в поле <span class="extComponent">Ext.FormPanel</span>, однако это необязательное требование.</p>
<h6>важно:</h6>
<p class="notice">подробнейшее описание <acronym title="Application Programming Interface">API</acronym>, конфигураций и немного примеров по каждому из контролов можно обнаружить в <a href="http://extjs.com/deploy/dev/docs/">ExtJS API Documentation</a> в соответствующей части дерева компонент, пакетов и классов.</p>
<p>Таким образом включить контрол в форму можно простым добавлением его конфигурационного объекта в items формы:</p>
<pre name="code" class="js">var myForm = new Ext.form.FormPanel({
    items: [{
        xtype: 'locationselect',
        url: '/someURL',
        prefix: 'some_location_',
        title: 'someFieldSetTitle',
        valueNotFoundText: 'Не важно',
        validator: function(){/*some js-code*/}
        autoHeight: true
    }]
});</pre>
<p>Нестандартными конфигурационными полями являются:</p>
<ul>
<li><span class="code">url</span> — адрес куда хранилища контрола будут обращаться за списками частей локаций (страны, регионы и города), по этому же адресу будет отправляться запрос на полную единовременную загрузку локации целиком. Например, Россия | Рязанская обл. | Рязань;</li>
<li><span class="code">prefix</span> — префикс имен переменных в которых будут сохранены ID частей локации. Для случая выше при сабмите контрол «сгенерирует» и отправит на сервер переменные <span class="jsVar">_some_location_country</span>, <span class="jsVar">_some_location_region</span>, <span class="jsVar">_some_location_city</span>;</li>
<li><span class="code">valueNotFoundText</span> — значение этого поля будет присвоено одноименному конфигурационному полю всех <span class="extComponent">Ext.form.ComboBox</span> контрола;</li>
<li><span class="code">validator</span> — функция будет вызываться при событии выбора любой части локации и позволит обязать, например, к указанию локации полностью.</li>
</ul>
<h6>о незаметном:</h6>
<p class="notice">обратите внимание на символ подчеркивания в именах генерируемых по префиксу переменных. Дело в том, что комбобокс в ExtJS реализации состоит из двух полей ввода — одно из которых и отправляет значение при сабмите формы т.е. приходится генерировать два похожих имени для каждого комбобокса.</p>
<p>Все остальные поля — наследие конфигурации суперкласса.</p>
<h3 class="floatBar indent">Загрузка локации целиком</h3>
<p>Существуют задачи когда необходимо позволить выбирать локацию и при этом начать выбор с той, что уже установлена — редактирование анкетных данных, оформление переезда и прочее.</p>
<p>Для этой цели контрол имеет метод <span class="jsMethod">loadLocation()</span>, который принимает конфигурационный объект формата <span class="code">{country: integer, region: integer, city: integer}</span>. При его вызове будет произведено обращение по url, указанном при создании, с передачей параметров локации. Если вернувшиеся данные соответствуют допустимому формату, то в соответствующие комбобоксы будут загружены списки с данными, а те части локации, которые были заданы в конфигурационном объекте будут выбраны.</p>
<h3 class="floatBar indent">Серверная часть</h3>
<p>Так как контрол подкачивает данные с помощью <acronym title="Asynchronous JavaScript and XML">AJAX</acronym>, необходимо «договориться» о протоколе общения его с сервером. Здесь имеется развилка:</p>
<ol>
<li>При выборе какой-либо одной части локации посредством комбобокса контрол «расчитывает» на один массив со значениями для следующего комбобокса в json-формате <span class="code">{rows: [{id: numeric, name: string}, ...]}</span>;</li>
<li>При полной загрузке локации контролу необходимы массивы для двух последних комбобоксов единовременно в json-формате <span class="code">{rows: {region: [{id: numeric, name: string}, ...], city: [{id: numeric, name: string}, ...]}}</span>.</li>
</ol>
<p>Мне было удобнее реализовать обе подгрузки в одном действии <span class="phpMethod">locationSelectGetSublocations()</span> контроллера <span class="phpClass">AjaxController</span>.</p>
<pre name="code" class="php">public function locationSelectGetSublocationsAction() {
        $this->_helper->viewRenderer->setNoRender();
        $filter = new Zend_Filter_Digits();
        $location = new Location();
        if ($this->getRequest()->getParam('country', 0) &#038;& $this->getRequest()->getParam('region', 0)){
            $countryId = $filter->filter($this->getRequest()->getParam('country', 0));
            $regionId = $filter->filter($this->getRequest()->getParam('region', 0));
            $result = array(
                'region' => $location->getSublocations($countryId),
                'city' => $location->getSublocations($regionId)
            );
            //добавляем опции по умолчанию
            array_unshift($result['region'], array('id' => 0, 'name' => 'Не имеет значения'));
            array_unshift($result['city'], array('id' => 0, 'name' => 'Не имеет значения'));
            echo json_encode(array('rows' => $result));
        } else {
            $id = $filter->filter($this->getRequest()->getParam('parentId', 1));
            $result = $location->getSublocations($id);
            //добавляем опцию по умолчанию
            array_unshift($result, array('id' => 0, 'name' => 'Не имеет значения'));
            echo json_encode(array('rows' => $result));
        }
    }
</pre>
<h6>на заметку:</h6>
<p class="notice">вы можете не использовать <acronym title="Zend Framework">ZF</acronym>, хранить данные локаций в любом удобном для вас виде и делать, что заблагорассудится, главное — возвращать данные как это указано выше.</p>
<p>Белым пятном действия является класс <span class="phpClass">Location</span> — модель таблицы <span class="dbTable">location</span>. Это ничто иное как наследник класса <span class="phpClass">Application_Db_Table_Nestedset</span> о котором велась речь в предыдущей <a href="/2008/03/zf-nestedset/">статье</a>. Код модели имеет вид:</p>
<pre name="code" class="php">class Location extends Application_Db_Table_Nestedset{

    protected $_name = 'location';
    protected $_primary = 'id';
    /**
     *  Return child locations of location
     *
     * @param integer parent location id
     * @return array location items
     */
    public function getSublocations($id){
        $result = array();
        foreach ($this->getChildren($id) as $row)
            $result[] = array('id' => $row['id'], 'name' => $row['name']);
        return $result;
    }
}</pre>
<h3 class="floatBar">Таблица данных локаций</h3>
<p>Таблицу локаций было решено вынести в отдельный пункт из-за самой её сути. В свое время пришлось попотеть, чтобы найти в Сети довольно полные данные по странам, регионам и городам. Теперь, когда эта задача решена можно <a class="tar" href="/websvn/dl.php?&#038;path=%2FlocationSelect%2Ftrunk%2Fdb%2F&#038;rev=0&#038;isdir=1">скачать</a> порядка 20к объектов одним кликом.</p>
<p>Локации упорядочены и собраны в одной таблице по схеме <cite class="footNote" post="zf-nestedset" place="1">Вложенных Множеств</cite>. Лично мне кажется, что эта схема является очень удачной для такого рода задач:</p>
<ul>
<li>данные очень редко изменяются, но часто выбираются;</li>
<li>нет многих таблиц данных и таблиц связей между ними.</li>
</ul>
<h3 class="floatBar">Ресурсы</h3>
<ul>
<li><a class="tar" href="/websvn/listing.php?path=%2FlocationSelect%2Ftrunk%2Fux%2F#_locationSelect_trunk_ux_">Ext.ux.locationSelect.js (trunk/ux)</a
<li><a class="svn" href="http://svn.web-dev.info/repos/locationSelect/trunk/ux/locationSelect.js">Ext.ux.locationSelect.js (trunk) UTF-8</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://web-dev.info/2008/03/location-select/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Zend Framework: дерево вложенных множеств</title>
		<link>http://web-dev.info/2008/03/zf-nestedset/</link>
		<comments>http://web-dev.info/2008/03/zf-nestedset/#comments</comments>
		<pubDate>Sat, 01 Mar 2008 05:45:03 +0000</pubDate>
		<dc:creator>wd</dc:creator>
		
		<category><![CDATA[Zend Framework]]></category>

		<category><![CDATA[nestedset]]></category>

		<category><![CDATA[php]]></category>

		<category><![CDATA[вложенные множества]]></category>

		<guid isPermaLink="false">http://web-dev.info/2008/03/zf-nestedset/</guid>
		<description><![CDATA[Хранение деревьев в базе — вопрос если не самый популярный, то частовозникающий.
В свое время на phpclub.ru была опубликована статья с неплохим описанием модели, структурой таблиц и примерами. Также Максимом Матюхиным был приведен класс для работы с этой моделью. После возникшей потребности этот класс был переписан под Zend Framework. При «портировании» я просто заставил код немного [...]]]></description>
			<content:encoded><![CDATA[<p>Хранение деревьев в базе — вопрос если не самый популярный, то частовозникающий.</p>
<p><!-- footnote1-->В свое время на phpclub.ru была опубликована <a href="http://phpclub.ru/detail/article/db_tree">статья</a> с неплохим описанием модели, структурой таблиц и примерами. Также Максимом Матюхиным был приведен класс для работы с этой моделью.<!-- /footnote1--> После возникшей потребности этот класс был переписан под Zend Framework. При «портировании» я просто заставил код немного по другому звучать и добавил некоторую функциональность из <a href="http://pear.php.net/package-info.php?pacid=187">PEAR::DB_NestedSet</a>.</p>
<p>Для работы с любыми классами производными от <acronym title="Zend Framework">ZF</acronym>-классов обычно рядом с директорией Zend, содержащей фреймворк,  создается директория <span class="filePath">Application</span> с аналогичной <acronym title="Zend Framework">ZF</acronym> иерархией — в этом случае Zend_Loader сможет подгрузить их в обычном порядке. Этим и обусловлено название класса — Application_Db_Table_Nestedset.</p>
<p>Код документирован и не является чем-то новым поэтому развернутый мануал по использованию приводить бессмыслено.</p>
<p>Доступ через <a class="svn" href="http://svn.web-dev.info/repos/ZF/trunk/Application/">хранилище</a> или <a class="tar" href="/websvn/listing.php?path=%2FZF%2Ftrunk%2FApplication%2FDb%2FTable%2F#_ZF_trunk_Application_Db_Table_">архивом</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://web-dev.info/2008/03/zf-nestedset/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Умное закрепление правки для PhpED</title>
		<link>http://web-dev.info/2008/02/smarty-commit/</link>
		<comments>http://web-dev.info/2008/02/smarty-commit/#comments</comments>
		<pubDate>Fri, 22 Feb 2008 05:35:43 +0000</pubDate>
		<dc:creator>wd</dc:creator>
		
		<category><![CDATA[PhpED]]></category>

		<category><![CDATA[commit]]></category>

		<category><![CDATA[database]]></category>

		<category><![CDATA[php]]></category>

		<category><![CDATA[script]]></category>

		<category><![CDATA[subversion]]></category>

		<category><![CDATA[svn]]></category>

		<guid isPermaLink="false">http://web-dev.info/2008/02/smarty-commit/</guid>
		<description><![CDATA[SVN экономит время и труд разработчика&#160;&#8212;&#160;она позволяет возвращаться к любой точке разработки, быть везде и всегда в курсе проводимых работ и многое другое. Но как всякая система экономящая время SVN требует затрат этого времени на свое функционирование. Именно о такой затрате, а именно операции закрепления правки в хранилище, экономии труда в ходе ее, а также [...]]]></description>
			<content:encoded><![CDATA[<p>SVN экономит время и труд разработчика&nbsp;&mdash;&nbsp;она позволяет возвращаться к любой точке разработки, быть везде и всегда в курсе проводимых работ и <cite class="footNote" post="vvedenie-v-subversion" place="3">многое другое</cite>. Но как всякая система экономящая время SVN требует затрат этого времени на свое функционирование. Именно о такой затрате, а именно операции закрепления правки в хранилище, экономии труда в ходе ее, а также расширения возможностей разработчика по хранению данных и пойдет речь.</p>
<p>PhpED, как и приставка Professional, здесь&nbsp;&mdash;&nbsp;ключевые моменты. Если вы используете иной редактор кода, то вам придется иной раз совершать дополнительные действия. Вы также можете создать на основе предлагаемого решения расширение и для своей любимой платформы. Что касается Professional, то только эта версия редактора поддерживает возможность связывания редактора с внешними сриптами, т.е. <i>shell handler</i>.</p>
<h3 class="floatBar">Предпосылки</h3>
<p>Самым простым и частым действием при работе с любой рабочей копией является полное закрепление всех внесенных в нее изменений. Делается это чтобы разбивка работы на этапы отражала себя и в хранилище, когда каждая правка является неким законченным результатом работы. Пока никаких сложностей, если вы привыкли использовать SVN.</p>
<p>Сложность первая, главная и последняя&nbsp;&mdash;&nbsp;база данных. Мысль, обуславливающая сложность, заключается в том, что необходимо сохранять в хранилище не только код и данные в рабочей копии, но и содержимое базы. Делая &laquo;слепок&raquo; вашего кода в хранилище, было бы разумным сохранять также и данные базы, иначе не будет возможности вернуться к прошлым правкам полностью&nbsp;&mdash;&nbsp;&laquo;старый&raquo; код будет пытаться работать с &laquo;новыми&raquo; данными.</p>
<h3 class="floatBar">Постановка задачи</h3>
<p>Немного подумав, приходим к выводу, что нужно реализовать простое, желательно масштабируемое средство, вобравшее в себя все следующие возможности:</p>
<ol>
<li>Выполнение закрепления правки в хранилище;</li>
<li>получение дампа базы и помещение его поверх существующего в рабочей копии перед закреплением;</li>
<li>обеспечение отката дампа до предыдущего, если закрепление в хранилище не произошло из-за ошибки;</li>
<li>комментарии, описывающие правку, должны браться из файла, куда они помещались во время внесения изменений&nbsp;&mdash;&nbsp;хорошая практика аккуратной работы;</li>
<li>логирование происходящего с выдачей всех диагностических сообщений.</li>
</ol>
<h3 class="floatBar">Что необходимо для решения</h3>
<p>Нам потребуется довольно обширный круг программ, которые будут в отдельности решать поставленные задачи:</p>
<ol>
<li>Консольный клиент SVN. Вне зависимости от <cite class="footNote" post="vvedenie-v-subversion" place="4">типа установки</cite>, если вы уже используете локальную инсталляцию этой системы контроля версий, то такой клиент у вас уже есть&nbsp;&mdash;&nbsp;это <span class="filePath">bin/svn.exe</span> в директории куда вы установили SVN. В ином случае вам необходимо будет его установить, причем совершенно не обязательно для использования в виде интеграции с Apache&nbsp;&mdash;&nbsp;просто установить, пусть лежит;</li>
<li>дампер баз данных&nbsp;&mdash;&nbsp;утилита конкретная каждая для своей СУБД и призванная, в зависимости от своих опций, создавать набор SQL-инструкций полностью отражающих структуру и/или данные базы;</li>
<li>PhpED Professional;</li>
<li>установленный и настроенный php пятой версии.</li>
</ol>
<p>Что касается серверов, то трудно представить себе web-разработчика, не имеющего на локальной машине тестовых серверов: http, БД и каких-либо других, по потребностям. Настолько трудно, что и пытаться не будем.</p>
<h6>сужаем круг</h6>
<p class="notice">В случае использования на локальной машине системы подобной <a href="http://denwer.ru">denwer</a> ответ на вопрос о СУБД однозначен&nbsp;&mdash;&nbsp;MySQL. Вообще, это практически стандарт в области web, таким образом, будем везде подразумевать именно ее. Требование к php пятой версии обусловлена двумя причинами: &laquo;давно пора&raquo; и &laquo;у нас он есть, даже если у нас его нет&raquo; о чем чуть позже.</p>
<p>В отношении SVN можно поступить так, как об этом говорилось выше или же установить его полностью для локального использования, о чем уже заходила беседа ранее в статье <a href="/vvedenie-v-subversion">&laquo;Введение в Subversion&raquo;</a>.</p>
<h3 class="floatBar">От теории к практике</h3>
<p>Для начала следует убедиться, что PhpED установлен с поддержкой как минимум одного php5. Если у вас в директории, куда был установлен редактор, имеется поддиректория с именем <span class="filePath">php5</span>, то все в порядке. Если это не так, то есть два пути:</p>
<ul>
<li>переустановить PhpED;</li>
<li>если вы и так используете для своей работы php5, то ничего не мешает использовать его и в наших целях, т.е. оставить в этом отношении PhpED в покое.</li>
</ul>
<p>Настройка путей ОС также необходимый шаг. SVN <acronym title="Command Line Interface">CLI</acronym> и <span class="command">mysqldump</span> можно, а в нашем случае и нужно, использовать из любой директории, если внести пути, по которым они расположены, в системную переменную <i>PATH</i>.</p>
<h3 class="floatBar">Установка и настройка скрипта</h3>
<p>Получить скрипт можно двумя путями&nbsp;&mdash; скачать <a href="/wp-content/uploads/2008/02/smartycommit.rar" title="Архив smartyCommit">архив</a> или взять непосредственно из <a href="http://svn.web-dev.info/repos/smartyCommit">репозитория</a> (<a href="http://web-dev.info/websvn/listing.php?path=%2FsmartyCommit%2F&#038;sc=1#_smartyCommit_">websvn</a>).</p>
<p>Поместите директорию <span class="filePath">smartyCommit</span> из архива в любое место на жестком диске. Можно порекомендовать размещать расширяющие функциональность PhpED скрипты в поддиректорию scripts редактора, но это&nbsp;&mdash;&nbsp;дело вкуса.</p>
<p>В настройках PhpED в разделе <span class="filePath">Tools/Integration</span> одноименной кнопкой создайте меню, задав любое нравящееся вам имя, например <i>ext</i>. В этом меню мы будем хранить наши собственные срипты. Далее нужно создать подменю, которое и будет командой вызываемой из редактора. После создания команды необходима настройка до состояния отраженного на рисунке ниже, однако обратите внимание, что потребуется указать свой путь к скрипту и выбрать нужный вам хоткей, которые могут быть отличными от тех, что приведены.</p>
<p class="image"><img src="/wp-content/uploads/2008/02/commandparams.gif" width="625" height="916" alt="Настройки команды в PhpED" /></p>
<p>Команда будет вызываться для активного проекта (в закладке Workspace он выделен жирным), а значит, показывать команду нужно только в <span class="filePath">Tools/Scripts</span>, что и отражено в установках опций <i>Show this command *</i>. Скрипт не будет логировать никаких данных, кроме ошибок, но <i>Redirect Output stream to log window</i> нужно выбрать все равно.</p>
<p>Выбор <i>shell</i>-обработчика обусловлен тем, что для выбранной работы нам подходят только <i>SRV</i> и <i>Shell</i>, но <i>SRV</i> открывает всегда новую вкладку со своим выводом в то время как решено было использовать log window.</p>
<h6>может все же SRV?</h6>
<p class="notice">Возможно. Я интенсивно использую log window, однако после рецензирования этой статьи пришел к выводу, что многим людям было бы приятнее работать с выводом в виде html, а это SRV. Кстати, переход можно сделать самостоятельно, наследуя класс и переопределяя методы вывода.</p>
<p>Командная строка содержит путь к выполняющему работу скрипту и ряд макропеременных в виде <i>@varName@</i> справку по которым можно получить посредством кнопки <i>Show macros</i>. Специальной переменной является переменная вида <i>@Proj(varName)@</i> значение которой берётся на закладке <i>Custom</i> свойств проекта (<span class="filePath">Project/Project Properties</span>) (имейте ввиду, что на закладке <a href="#nuanceCustomVar">переменной может и не оказаться</a>). С установкой покончено.</p>
<p>В указании на отправную точку работы&nbsp;&mdash;&nbsp;директорию рабочей копии мы будем опираться на проекты PhpED. Чаще всего под некий сайт или работу выделяется отдельное рабочее пространство (Workspace) в котором существует один или несколько проектов.</p>
<h6>почему несколько?</h6>
<p class="notice">Иногда удобно располагать все служебные файлы, библиотеки, настройки выше уровня <i>DocumentRoot</i>, что повышает безопасность. Такой подход сказывается на организации удаленной отладки, что и влияет на количество проектов в рабочем пространстве. Подробнее обо всех способах можно узнать из <a href="http://support.nusphere.com/viewtopic.php?t=2135">HOWTO: Set project mapping</a>.</p>
<p>В свойства проекта необходимо установить значение переменной <i>svnRoot</i> таким образом, чтобы она содержала путь к рабочей копии. Если у вас несколько проектов, то этот шаг необходимо проделать для каждого проекта т.к. неизвестно какой из них будет активным в любой момент времени.</p>
<p>Время настроек задачи: имя БД, логины, пароли и другие данные, которые меняются от задачи к задаче. Значения таких опций устанавливается посредством конфигурационного файла, располагающимся в корне рабочей копии и обычно находящимся в игнор-листе SVN. Файл имеет имя <span class="filePath">peScripts.options.php</span> и представляет собой обычный php-скрипт с одним простым массивом. Пример полного массива приведен ниже, причем все указанные значения являются значениями по умолчанию, как если бы опция отсутствовала в вашем варианте массива.</p>
<pre name="code" class="php">&lt;?php
$smartyCommit = array(
	'db' =&gt; array(
		'host'     =&gt; 'localhost',
		'user'     =&gt; '',
		'password' =&gt; '',
		'name'     =&gt; ''  //database name
	),
	'dump' =&gt; array(
		'params' =&gt; '--allow-keywords --no-create-db --quote-names', //dumper command lines additional params
		'file'   =&gt; 'dump.sql' //file where dump will store
	),
	'svn' =&gt; array(
		'params'         =&gt; '--no-auth-cache',  //svn commit command lines additional params
		'fileLogMessage' =&gt; 'logs/commit.message', //file with commit messages. Note: path is _relative_ from svnRoot
		'user'           =&gt; '',
		'password'       =&gt; '',
		'log'            =&gt; 'logs/smartyCommit.log' //file where svn commit log will stored. Note: path is _relative_ from svnRoot
	),
	'dirTmp' =&gt; 'c:/windows/temp/'
);
?&gt;</pre>
<p>По сути это все. Теперь по хоткею команды будет произведено дампирование базы данных, а затем последующая фиксация всей рабочей копии в хранилище. Если на каком-то этапе произойдет ошибка, то будет произведен откат до состояния как если бы команда вообще не была выполнена.</p>
<p>Если у вас возникли какие-либо проблемы, то проверьте все ли правильно вы сделали по установке и настройке, обращая особое внимание на пути, задаваемые в настройках.</p>
<h3 class="floatBar">Подводные камни и нюансы</h3>
<ol>
<li>Макропеременная <i>@php5@</i>, в отличие от <i>@php@</i>, которая указывает на <a href="http://ru2.php.net/manual/ru/features.commandline.php">CLI</a> версию php4, ссылается на <acronym title="Common Gate Interface">CGI</acronym> вариант. Это следует учитывать, так как в эти версии довольно сильно разнятся;</li>
<li><span class="command">mysqldump</span> читает конфигурационный файл СУБД, если вы указали в нем кодировки, то нет необходимости сообщать о них <span class="command">mysqldump</span> отдельно;</li>
<li>если команда расширения PhpED не указана как включаемая в <span class="filePath">Tools</span>, то она не будет выполняться по выбранному для нее хоткею;</li>
<li id="nuanceCustomVar">означить макропеременную <i>someVar</i> на закладке <i>Custom</i> свойств проекта станет возможно только с использованием <i>@Proj(someVar)@</i> в командах. Общий принцип&nbsp;&mdash;&nbsp;сначала заявите об использовании, затем означьте;</li>
<li>если значение макропеременной оставить пустым, то будет передано ее имя;</li>
<li>при выполнении длительной операции PhpED выдает соответствующее окошко с прогресс баром. Используйте sleep() для отладки;</li>
<li>если в свойствах команды отключено перенаправление потока Output в log window, то даже с активированным перенаправлением потока Error ошибки не будут попадать в log window&nbsp;&mdash;&nbsp;возможности писать в это окно не будет вовсе;</li>
<li>только Professional версия редактора поддерживает <i>shell</i> и <i>SRV</i> обработчики;</li>
<li>конечные слэши в путях к различным объектам можно как указывать, так и опускать&nbsp;&mdash;&nbsp;скрипт позаботится об их верном количестве;</li>
<li>файл с комментариями для <span class="command">svn commit</span> должен находиться в активном проекте, если вы хотите запускать срипт, находясь на его закладке. Обычно это нечасто at once встречающийся нюанс.</li>
</ol>
<h3 class="floatBar">Заключение</h3>
<p>Надстройка создавалась из соображения аккуратного ведения работы: тщательное комментирование, необходимость версионирования модели данных. Практически необходимо лишь выполнять первое требование&nbsp;&mdash;&nbsp;держать открытым в отдельной закладке файл с комментариями к правке и записывать в него свои действия. Как только это необходимо&nbsp;&mdash;&nbsp;делать фиксацию. Остальное возьмет на себя скрипт.</p>
]]></content:encoded>
			<wfw:commentRss>http://web-dev.info/2008/02/smarty-commit/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Ветвление в Subversion</title>
		<link>http://web-dev.info/2008/02/vetvlenie-v-subversion/</link>
		<comments>http://web-dev.info/2008/02/vetvlenie-v-subversion/#comments</comments>
		<pubDate>Tue, 19 Feb 2008 21:48:59 +0000</pubDate>
		<dc:creator>wd</dc:creator>
		
		<category><![CDATA[Техники]]></category>

		<category><![CDATA[branch]]></category>

		<category><![CDATA[merge]]></category>

		<category><![CDATA[subversion]]></category>

		<category><![CDATA[svn]]></category>

		<category><![CDATA[switch]]></category>

		<category><![CDATA[tag]]></category>

		<guid isPermaLink="false">http://web-dev.info/2008/02/vetvlenie-v-subversion/</guid>
		<description><![CDATA[Рано или поздно руки любого кто однажды притронулся к SVN, дотягиваются и до одного из самых мощных инструментов этой системы&#160;&#8212;&#160;ветвления. Поразительно как кажущаяся видимая сложность этого механизма оборачивается эффективным решением многих задач, которые возникают при любой разработке.
Данная статья основывается на предыдущей, а также на мысли, что лучший способ что-либо понять&#160;&#8212;&#160;сделать это самому.
Что такое ветвление
Ветвление (branching) [...]]]></description>
			<content:encoded><![CDATA[<p>Рано или поздно руки любого кто однажды притронулся к SVN, дотягиваются и до одного из самых мощных инструментов этой системы&nbsp;&mdash;&nbsp;ветвления. Поразительно как кажущаяся видимая сложность этого механизма оборачивается эффективным решением многих задач, которые возникают при любой разработке.</p>
<p>Данная статья основывается на предыдущей, а также на мысли, что лучший способ что-либо понять&nbsp;&mdash;&nbsp;сделать это самому.</p>
<h3 class="floatBar">Что такое ветвление</h3>
<p>Ветвление (branching) SVN&nbsp;&mdash;&nbsp;это именно то, что стоит за этим словом: создание ветвей. Ничем большим или меньшим это понятие не обладает.</p>
<p>То чем мы занимались до этого (см. статью <a href="/">&laquo;Введение в Subversion&raquo;</a>), было движением по единственной координате&nbsp;&mdash;&nbsp;времени. Однако представьте себе некий проект, в котором одновременно идет работа по многим направлениям разом, но в неком одном общем. Например, это может быть разработка &laquo;ПрограммаХ&raquo;, когда один отдел работает над известными ошибками, другой занимается разработкой плагинов, а третий работает по главному вектору разработки. Вся работа, проделываемая ими, сказывается на разнице &laquo;ПрограммаХ&raquo; от самой себя, например, в какой-то момент времени в основной версии программы содержится некая ошибка, в то время как в версии отдела по борьбе с ошибками ее уже нет.</p>
<h6>краткий итог:</h6>
<p class="notice">Чтобы обеспечить удобную работу различных разработчиков в рамках одного хранилища, дать им эффективный инструмент совмещения изменений и существует ветвление.</p>
<h3 class="floatBar">Зачем нужны ветки</h3>
<p>Представьте себе, что вам поручили оптимизировать схему подключения к основной программе плагинов. Или другой случай&nbsp;&mdash;&nbsp;в вашей среде разработки принято делать технический релиз в субботу вечером, чтобы группа по работе над качеством могла приступить к своему делу в понедельник. Как бы то ни было, из-за того, что потребуется внести много мешающих основной линии разработки изменений, самое эффективное решение&nbsp;&mdash;&nbsp;создание отдельной ветки, в то время как в основной не прекращает кипеть работа по своим графикам. Суть в примерах только в том, что они порождают различные &laquo;типы&raquo; веток. Таких несколько:</p>
<ul>
<li>Функциональные ветки;</li>
<li>Ветки релизов;</li>
<li>Метки.</li>
</ul>
<h6>это принципиально:</h6>
<p class="notice">Тип ветки&nbsp;&mdash;&nbsp;условное понятие. С точки зрения SVN любая ветка&nbsp;&mdash;&nbsp;это обычная директория в хранилище. То, что делает эту директорию тем или иным типом ветки находится в вашем ее восприятии. Это порой путает, но является исключительно простым по сути, надо только запомнить.</p>
<p>Именно наличие нескольких типов веток и объясняют странную на первый взгляд структуру хранилища по умолчанию:</p>
<p class="image"><img src="/wp-content/uploads/2008/02/structdefault.png" width="300" height="77" alt="Структура хранилища по умолчанию" /></p>
<p>Если ветками релизов и основной линией разработки все ясно, то в тени остались ветки функциональные и метки. Разница в них только во времени существования&nbsp;&mdash;&nbsp;тогда как метка может существовать сколь угодно долго, функциональная ветка вскоре по окончанию работы в ней удаляется.</p>
<h3 class="floatBar indent">Функциональные ветки</h3>
<p>Функциональная ветка создается по мере необходимости произвести некий набор изменений, который или прилично растянут по времени, или до окончательной реализации вступает в конфликт с остальными ведущимися изменениями&nbsp;&mdash;&nbsp;первый пример из начала этой главы.</p>
<h3 class="floatBar indent">Метки</h3>
<p>Может возникнуть вопрос о причине вынесения меток в отдельную директорию. Дело в том, что обычно это просто ссылка на ту или иную правку какой-либо ветки. Почему ссылка? Вспомните, <cite class="footNote" post="vvedenie-v-subversion" place="1">что говорилось о &laquo;дельте данных&raquo;</cite> в предыдущей статье. Точно такая же схема используется и при создании ветки, а в случае метки&nbsp;&mdash;&nbsp;копии ветки. Т.к. при создании метки ничего нового не появляется &laquo;дельта&raquo; по сути равна нулю и создаются просто ссылки на объекты ветки из которых создается метка. Таким образом, метки уменьшают многословность и неопределенность вида &laquo;ветка <span class="filePath">branches/version_3.14@364</span>&raquo; сводя ее к примерно следующему <span class="filePath">&laquo;tags/october&raquo;</span>&nbsp;&mdash;&nbsp;октябрьский релиз. Данные при этом не копируются, хранилище не растет вширь, навигация по множеству релизов упрощается. Совокупность меток, релизов, а при необходимости и функциональных веток, помогает организовать работу для решения задачи из второго примера в начале этой главы.</p>
<h6>краткий итог:</h6>
<p class="notice">Ветки&nbsp;&mdash;&nbsp;простые директории. То, что заставляет директории быть ветками различного типа, кроется в нашем их восприятии. Однако именно это восприятие и дает нам возможность решать широкий круг задач, будь то борьба с ошибками или предоставление клиентам регулярных релизов. Мы помогаем сами себе, а ветки&nbsp;&mdash;&nbsp;лишь инструмент.</p>
<h3 class="floatBar">Использование веток</h3>
<p>Разберемся с ветвлением на примере движка блога <a href="http://www.wordpress.org">WordPress</a>, сопровождая процесс знакомства временными диаграммами правок хранилища и иллюстрациями изменяющейся файловой структуры проекта.</p>
<p>Пусть нашей целью будет обновление WordPress v2.2.3 на v2.3.0. Также предположим, что в нашем распоряжении имеется хранилище, расположенное по адресу <span class="filePath">http://127.0.0.1/svn/web-dev</span> причем со структурой директорий как это рассматривалось выше. Главная ветка разработки с номером правки 35 содержит WordPress v2.2.3.</p>
<h6>уступка реальности:</h6>
<p class="notice">для тех, кто имеет дело с WordPress не секрет, что переход на v2.3.0 сопряжен также и с изменением БД ввиду введения в понятие движка меток-тегов. Также не во всех случаях обновление возможно простым &laquo;копированием поверх&raquo;. Для упрощения примера опустим эти тонкости. Вопрос синхронного закрепления в хранилище, как файлов, так и содержимого БД&nbsp;&mdash;&nbsp;предмет дальнейшей статьи &laquo;smarty commit&raquo;.</p>
<p>Таким образом, начальная картина хранилища и ее временная диаграмма будут иметь вид:</p>
<p class="image"><img src="/wp-content/uploads/2008/02/structwebdev.png" width="515" height="206" alt="Начальное состояние хранилища" /></p>
<p>Каковы будут действия для выполнения поставленной задачи:</p>
<ol>
<li>Создадим метку <span class="filePath">tags/v223</span> для текущего состояния <span class="filePath">trunk</span>&nbsp;&mdash;&nbsp;по сути именованный backup;</li>
<li>Создадим ветку <span class="filePath">branches/v230</span> на основе <span class="filePath">trunk</span>&nbsp;&mdash;&nbsp;поле деятельности, в котором и будет происходить файловые операции по обновлению WordPress;</li>
<li>Выполним все операции, требующиеся для обновления;</li>
<li>Сольем <span class="filePath">trunk</span> и <span class="filePath">branches/v230</span> чтобы в будущем вести в <span class="filePath">trunk</span> работу над v2.3.0.</li>
</ol>
<h3 class="floatBar indent">Создание</h3>
<p>Создание ветки осуществляется обычным копированием того, что будет ответвлено (обычно это <span class="filePath">trunk</span>) в ту директорию хранилища, которая соответствует типу создаваемой ветки.</p>
<h6>чтобы никто не ошибся:</h6>
<p class="notice">Перед тем как создавать ветки закрепите в хранилище все свои изменения, чтобы их потом не пришлось лишний раз копировать между ветвями.</p>
<p>Обратимся к нашему примеру, где для начала необходимо создать метку. Откройте хранилище <span class="filePath">http://127.0.0.1/svn/web-dev</span> посредством пункта контекстного меню <span class="contextMenu">TortoiseSVN&#8594;Repo-browser</span> на любой директории любого локального диска. Выделите директорию <span class="filePath">trunk</span> и для команды контекстного меню <span class="contextMenu">Copy to…</span> задайте адрес <span class="filePath">http://127.0.0.1/svn/web-dev/tags/v223</span>. Копирование произойдет очень быстро т.к., по сути, никакие данные не копируются, о чем уже говорилось выше. Ветка успешно создана, а о том, что это метка говорит нам только ее месторасположение.</p>
<h6>на заметку:</h6>
<p class="notice">При каждом создании ветки создается новая правка, именно поэтому в нашем примере номер правки с которой метка начинает жить самостоятельно&nbsp;&mdash;&nbsp;36.</p>
<p>Для того чтобы выполнить второй пункт нашего алгоритма повторите действия по созданию ветки (сначала, если вы поспешили закрыть браузер хранилища), но конечной целью укажите <span class="filePath">branches/v230</span>.</p>
<h6>можно иначе:</h6>
<p class="notice">клиент TortoiseSVN предоставляет альтернативный способ ветвления посредством команды <span class="contextMenu">TortoiseSVN&#8594;Branch/Tag…</span> Смысл у нее тот же самый, все изменения также производятся на стороне хранилища без передачи данных клиенту, и команда, по сути, является просто быстрым способом создания ветки без обозревания хранилища.</p>
<p>Мы добились некоторых результатов, которые можно было бы отразить следующим образом:</p>
<p class="image"><img src="/wp-content/uploads/2008/02/structwebdev_branchescreate.png" width="515" height="206" alt="Состояние хранилища после создания веток" /></p>
<h6>краткий итог:</h6>
<p class="notice">Ветка&nbsp;&mdash;&nbsp;копия чего-либо. Но технически копия &laquo;легкая&raquo;. Это означает, что при ее создании создается новая правка, все объекты которой указывают (ссылаются) на объекты предыдущей правки. Настоящее копирование произойдет только при изменении чего-либо в ветке. Все это напоминает обычное закрепление изменений и по сути таковым и является, с той лишь разницей, что добавляется дополнительная информация об истории.</p>
<h3 class="floatBar indent">Изменение</h3>
<p>Подкупающей простотой в ветках является заложенная в них очевидность: создание&nbsp;&mdash;&nbsp;копирование, изменение&nbsp;&mdash;&nbsp;работа как с обычной рабочей копией, об удалении и говорить нечего.</p>
<p>Теперь, когда у нас есть новая ветка <span class="filePath">branches/v230</span> мы можем поступить по-разному:</p>
<ol>
<li>Сделать <cite class="footNote" post="vvedenie-v-subversion" place="2">checkout</cite> ее содержимого в какую-то директорию и начать с ней работать;</li>
<li>Переключить нашу, уже существующую рабочую копию, которая указывает на <span class="filePath">trunk</span> так, чтобы она стала отражать содержимое <span class="filePath">branches/v230</span>;</li>
<li>Скопировать рабочую копию <span class="filePath">trunk</span> в какую-нибудь директорию и поступить с ней как это описано в предыдущем пункте.</li>
</ol>
<p>Первый способ в большинстве случаев будет удобен пользователям <a href="http://denwer.ru">denwer</a> ввиду возможности автосоздания виртуальных хостов&nbsp;&mdash;&nbsp;очень удобно произвести выгрузку ветки в отдельную директорию в home комплекса, перезагрузить его и работать в отдельной ветке с возможностью всегда изменить и копию соответствующую <span class="filePath">trunk</span>.</p>
<h6>чтобы никто не забыл:</h6>
<p class="notice">Мы исходим из web-ориентированности повествования. У нас тут всякие Apache и прочие www.</p>
<p>Третий вариант является частным случаем второго, который, в свою очередь, будет работать максимально эффективно при следующих условиях:</p>
<ul>
<li>Нет необходимости работать одновременно в нескольких рабочих копиях разных ветвей;</li>
<li>Хранилище расположено удаленно;</li>
<li>Переключение предполагается произвести между <span class="filePath">trunk</span> и только что созданной на ее основе ветки.</li>
</ul>
<p>Первый пункт обуславливает меньшее количество телодвижений&nbsp;&mdash;&nbsp;не надо ничего создавать, выгружать, конфигурировать, перезагружать (если вы используете не denwer) и прочее. Последний пункт экономит наш трафик при условии выполнении второго пункта и просто время, если это не так. Дело во все той же &laquo;дельте&raquo;&nbsp;&mdash;&nbsp;при создании рабочей копии мы передали бы некоторую часть хранилища полностью, в то время как при переключении&nbsp;&mdash;&nbsp;только разницу, которая в этом частном случае теоретически равна нулю.</p>
<h6>о вечном:</h6>
<p class="notice">В теории все замечательно, однако на практике немного информации все же передается, но по сравнению с checkout ее объем пренебрежительно мал.</p>
<p>Так как в способе первом ничего интересного нет, попытаемся освоить другой метод. Освоение будет заключаться в выполнении над рабочей копией trunk команды <span class="contextMenu">TortoiseSVN&#8594;Switch…</span> с указанием того на что мы будем переключаться <span class="filePath">branches/v230</span>. После завершения операции рабочая копия будет представлять собой содержимое этой самой ветки, <span class="command">update</span>, <span class="command">commit</span> и некоторые прочие операции по умолчанию будут работать уже не с <span class="filePath">trunk</span>, а с <span class="filePath">branches/v230</span>. В общем, произошло нечто ожидаемое&nbsp;&mdash;&nbsp;переключение на другую ветку разработки без утомительных выгрузок в новую рабочую копию и прочего. Теперь можно работать, как это делалось прежде. Для нашего примера это все необходимые действия по обновлению WordPress&nbsp;&mdash;&nbsp;копирование поверх обновленных файлов движка, проверке все ли в порядке, доводка напильником и прочими подручными средствами. Естественно, что в ходе этих действий можно и нужно использовать всю мощь SVN: <span class="command">update</span>, <span class="command">commit</span>, <span class="command">revert</span> и прочее.</p>
<h6>если подумать:</h6>
<p class="notice">Команды <span class="command">switch</span> и <span class="command">update</span> очень похожи&nbsp;&mdash;&nbsp;обе изменяют рабочую копию на основе анализа хранилища. На самом деле <span class="command">update</span> является частным случаем <span class="command">switch</span>, которая может сравнивать и изменять текущую рабочую копию с различными ветками в хранилище. Это более гибкий и мощный инструмент. Оправданием существования <span class="command">update</span> может послужить то, что это менее гибкий и мощный инструмент.</p>
<p>Для того чтобы продолжить линию нашего примера представим, что в ходе изменений было создано 7 правок и мы остановились на правке №44:</p>
<p class="image"><img src="/wp-content/uploads/2008/02/structwebdev_branchesedit.png" width="515" height="206" alt="Состояние хранилища после изменения ветки" /></p>
<h3 class="floatBar indent">Копирование изменений между ветками</h3>
<p>Очень распространенный вопрос, который решается в ходе интенсивного использования ветвления&nbsp;&mdash;&nbsp;копирование изменений между ветками.</p>
<p>То как часто это следует делать, необходимо ли делать вовсе и прочие вопросы, связанные с этой операцией решаются исходя из типа ветки, внутренней политики разработки и чувства меры. Например, самый распространенный случай, когда имеется главная ветка разработки и несколько функциональных ветвей. Тогда если в ветвях делаются изменения по каким-то причинам выходящие за рамки целей создания этих ветвей, их необходимо копировать в главную ветку разработки как можно скорее. Примером такого рода изменений может служить обнаруженная и исправленная ошибка, в то время как ветки не предназначались для их отлова и исправления. Как видно существует довольно много &laquo;если&raquo; и вопрос своевременности копирования изменений из ветки в ветку по праву можно считать тонким.</p>
<h6>из чувства справедливости:</h6>
<p class="notice">Тонким, но не сложным. Руководствуйтесь логикой, создавайте ветки, когда это покажется вам необходимым&nbsp;&mdash;&nbsp;довольно рутинная операция копирования изменений быстро отучит вас создавать ветки, когда в этом нет надобности.</p>
<h3 class="floatBar indent2">Копирование части изменений</h3>
<p>Пусть в ходе работ по обновлению было выяснено, что в обеих ветках (<span class="filePath">trunk</span> и <span class="filePath">branches/v230</span>) в файле <span class="filePath">www/wp-config.php</span>, который не был перезаписан обновлением, была замечена ошибка. Таким образом, ошибка содержится в обеих ветках. Предположим, что мы справили ее в текущей на данный момент <span class="filePath">branches/v230</span>, и закрепили исправление в правке №44. Задача состоит в копировании исправления в <span class="filePath">tags/v223</span>.</p>
<p>Предыдущая мысль&nbsp;&mdash;&nbsp;типичный пример засады, которые встречаются на пути не только начинающих пользователей SVN. Ошибка заключается в предании забвению факта, что все правки любой из веток&nbsp;&mdash;&nbsp;фотографии, слепки всего дерева хранилища на какой-то момент времени, а значит, мы не можем изменить уже закрепленные правки&nbsp;&mdash;&nbsp;<span class="filePath">tags/v223</span> &laquo;потеряна&raquo; для нас. К тому же эта метка, как и другие ветки, живет отдельно и изменения в ней не будут видны, например пользователям <span class="filePath">trunk</span>.</p>
<p>Следует поступить по следующей схеме:</p>
<ul>
<li>Портировать изменения в самую общую ветвь&nbsp;&mdash;&nbsp;<span class="filePath">trunk</span>, обязательно указав в комментарии, что это порт и описав его суть, чтобы для людей пользующихся одновременно <span class="filePath">trunk</span> и <span class="filePath">branches/v230</span> не поставил в тупик неожиданный могущий возникнуть конфликт;</li>
<li>Удалить <span class="filePath">tags/v223</span> или оставить ее на месте;</li>
<li>В зависимости от выбранного действия над <span class="filePath">tags/v223</span> нужно или портировать в нее изменения из <span class="filePath">trunk</span>, или создать на основе <span class="filePath">trunk</span> новую метку, назвав ее, например, <span class="filePath">tags/v223_patched1</span> с комментарием о сути патча.</li>
</ul>
<p>Теперь у нас полный порядок в логике и хоть сказанное выше звучит устрашающе, но на практике все очень просто.</p>
<p>Портирование изменений осуществляет консольная команда <span class="command">svn merge</span> или ее представитель <span class="contextMenu">TortoiseSVN&#8594;Merge…</span>. Команда принимает адреса сравниваемых деревьев и рабочую копию, к которой разница между деревьями будет применена в виде локальных (!!!) изменений. Потом их можно будет закрепить или отменить.</p>
<p>Ветви, которые участвуют в портировании изменений, дадут адреса деревьев, а направление ответит на вопрос к рабочей копии какой ветки применять разницу. В нашем примере from&nbsp;&mdash;&nbsp;<span class="filePath">http://127.0.0.1/svn/branches/v230/www/wp-config.php</span>, to&nbsp;&mdash;&nbsp;также <span class="filePath">http://127.0.0.1/svn/branches/v230/www/wp-config.php</span>, а рабочая копия для ветки <span class="filePath">trunk</span>.</p>
<h6>обратите внимание:</h6>
<p class="notice">В качестве деревьев выступает один единственный файл, частный случай обуславливаемый названием и смыслом текущей главы.</p>
<p>Адреса деревьев будут не полными без указания номеров правок. Это уточняющая координата. Обратимся к логике: мы условились, что исправления, которые мы должны портировать, закреплены в правке №44, а значит, в правке №43 их не было. Следует вывод, что диапазон номеров правок будет 43&minus;44. Это все необходимые данные.</p>
<h6>чтобы не ошибиться:</h6>
<p class="notice">Рекомендуется запускать тестовое слияние посредством кнопки &laquo;dry run&raquo; перед тем как запустить его по-настоящему.</p>
<p>Теперь исправление ошибки находится в ветке <span class="filePath">trunk</span> под правкой №45, но оно никак не отражено в метке <span class="filePath">tags/v223</span>. Логично хранить все изменения в одном месте, значит, ничего удалять не будем, а портируем только что портированные в <span class="filePath">trunk</span> изменения и туда. В этом случае адрес дерева from будет таким же, как и в предыдущем портировании, to&nbsp;&mdash;&nbsp;аналогично. Диапазон номеров правок также останется без изменений. Изменится лишь рабочая копия, которая должна будет отражать состояние <span class="filePath">tags/v223</span>. В этом нам поможет уже известная нам <span class="contextMenu">TortoiseSVN&#8594;Switch…</span>. Теперь все логически верно: все состояния, на которые можно откатиться при неудачном обновлении WordPress до v2.3.0, у нас находятся в <span class="filePath">tags/v223</span>, причем с возможностью выбора момента отката&nbsp;&mdash;&nbsp;до исправления ошибки (правка №36) и после ее портирования (правка №46).</p>
<p>Все изменения, произошедшие с хранилищем, можно отобразить следующей диаграммой, где жирная линия означает портирование изменение с последующим закреплением:</p>
<p class="image"><img src="/wp-content/uploads/2008/02/structwebdev_branchesmerge.png" width="515" height="206" alt="Состояние хранилища после портирования части изменений" /></p>
<p>Обратите внимание на жирную линию со штрихом. Это альтернативный способ портирования изменений в <span class="filePath">tags/v223</span> достигаемый при параметрах from и to равными <span class="filePath">http://127.0.0.1/svn/trunk/www/wp-config.php</span>, с диапазоном правок 35&minus;45. Аналогичный результат даст команда и для правок 35&minus;44 при неизменном адресе сравниваемых деревьев. Это обусловлено тождественностью правок 44 и 45. Как видите, результата можно достигнуть множеством путей. Однакое необходимо заметить, что эти способы работают только в конкретном случае примера, когда между правками 35 и 37 не было изменений <span class="filePath">trunk</span>, что помешало бы использованию альтернативных способов, но об этом ниже.</p>
<h6>краткий итог:</h6>
<p class="notice">При слиянии необходимо всегда руководствоваться логикой и запомнить, что merge выполняется для двух деревьев хранилища заданных адресами и диапазоном правок, а разница всегда применяется к рабочей копии или части ее, если команда была выбрана не для всей копии. Никакие изменения в хранилище при этом не вносятся&nbsp;&mdash;&nbsp;вам нужно будет сделать это самостоятельно позже обычным commit, если потребуется. Сломать что-либо слиянием очень сложно, хотя запутаться есть где. Затем и логика.</p>
<h3 class="floatBar indent2">Слияние веток полностью</h3>
<p>Мы только что закончили все операции по обновлению WordPress до v2.3.0 и остались довольными качеством работы. Время за слиянием функциональной ветки <span class="filePath">branches/v230</span> в основную линию разработки <span class="filePath">trunk</span>.</p>
<p>Опять прибегаем к логике: необходимо сравнить <span class="filePath">trunk@35</span> и <span class="filePath">branches/v230@44</span> чтобы получить разницу которую надо применить к <span class="filePath">trunk</span>. Для нашего конкретного случая это сработает, все верно. Но это ошибочный путь, не думайте в таком ключе. Представьте себе, что кто-то из ваших товарищей все это время работал над <span class="filePath">trunk</span> и вносил туда изменения. Тогда при попытке вычислить разницу между <span class="filePath">trunk@35</span> и <span class="filePath">branches/v230@44</span> вы засечете и его изменения, которые, пересекаясь с вашими, могут дать непредсказуемые результаты. Гораздо легче и безопаснее сравнивать <span class="filePath">branches/v230@37</span> и <span class="filePath">branches/v230@44</span>, что оправдано и логически: взять все изменения в ветке <span class="filePath">branches/v230</span> с момента ее создания до последней правки.</p>
<p>Самое &laquo;сложное&raquo;&nbsp;&mdash;&nbsp;определить начальную правку ветки. Делается это посредством просмотра лога ветки при выбранной опции &laquo;stop on copy/rename&raquo;, что в свою очередь достигается через контекстное меню нужной ветки в repo-browser.</p>
<p>Остальные моменты слияния ничем не отличаются от уже делавшихся нами в предыдущей части этой статьи.</p>
<h6>для внимательных:</h6>
<p class="notice">Можно заметить, что для trunk часть изменений применяется дважды, что объясняется неспособностью автора придумать более элегантный пример. Однако ничего страшного в этом двойном применении нет т. к. второго, по сути, не произойдет&nbsp;&mdash;&nbsp;в этом месте &laquo;дельта&raquo; изменений и рабочей копии будет равна нулю.</p>
<p>Теперь ветку <span class="filePath">branches/v230</span> можно удалить или оставить и создать метку <span class="filePath">tags/v230</span>, что уже не является сложной задачей.</p>
<h3 class="floatBar">Подводные камни и нюансы</h3>
<ol>
<li>Ветки&nbsp;&mdash;&nbsp;простые копии;</li>
<li>Можно переключать с ветки на ветку не только всю рабочую копию, но и один единственный файл или директорию. Все, что необходимо&nbsp;&mdash;&nbsp;знать, что только с ними будет вестись работа. Также имеется возможность переключаться и на любую правку ветки;</li>
<li>Обязательно комментируйте портирование изменений вплоть до номеров правок&nbsp;&mdash;&nbsp;это поможет вам в ряде случаев. Например, предположим, что <span class="filePath">branches/v230</span> не была удалена после полного слияния с <span class="filePath">trunk</span> и в ней продолжилась работа, которая привела к появлению правок 48&minus;55. Пусть необходимо также портировать эти изменения в <span class="filePath">trunk</span>. В этом случае по комментариям в логе хранилища легче всего определить, что первая волна изменений охватывала правки 37&minus;44 и была закреплена в правке 47. Значит, вторая волна должна охватывать правки 48&minus;55 и никак не 37&minus;50, что приведет к захвату изменений первой волны дважды;</li>
<li>Никто не обязывает вас делать хранилище со структурой, о которой говорилось в этой статье. Организация хранилища&nbsp;&mdash; личное дело каждого. Однако стоит помнить, что вещи проверенные временем заслуживают внимания;</li>
<li>Совсем не обязательно иметь на локальной машине такую же структуру, как и у хранилища. Может случиться, что у вас на компьютере в один момент времени будет лишь одна рабочая копия, расположенная в неком DocumentRoot некоего сайта и вы будете интенсивно использовать svn switch для доступа к иным местам хранилища. Экономьте свое место на диске и время.</li>
</ol>
<h3 class="floatBar">Заключение</h3>
<p>Ветки&nbsp;&mdash;&nbsp;это просто. Все, что необходимо в работе с ними&nbsp;&mdash;&nbsp;логика и четкое понимание происходящего. Ну и практика, конечно. Не опасайтесь их, просто используйте.</p>
]]></content:encoded>
			<wfw:commentRss>http://web-dev.info/2008/02/vetvlenie-v-subversion/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Эффект всплеска при клике</title>
		<link>http://web-dev.info/2008/02/pellet/</link>
		<comments>http://web-dev.info/2008/02/pellet/#comments</comments>
		<pubDate>Sat, 16 Feb 2008 01:11:27 +0000</pubDate>
		<dc:creator>wd</dc:creator>
		
		<category><![CDATA[JavaScript]]></category>

		<category><![CDATA[effect]]></category>

		<category><![CDATA[flash]]></category>

		<category><![CDATA[js]]></category>

		<category><![CDATA[mootools]]></category>

		<category><![CDATA[pellet]]></category>

		<guid isPermaLink="false">http://web-dev.info/2008/02/pellet/</guid>
		<description><![CDATA[Порочно угнездившееся во многих умах &#171;красота спасет мир&#187; заставляет вышеупомянутый мир биться в конвульсиях и постанывать от очередной попытки привести его к точке благоденствия.
Представим, что история предшественников ничему нас не научила и украсим document.body со всей жестокостью, на которую способен человеческий разум в погоне за прекрасным&#160;&#8212;&#160;заставим страничку откликаться всплеском как от хорошо булькнувшего в болоте [...]]]></description>
			<content:encoded><![CDATA[<p>П<del>о</del>рочно угнездившееся во многих умах &laquo;красота спасет мир&raquo; заставляет вышеупомянутый мир биться в конвульсиях и постанывать от очередной попытки привести его к точке благоденствия.</p>
<p>Представим, что история предшественников ничему нас не научила и украсим document.body со всей жестокостью, на которую способен человеческий разум в погоне за прекрасным&nbsp;&mdash;&nbsp;заставим страничку откликаться всплеском как от хорошо булькнувшего в болоте камешка.</p>
<p>Нам понадобится <a href="http://mootools.net">Mootools</a>, редактор Flash, чувство меры, руки и непредвзятое отношение ко всему, что будет дальше.</p>
<h3 class="floatBar">Что необходимо получить</h3>
<p>Нет нужды перебирать достоинства или недостатки flash. Как в случае со многими технологиями необходимо просто прийти к решению использовать его или отказаться. В этом примере мы решили закрыть глаза на все минусы и применить в качестве фундамента.</p>
<p>Озадачимся созданием анимационного эффекта всплеска от клика на канве страницы с поддержкой полупрозрачности.</p>
<h6>следует заметить</h6>
<p class="notice">Эффект надо бы назвать псевдовсплеском ввиду того, что он не деформирует поверхность как это есть при реальных кругах на воде.</p>
<p>Также необходим механизм выборочного отключения эффекта для различных элементов и их групп.</p>
<h3 class="floatBar">Эффект в действии</h3>
<p>Если вы хотите что-либо изучить, а не выучить, то лучший способ этого добиться&nbsp;&mdash;&nbsp;смотреть код самостоятельно. Документация это замечательно, код&nbsp;&mdash;&nbsp;лучше. Поэтому рекомендуется просто заглянуть в код этой страницы. Для нетерпеливых выборочные участки кода приведены ниже.</p>
<p>В самом простом случае включение эффекта осуществляется подключением <span class="filePath">pellet.js</span> и созданием объекта эффекта:</p>
<pre name="code" class="html">&lt;html&gt;
	&lt;head&gt;
	//…
	&lt;script type="text/javascript" src="/js/pellet.js"&gt;&lt;/script&gt;
	//…
	&lt;/head&gt;
	&lt;body&gt;
		&lt;script type="text/javascript"&gt;
			var myPellet;
			window.addEvent('domready', function(){
				myPellet = new pellet();
			});
		&lt;/script&gt;
	&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Смысл заключается в создании объекта после загрузки всего дерева элементов страницы. Реализуется это силами Mootools в теле обработчика нестандартного события onDomReady. Это все.</p>
<h3 class="floatBar">Подгонка эффекта</h3>
<p>Как изменить цвет всплеска, его размеры, фактуру? Просто откройте <span class="filePath">pellet.fla</span> и рисуйте. Стоит, однако, помнить, что если вы изменили габариты flash-ролика, то об этом надо сообщить и js-части.</p>
<p>Для того чтобы задать элементы при клике на которых эффект воспроизводиться не будет, используется один из параметров конструктора exclude. Параметр представляет собой строку, где через запятую в синтаксисе CSS перечислены правила для исключаемых элементов.</p>
<h6>на заметку</h6>
<p class="notice">Mootools&nbsp;&mdash;&nbsp;правильный &laquo;браузер&raquo; и поддерживает большинство из всех мыслимых комбинаций селекторов. Обратитесь к шпаргалке по CSS, если хотите узнать о них больше. Это автоматически расширит ваши возможности в исключении элементов из &laquo;поля видимости&raquo; эффекта.</p>
<p>Немного примеров:</p>
<pre name="code" class="js">var myPellet = new pellet();
/*
все параметры по умолчанию:
	ролик находится по /flash/pellet.swf
	ширина: 50px
	высота: 50px
	исключаемые элементы: *[disabled], input[type=text], input[type=checkbox], input[type=radiobutton], textarea, select
*/</pre>
<pre name="code" class="js">myBluePellet = new pellet({flash: '/flash/bluePellet.swf'});
/*
ролик находится по /flash/bluePellet.swf
все остальные параметры по умолчанию:
	ширина: 50px
	высота: 50px
	исключаемые элементы: *[disabled], input[type=text], input[type=checkbox], input[type=radiobutton], textarea, select
*/</pre>
<pre name="code" class="js">myCustomPellet = new pellet({flash: '/flash/customPellet.swf', width: 40, height: 40, exclude: '#header, div.blackHole, input[type~=submit]'});
/*
все параметры переопределены:
	ролик находится по /flash/customPellet.swf
	ширина: 40px
	высота: 40px
	исключаемые элементы: #header, div.blackHole, input[type~=submit]
*/</pre>
<h3 class="floatBar">Подводные камни и нюансы</h3>
<ol>
<li><span class="captionLi">Когда начинать</span>? Создание объекта должно произойти только после полной загрузки объектной модели документа. IE, например, имеет дурную привычку выводить сообщение о невозможности загрузить страницу при попытке писать в еще не загрузившийся <span class="filePath">document.body</span>.</li>
<li><span class="captionLi">IE и фоновое изображение</span>. IE иногда глючит, забывая обновить фоновую картинку после того как всплеск исчез. В этом можно убедиться, кликнув на грани градиентного фона внизу страницы.</li>
<li><span class="captionLi">Linux</span>. В большинстве дистрибутивов Linux и Unix наблюдается отвратительная поддержка прозрачного фона flash-ролика&nbsp;&mdash;&nbsp;ее попросту нет. Ужасное зрелище, особенно на темных страницах. Рекомендуется определять тип платформы чтобы не использовать эффект который плохо поддерживается.<br />
<h6>примечание</h6>
<p class="notice">В Mootools 1.2dev имеется набор свойств <span class="filePath">Client.Platform</span> который поможет в детектировании платформы. К сожалению, это полезное семейство отсутствует в версии 1.11 которая является стабильной веткой фреймворка.</p>
</li>
<li><span class="captionLi">Когда лучше</span>? В случае светлой странички&nbsp;&mdash;&nbsp;мелкие цветовые артефакты эффекта заметны менее, да и оттенки видны лучше.</li>
<li><span class="captionLi">Минус</span>. Заключается он в перекрытии собой на время проигрывания всего, что находится под ним. Например, в случае сгруппированных рядом чекбоксов не всегда получится с должной скоростью прощелкать по всем ним&nbsp;&mdash;&nbsp;клик будет попадать по &laquo;всплеску&raquo;.</li>
</ol>
<h3 class="floatBar">Заключение</h3>
<p>В качестве вкусного напоследок будет <a href="/wp-content/uploads/2008/02/pelletexamples.rar">архив</a> с примерами, исходниками двух &laquo;плюх&raquo;, их swf версиями и самим js-классом. Приятного <del>аппетита</del> <ins>чтения</ins>.</p>
]]></content:encoded>
			<wfw:commentRss>http://web-dev.info/2008/02/pellet/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Введение в Subversion</title>
		<link>http://web-dev.info/2008/02/vvedenie-v-subversion/</link>
		<comments>http://web-dev.info/2008/02/vvedenie-v-subversion/#comments</comments>
		<pubDate>Fri, 15 Feb 2008 04:26:38 +0000</pubDate>
		<dc:creator>wd</dc:creator>
		
		<category><![CDATA[Техники]]></category>

		<category><![CDATA[beginning]]></category>

		<category><![CDATA[subversion]]></category>

		<category><![CDATA[svn]]></category>

		<guid isPermaLink="false">http://web-dev.info/2008/02/vvedenie_v_subversion/</guid>
		<description><![CDATA[ Системы контроля версий. Согласитесь, звучит. А чем дольше пользоваться одной из них, тем все более соблазнительные для искушенных умов рисуются перспективы. Начнем.
В этой статье мы поговорим, установим и попытаемся использовать в своих корыстных целях одну из разновидностей СКВ&#160;&#8212; Subversion. Почему так? Subversion очень популярна среди разработчиков, предоставляется многими хостерами, а возможная тесная интеграция с [...]]]></description>
			<content:encoded><![CDATA[<p> Системы контроля версий. Согласитесь, звучит. А чем дольше пользоваться одной из них, тем все более соблазнительные для искушенных умов рисуются перспективы. Начнем.</p>
<p>В этой статье мы поговорим, установим и попытаемся использовать в своих корыстных целях одну из разновидностей <acronym title="Система контроля версий">СКВ</acronym>&nbsp;&mdash; Subversion. Почему так? Subversion очень популярна среди разработчиков, предоставляется многими хостерами, а возможная тесная интеграция с web-сервером Apache только добавляет ей очков. Чем-то Subversion походит в этом отношении на PHP — также много отличных альтернатив, но…</p>
<h6>на заметку</h6>
<p class="notice">Примечание: не стоит думать, что Subversion это «только под web и для web». <a href="#h33">Соответствующим образом настроенная</a> эта СКВ подойдет всем кто к ней готов.</p>
<h3 class="floatBar">Причины использования</h3>
<p>Подавляющее большинство разработчиков приходят к СКВ, когда им перестает хватать резервного копирования проекта в виде стека директорий (оставим в стороне случай, когда использование СКВ — требование «свыше», мы все не любим насилие). Кто-то пытается подвести под наименование таких директорий некую систему (к примеру, в виде дат), кто-то хранит описание копий в файлах, кто-то находит решение в соответствующем ПО вроде <a href="http://nncron.ru/index_ru.shtml#nnbackup">nnbackup</a>. Однако приходит определенный момент и мысли тянутся к более эффективным решениям&nbsp;&mdash; СКВ. Установив одну из них, можно не беспокоиться о создании резервных копий данных — все будет сделано автоматически. Более того, вскоре вы даже забудете о таком явлении как резервирование, вы будете пользоваться широкими возможностями СКВ, не замечая, что создается один backup за другим. И это хорошо.</p>
<p>Также одной из самых распространенных причин применения СКВ может служить командная разработка. В таком случае контроль монопольного редактирования, отслеживание изменений и их слияние являются настоящим кошмаром, если не использовать СКВ.</p>
<h6>малый итог</h6>
<p class="notice">Использование СКВ оправдано как в случае одиночного разработчика, так и для ситуации когда их несколько.</p>
<h3 class="floatBar">Общая теория</h3>
<p>Для начала необходимо усвоить базовые понятия и термины, единые для большинства различных СКВ. Их не много:</p>
<ul>
<li>repository (хранилище) — место где СКВ хранит данные, информацию об их изменениях и другую системную информацию;</li>
<li><!-- footnote2 -->checkout — извлечение части или всего содержимого хранилища и создание на их основе рабочей копии<!-- /footnote2 -->;</li>
<li>working copy (рабочая копия) — слепок хранилища расположенный на локальной машине. Именно рабочая копия подвергается изменениям перед тем как данные будут занесены в хранилище. Рабочая копия содержит также данные о версиях содержащихся в ней файлов. Именно их наличие и определяет, что директория и есть рабочая копия;</li>
<li>revision (версия, номер правки, правка) — каждый объект в понятиях СКВ имеет версию, номер которой назначается автоматически, если документ изменялся;</li>
<li>commit (закрепление изменений) — копирование изменений из рабочей копии в хранилище. При этом создается новая правка;</li>
<li>update (обновление) — операция получения данных из хранилища, если в рабочей копии они устарели;</li>
<li>merge (слияние) — объединение независимых изменений над одним файлом. Такое происходит, когда в разных правках менялся один и тот же файл.</li>
</ul>
<p>Также потребуется рассмотреть самые известные реализации СКВ, чтобы знать об их возможностях и недостатках:</p>
<ul>
<li>CVS (Concurrent Version System) — система, своими корнями уходящая в далекие 80-е, очень популярна и сейчас. Обладает почти всеми необходимыми инструментами для контроля изменений, сравнения, слияния и устранения конфликтов. Существенным минусом данной реализации является отсутствие контроля версий директорий;</li>
<li>VSS (MS Visual SourceSafe) — реализация СКВ софтверного гиганта, ориентированная на индивидуальных разработчиков или небольшие их группы. Доступна посредством установки Visual Studio. Имеет старшего брата Visual Studio Team Foundation Server. К недостаткам относится Win32-ориентация;</li>
<li>SVN (Subversion) — система контроля версий пришедшая на смену CVS и призванная устранить все ее недостатки. <!-- footnote3-->Имеет возможность интегрирования с web-сервером Apache, отслеживает версии не только файлов, но и директорий. За каждым объектом в хранилище можно закрепить любую информацию в так называемых свойствах и она также попадет под контроль версий.<!-- /footnote3--></li>
</ul>
<p>Дополнительную информацию о приведенных и других реализациях СКВ можно найти, запросив Google.</p>
<h6>малый итог</h6>
<p class="notice">Даже на первый взгляд видна исключительная аппетитность SVN. Возможно, дело в предвзятом изложении материала, но вероятнее всего, что это законное положение лидера.</p>
<h3 class="floatBar" title="Subversion">Subversion</h3>
<p>Будем последовательны — разовьем тему SVN.</p>
<p>Главное преимущество SVN — интеграция с Apache. Это настоящий подарок web-разработчикам — один привычный сервер, известные подходы в обеспечении безопасности, авторизованного доступа и прочего.</p>
<h6>замечание</h6>
<p class="notice">CVS также имеет модуль позволяющий использовать ее с Apache, но он слабо распространен и последний раз обновлялся в далеком 2003 году.</p>
<p>Свобода на пространстве файловых операций: файлы и директории как в рабочей копии, так и в хранилище можно свободно копировать, перемещать, удалять — никаких накладок это не вызовет.</p>
<p><!-- footnote1 -->SVN всегда передает в хранилище «дельту»&nbsp;&mdash; только измененные данные. Не важно, бинарный ли это файл или любимые цитаты с башорга, но на сервер отправятся только новые данные.<!-- /footnote1 --></p>
<p>Спокойствие за свои изменения при закреплении их в хранилище — SVN или закрепит все изменения, или не сделает этого вовсе, так что путаницы не будет.</p>
<h6>риторика</h6>
<p class="notice">Надо ли говорить, что SVN также наследует все положительные стороны своего родителя — CVS.</p>
<p>Отличным инструментом, предоставляемым SVN, является команда вычисления разницы, которая может применяться как к отдельным файлам, так и к правкам. Хотите узнать, что было изменено коллегой или левой рукой за вчерашний день? Это просто.</p>
<h6>примечание</h6>
<p class="notice">Нельзя <del>вычислить</del> <ins>отобразить</ins> разницу между двумя директориями, двумя фильмами или другими бинарными файлами. К слову, для некоторых бинарных файлов возможность сравнения все же имеется, но реализована она на уровне хака, надстройки. К файлам такого рода относятся в основном файлы офисного пакета MS Office и ограниченный набор  файлов изображений.</p>
<p>Несколько разработчиков могут редактировать один и тот же файл и если их изменения не пересекаются, то SVN даже не пикнет — оба изменения будут закреплены автоматически. Возможно, что вы об этом даже не узнаете.</p>
<p>Всегда в распоряжении возможность создания веток — самостоятельных линий разработки, начинающихся в точке ветвления и ничем не отличающихся от основной по принципам работы. Это очень удобно, когда требуется внедрить в проект некую функциональность, реализация которой потребует множества правок. Ваши действия будут мешать остальным разработчикам (или вам самим) просто тем, что какое-то продолжительное время не смогут быть завершены. По окончанию работы в ветке изменения, внесенные в проект на ее пути, можно «слить» в основную линию разработки с минимальными усилиями со стороны разработчиков.</p>
<h3 class="floatBar indent">Виды установки</h3>
<p><!-- footnote4--></p>
<p>Различают три способа установки SVN сказывающиеся на типе доступа к хранилищу:</p>
<ul>
<li>Под управлением web-сервера Apache посредством mod_dav_svn;</li>
<li>В виде самостоятельного сервиса <span class="command">svnserve</span> — небольшого демона, который для связи с клиентами использует собственный протокол;</li>
<li>Не устанавливать SVN вовсе.</li>
</ul>
<p><!-- /footnote4--></p>
<h6>важно</h6>
<p class="notice">В случае интеграции с Apache возможна установка только для Apache версии 2.x. Это связано с рядом архитектурных особенностей второй ветки сервера. Если вы используете Apache версии 1.x, можно порекомендовать установить Apache версии 2.x в виде второго web-сервера на другом порту.</p>
<p>Сравнение двух первых способов можно видеть в таблице ниже:</p>
<table class="data" width="100%">
<tr>
<th>Возможность</th>
<th>Apache + mod_dav_svn</th>
<th>svnserve</th>
</tr>
<tr>
<td>Авторизация:</td>
<td>стандартное установление личности средствами HTTP(S), сертификаты X.509, LDAP, NTLM, а также другие механизмы, доступные для использования в Apache</td>
<td>CRAM-MD5 или SSH</td>
</tr>
<tr>
<td>Хранение учетных записей:</td>
<td>внутренний файл «users»</td>
<td>внутренний файл «users» или использование существующих системных (SSH) учетных записей</td>
</tr>
<tr>
<td>Шифрование:</td>
<td>опционально через SSL</td>
<td>опционально через SSH-туннель</td>
</tr>
<tr>
<td>Логирование:</td>
<td>полное логирование каждого HTTP запроса как это настроено в Apache</td>
<td>не поддерживается</td>
</tr>
<tr>
<td>Просмотр через web:</td>
<td>ограниченная встроенная поддержка, или использование программ сторонних разработчиков, таких, как ViewVS</td>
<td>только при помощи программ сторонних разработчиков, таких, как ViewVS</td>
</tr>
<tr>
<td>Скорость:</td>
<td>более низкая</td>
<td>более высокая</td>
</tr>
<tr>
<td>Установка:</td>
<td>более сложная</td>
<td>менее сложная</td>
</tr>
</table>
<p>Оба вышеописанных варианта подразумевают, что доступ к хранилищам будет предоставляться на основе парадигмы «клиент-сервер». Это частая ситуация когда разработчиков много. Однако нередко нужно развернуть систему контроля версий без подобных сложностей. В таком случае потребуется только клиент — программа необходимая в любом случае. У подхода есть один минус — файловая система, на которой находится хранилище, должна быть локальной.</p>
<h6>краткий итог</h6>
<p class="notice">Если заниматься разработкой web-приложений под управлением Apache, то выбор очевиден — один и привычный сервер. В ином случае целесообразно использовать поставляемое с SVN <span class="command">svnserve</span>. Когда же требуется локальная реализация SVN, без доступа извне, то можно обратить внимание на третий вариант.</p>
<h3 class="floatBar indent">Настройка</h3>
<p>Так как статья носит web-ориентированный характер, мы коснемся только реализации «SVN через протокол WebDAV» под управлением сервера Apache. Этот способ даст нам следующие преимущества по сравнению с другими:</p>
<ul>
<li>Просмотр содержимого последней правки хранилища в браузере — мы все любим ссылки;</li>
<li>Передаваемые данные могут быть сжаты обычным образом с помощью mod_deflate;</li>
<li>С помощью скриптов на своем любимом языке можно оперировать данными хранилища.</li>
</ul>
<p>Условимся использовать в этой статье пути, которые используются мной в повседневности: мне удобно излагать, а вам не привыкать видеть чужие конфиги. Также «сразу» предупреждаю, что разговор будет идти на языке Windows. Извините.</p>
<p>Пусть Apache установлен у нас как это видно на рисунке ниже. Содержимое <span class="filePath">home</span> — ряд директорий, внутри которых имеется директория <span class="filePath">www</span> — DocumentRoot соответствующего виртуального хоста.</p>
<p class="image"><img src="/wp-content/uploads/2008/02/virtualhosts.gif" alt="Вид директорий с которыми мы будем иметь дело" height="260" width="201" /><br />
Вид директорий с которыми мы будем иметь дело</p>
<h6>вы заметили?</h6>
<p class="notice">Файловая организация популярного пакета <a href="http://denwer.ru">denwer</a> и используемая в данной статье, пересекаются в <span class="filePath">home</span> — функции этой директории идентичны и там, и там. Этот факт может помочь для перенесения  изложенного материала на рабочее пространство denwer.</p>
<p>Убедимся, что у нас есть, установлено и настроено все необходимое:</p>
<ul>
<li>Web-сервер Apache;</li>
<li>Модуль расширения mod_dav.so;</li>
</ul>
<p>Брать дистрибутив SVN из-за ОСзависимости лучше с <a href="http://subversion.tigris.org">сайта разработчика</a>. Установка не вызывает обычно вопросов, но есть пара нюансов:</p>
<ul>
<li>Снимите чекбокс конфигурации модулей с перезапуском Apache — мы все сделаем самостоятельно;</li>
<li>Не поддавайтесь на уговоры установщика и не редактируйте autoexec.bat. Найти смысл в этой рекомендации также сложно, как и сам файл.</li>
</ul>
<p>Ссылку на дистрибутив TortoiseSVN, визуального клиента SVN для Windows, можно найти во все том же разделе загрузок. Установка. Игнорирование предложения перезагрузиться.</p>
<p>Конфигурирование Apache. Многие используют комплект denwer, у меня своя сборка триады Apache, PHP, MySQL, есть иные варианты. Объединяет их одно — во всех случаях файл httpd.conf должен существовать. Им и займемся:</p>
<ul>
<li>Там где у вас скопление директив загрузки модулей «LoadModule» добавьте еще одну:
<pre name="code" class="html">LoadModule dav_svn_module "@SubversionDir@/Subversion/bin/mod_dav_svn.so"</pre>
</li>
<li>Убедитесь, что также активна директива загрузки модуля WebDAV, модуль существует, а его загрузка идет до загрузки dav_svn_module:
<pre name="code" class="html">LoadModule dav_module modules/mod_dav.so</pre>
</li>
<li>Там где вам это кажется уместным, но обязательно в файле httpd.conf создайте описание «директории» для SVN:
<pre name="code" class="html">&lt;Location /svn&gt;
	DAV svn
	SVNParentPath "d:/www/svn_repository"
&lt;/Location&gt;</pre>
</li>
</ul>
<p>Создаем <span class="filePath">d:\www\svn_repository</span>, перезагружаемся и пытаемся запустить Apache. В случае удачи ничто вас не побеспокоит. Если же что-то пошло не так, то следует разобраться в ошибке с помощью чтения логов и действий по обстоятельствам.</p>
<h3 class="floatBar indent">Использование</h3>
<p>Теперь, когда все необходимое установлено и работает можно вручить свой проект в надежные руки SVN.</p>
<p>Обычный процесс работы с SVN можно изобразить, как это сделано на схеме ниже, которая отражает полный цикл жизни: от создания хранилища до его использования.</p>
<p class="image"><img src="/wp-content/uploads/2008/02/scheme1.gif" alt="Схема действий при работе с SVN" height="180" width="340" /><br />
Схема действий при работе с SVN</p>
<h6>Примечание</h6>
<p class="notice">Кружочек с вложенным кружочком на схеме означает, что действие составное — может состоять из многих различных действий.</p>
<p>Разберемся со всем по порядку:</p>
<ol>
<li><a href="#svnWork1">Создание хранилища</a>;</li>
<li><a href="#svnWork2">предимпортная настройка</a>;</li>
<li><a href="#svnWork3">импорт проекта в хранилище</a>;</li>
<li><a href="#svnWork4">создание рабочей копии</a>;</li>
<li><a href="#svnWork5">редактирование рабочей копии</a>;</li>
<li><a href="#svnWork6">закрепление изменений</a> (правки);</li>
<li><a href="#svnWork7">выборка данных из хранилища</a>;</li>
<li><a href="#svnWork8">отмена изменений</a>.</li>
</ol>
<p>Для того чтобы различные действия были понятны договоримся каждый из шагов снабжать популярным объяснением которое будет основано на двух персонажах:</p>
<ul>
<li>Разработчик — человек, который выполняет работу. К такой работе сводится создание, удаление, изменение файлов составляющих некий продукт или гордость за все вместе взятое;</li>
<li>Лидер — человек, который определяет линию разработки, а также обеспечивает наличие всех необходимых в разработке вещей, например, данных.</li>
</ul>
<p>Подобная, очень упрощенная, организация процесса труда принята для повышения эффективности разработки в целом. Но представьте себе ситуацию, когда разработчик, например в ситуации с личным блогом, один. Никто не возьмет на себя роль лидера, ее придется выполнять самостоятельно. Поэтому, если какие-то действия покажутся вам странными и непонятными, попытайтесь распределить роли и все встанет на свои места.</p>
<ol>
<li> 		<a id="svnWork1">Создание хранилища</a>. Хранилище или иначе репозиторий — особое место, где SVN в собственном формате хранит все наши данные в виде правок. Директорией, где мы решили разместить хранилища, будет <span class="filePath">d:\www\svn_repository</span>, а хранилище будет называться <span class="filePath">web-dev</span>. Тогда создаем директорию <span class="filePath">d:\www\svn_repository\web-dev</span> и через контекстное меню для нее выбираем <span class="contextMenu">TortoiseSVN→Create repository here…</span> Тип репозитория должен быть FSFS. Хранилище создано и готово к работе;<br />
<h6>метафора</h6>
<p class="notice">Этот шаг можно сравнивать с примерным лидером который готовит рабочее место для разработчика наряду с созданием ftp аккаунтов и прочих подобных вещей.</p>
</li>
<li> 		<a id="svnWork2">Предимпортная настройка</a>. Этот шаг опционален и не является каноническим, однако я бы порекомендовал делать его всегда, когда его есть к чему применить. Речь пойдет о настройке рабочей копии в плане задания свойства игнорирования файлов, для которых не нужно хранить информацию о версии (версионировать). Примером таких файлов могут быть «скомпилированные» шаблоны Smarty, файлы проекта редактора PhpED, дампы баз и прочее, что по каким-то причинам расположено в рамках рабочей копии, но не нуждается в отслеживании изменений. Создайте простую копию проекта, а в оригинале удалите все то, что не считаете нужным отдать под версионирование. Пока это все;<br />
<h6>метафора</h6>
<p class="notice">Лидер уже располагает какими-то данными, но он также понимает, что не все из них нужно отдавать в хранилище даже если эти «излишки» нужны в работе. Своими действиями он готовит чистую заготовку рабочей копии без всякого мусора — базовую редакцию.</p>
</li>
<li> 		<a id="svnWork3">Импорт проекта в хранилище</a>. Здесь необходимо сообщить хранилищу базовую редакцию проекта на основе которой он будет, подобно пирамиде из правок (часто очень причудливой — подумайте о ветвлении), хранить наши изменения. Пусть это будет проект блога в котором размещена эта статья: <span class="filePath">d:\www\ApachePhpMysql\apache\home\web-dev.info\</span>. Импорт осуществляется посредством пункта контекстного меню <span class="contextMenu">TortoiseSVN→Import…</span> В качестве запрошенного URL к хранилищу укажите <span class="filePath">http://127.0.0.1/svn/web-dev/trunk</span>.<br />
<h6>для ясности</h6>
<p class="notice">Частичка <span class="filePath">svn</span> в этом пути взялась из httpd.conf, где мы указали ее в директиве <span class="command">Location</span>, с тем же успехом можно было бы написать в обоих местах <span class="filePath">my_pretty_good_svn_directory</span>. Что касается частички <span class="filePath">trunk</span>, то по ее поводу забегите <a href="#nuancePaths">немного вперед</a>, если вам интересно.</p>
<p>В зависимости от объема импортируемых данных зрелище будет более или менее впечатляющее… и длительное;</p>
<h6>метафора</h6>
<p class="notice">Лидер создает фундамент на котором силами разработчика будет строиться вся разработка. Таковым фундаментом является первая правка в хранилище — результат прошедшего импорта.</p>
</li>
<li> 		<a id="svnWork4">Создание рабочей копии</a>. Теперь самое время… удалить оригинал вашего проекта.<br />
<h6>Внимание!</h6>
<p class="notice">До тех пор пока не будете уверенно использовать SVN, всегда делайте резервирование данных проекта привычным для вас образом. В процессе обучения, чтобы начать все с чистого листа, обычно не раз удаляется хранилище — а значит и резервные копии.</p>
<p>Делается это потому, что оригинал проекта не содержит еще данных о версиях файлов, а значит, не является рабочей копией. Директорию нужно таковой сделать. Для этого и производится удаление, чтобы потом создать под этим же именем директорию, которая будет рабочей копией с точки зрения SVN и файлами, составляющими блог, с точки зрения Apache. Далее, для пустой директории <span class="filePath">web-dev.info</span> запустите команду контекстного меню <span class="contextMenu">SVN→Checkout…</span>, URL хранилища нужно взять из предыдущего шага: <span class="filePath">http://127.0.0.1/svn/web-dev/trunk</span>. Теперь, после завершения, у вас есть рабочая копия вашего хранилища, с которой можно производить любые действия, вплоть до забрасывания проекта;</p>
<h6>метафора</h6>
<p class="notice">Лидер продолжает заботиться о разработчике и делать свою работу. На этом этапе он создал собственную рабочую копию т.к. необходимо навести порядок с файлами, исключенными на этапе предимпортной настройки. Тем, что над лидером нет никого, кто сделал бы эту работу для него и обусловлена эта чехарда с удалением оригинала и созданием пустой директории.</p>
</li>
<li> 		<a id="svnWork5">Редактирование рабочей копии</a>.<br />
Тут можно дать волю фантазии, например, отбросить ее в сторону и заняться делом. Под редактированием понимается любое изменение содержимого файлов, их переименование, изменение свойств и прочее. Все эти телодвижения скажутся на рабочей копии так, что нам надо будет их сохранить, а в терминах SVN — закрепить. Давайте потратим нашу первую правку на что-нибудь полезное: второй этап настройки списка игнорируемых объектов. Мысленно вернитесь к <a href="#svnWork2">этапу предимпортной настройки</a>. По его результатам у нас имеется оригинал, ставший после последующих шагов рабочей копией и старая резервная копия. Скопируйте резервную копию поверх рабочей копии. выполните для последней команду <span class="contextMenu">TortoiseSVN→Check for modifications</span> и вы увидите все файлы, которые решили не версионировать с пометкой «non-versioned». Можно отметить их как игнорируемые, в чем <a href="#nuanceIgnoreDir">клиент предоставляет нам довольно большую свободу</a> посредством контекстного меню, вызываемом при клике правой кнопкой мыши на не версионированных объектах. Дело за вами и пусть эти изменения будут пока единственными;</p>
<h6>метафора</h6>
<p class="notice">Лидер и разработчик на данном шаге одноправны — оба вносят различного рода изменения. То, что мы лишь добавили игнорирование некоторых объектов, не говорит, что это единственное действие, которое можно производить для изменения хранилища. Например, разработчик всегда будет менять файлы, находясь на этом этапе, самом часто посещаемом.</p>
</li>
<li><a id="svnWork6">Закрепление изменений</a>. Действие сводится к выбору команды контекстного меню <span class="contextMenu">SVN Commit…</span> Причем для какого объекта вы ее выбрали, тот и будет закреплен. Это открывает большие возможности по ветвлению в рамках одной ветки: можно разбить ряд внесенных изменений по правкам. Commit для всей рабочей копии, естественно приведет к закреплению всех изменений, что вы и сделали в конце предыдущего шага. Вообще, закрепление изменений рекомендуется делать осмысленно. Не раз в день, не каждые 3.14 минуты, а по задаче. В качестве примера, локализация плагина WordPress — отличная вещь, умещающаяся в одной правке. А вот локализацию большего их числа лучше разбить на несколько. Идея проверить что-либо «на живую» тоже вполне втиснется в рамки одной правки. Другое дело, если «что-либо» это что-то кардинальное, да и к тому же конфликтующее с текущими наработками, тогда дело за ветками, о чем как-нибудь потом;</li>
<li><a id="svnWork7">выборка данных из хранилища</a>. Широкие горизонты этой возможности открывает нам команды <span class="contextMenu">SVN Update</span> и <span class="contextMenu">TortoiseSVN→Update to revision…</span> Первая берет самую актуальную информацию об объекте для которого применена, а вторая позволяет выбрать номер правки из которой будет взята информация — машина времени в действии. Номер необходимой правки можно узнать по щелчку на кнопке Show Log. Еще к выборке можно отнести и команду <span class="contextMenu">SVN Checkout</span>, которая также позволяет выбрать номер правки для выборки, но доступна эта команда только для объектов не находящихся в поле рабочей копии. Таким образом, можно всегда создать рабочую копию любой правки, что также есть путешествие во времени;</li>
<li><a id="svnWork8">Отмена изменений</a>. Действия по изменению объектов рабочей копии можно отменить. Для этого существует команда <span class="contextMenu">TortoiseSVN→Revert…</span>, которая заменяет объект своим кэшированным вариантом, который соответствует последней правке. Обращения к серверу не будет, кэш находится в системных директориях <span class="filePath">.svn</span>;</li>
</ol>
<p>Теперь, когда более или менее подробно разобраны все обычно необходимые и достаточные шаги по оперированию хранилищем и рабочей копией вы можете начать развивать собственную практику общения с SVN. Осталось немного, некоторые подводные камни и нюансы.</p>
<h3 class="floatBar">Подводные камни и нюансы</h3>
<ol>
<li id="nuancePaths"><span class="captionLi">Пути</span>. Устоявшимся стандартом организации путей в хранилище является создание трех «директорий»: <span class="filePath">trunk</span>, <span class="filePath">tags</span> и <span class="filePath">branches</span>, причем текущая разработка обычно находится в <span class="filePath">trunk</span>, а остальные используются по мере надобности. Именно поэтому пути в примерах несколько странные на первый взгляд. Зачем и почему принята такая структура можно узнать из соответствующих глав книги <a href="http://svnbook.red-bean.com/nightly/ru/svn.reposadmin.projects.html#svn.reposadmin.projects.chooselayout">SVN Book</a>;</li>
<li><span class="captionLi">удаление и прочие файловые операции</span>. Если вы решили удалить или переименовать какой-то объект рабочей копии, то делайте это посредством соответствующих команд контекстного меню — изменение часть файловой системы и хранилища при последующем <span class="command">commit</span> — разные вещи;</li>
<li><span class="captionLi">рекурсивные свойства</span>. Если вы добавляете в рабочую копию новую директорию, а где-то для ее предков установлено свойство с пометкой «apply property recursively», то его надо переустановить, чтобы оно было выставлено и для новой директории;</li>
<li><span class="captionLi">что если ошибка</span>? На наше счастье операции commit и update — атомарные. Это значит, что если на протяжении этих операций возникает ошибка, то операции просто не было. Это очень напоминает транзакции;</li>
<li><span class="captionLi">ошибка «… probably out of date …»</span>. В случае данного сообщения попытайтесь сделать <span class="command">update</span> для объекта (чаще всего это директории), обычно это помогает. Дело в то, что объект был изменен как в рабочем копии, так и в хранилище. Объект необходимо сначала обновить: <span class="command">update</span> попытается объединить локальные изменения с опубликованными. Если SVN не сможет самостоятельно совершить объединение, он предложит пользователю разрешить конфликт вручную;</li>
<li><span class="captionLi">ошибка при изменении комментария к правке: «DAV request failed; it’s possible that the repository’s pre-revprop-change hook either failed or is non-existent»</span>. Изменения свойств хранилища <a href="http://svnbook.red-bean.com/nightly/ru/svn.reposadmin.create.html#svn.reposadmin.create.hooks">по умолчанию отключены</a>. Для активации возможности изменять логи правок создайте в поддиректории hooks хранилища файл с именем <span class="filePath">pre-revprop-change.bat</span> и следующим кодом:
<pre name="code" class="html">@ECHO Off
IF "%4" == "svn:log" GOTO :SVN_LOG
EXIT 1
:SVN_LOG
EXIT 0</pre>
</li>
<li> 		<span class="captionLi">переименование объектов, сводящееся к изменению регистра символов в имени</span>. В этом случае ошибка растет из регистронезависимости имен файлов ОС Windows. Что случается при любом переименовании файла? Клиент SVN помещает в системные поддиректории <span class="filePath">.svn</span> переименованный файл, но также сохраняет там и старый — конфликт! Существует два пути решения такой проблемы:
<ul>
<li>непосредственно в хранилище (рекомендуемый)
<ul>
<li>закрепить правку;</li>
<li>переименовать <span class="filePath">нужный_файл.файл</span>, используя браузер хранилища, в <span class="filePath">Нужный_Файл.файл</span>;</li>
<li>обновить рабочую копию.</li>
</ul>
</li>
<li>через временный файл
<ul>
<li>переименовать <span class="filePath">нужный_файл.файл</span> в <span class="filePath">нужный_файл.файл_</span>;</li>
<li>закрепить правку;</li>
<li>переименовать <span class="filePath">нужный_файл.файл</span> в <span class="filePath">Нужный_Файл.файл</span>;</li>
<li>закрепить правку.</li>
</ul>
</li>
</ul>
</li>
<li><span class="captionLi">перемещение через переименование</span>. В случае, когда в рабочей копии необходимо перенести объект необязательно его удалять и добавлять заново. Воспользуйтесь функцией переименования и укажите относительный путь конечной директории;</li>
<li id="nuanceIgnoreDir"><span class="captionLi">Игнорирование родителя</span>. Если вы занесли в игнор-лист некую директорию, то все ее дочерние элементы будут игнорироваться автоматически, что удобно при разнородном содержимом директории.</li>
</ol>
<h3 class="floatBar">Заключение</h3>
<p>Если вы дошли до этих строк, значит, вам это было нужно, выпейте чаю. Также возможно, что у вас просто быстрый скроллинг, но это не умаляет предыдущую мысль.</p>
]]></content:encoded>
			<wfw:commentRss>http://web-dev.info/2008/02/vvedenie-v-subversion/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
