<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	><channel>

<title>Главная страница</title>
<link>http://blog.alno.name</link>
<description>Заметки о разрaботке на C++, Java и Rails</description>

<pubDate>Tue, 22 Mar 2011 20:03:31 GMT</pubDate>
<generator>http://github.com/alno/rayo</generator>
<language>ru</language>

<atom:link href="http://blog.alno.name/ru/feed.xml" rel="self" type="application/rss+xml" />


<item>
	<title>Получение списка из различных моделей в Ruby on Rails</title>
	<link>http://blog.alno.name/ru/2011/03/rails-list-multiple-models</link>
	<comments>http://blog.alno.name/ru/2011/03/rails-list-multiple-models#comments</comments>
	<pubDate>Tue, 22 Mar 2011 20:03:31 GMT</pubDate>
	<dc:creator>Alno</dc:creator>
	<guid isPermaLink="true">http://blog.alno.name/ru/2011/03/rails-list-multiple-models</guid>
	<description><![CDATA[<p>В последнее время мне дважды задавался вопрос о том вывести в одном списке несколько Rails-моделей так, чтобы их можно было нормально фильтровать и листать.<br />
Здесь я опишу одно из возможных решений этой проблемы.</p>
<p>Предположим, что у нас в приложении есть модели <tt>Post</tt> и <tt>Question</tt> и мы хотим на главной странице их вперемежку упорядоченными по дате создания. Какие возможны решения?</p>]]></description>
	<content:encoded><![CDATA[
		<p>В последнее время мне дважды задавался вопрос о том вывести в одном списке несколько Rails-моделей так, чтобы их можно было нормально фильтровать и листать.<br />
Здесь я опишу одно из возможных решений этой проблемы.</p>
<p>Предположим, что у нас в приложении есть модели <tt>Post</tt> и <tt>Question</tt> и мы хотим на главной странице их вперемежку упорядоченными по дате создания. Какие возможны решения?</p><ul>
	<li>Можно загрузить списки обоих моделей большей длины и потом их объединить в Ruby-коде. В  этом случае основная проблема возникает с организацией постраничного вывода, поскольку мы не знаем сколько моделей каждого типа окажется на странице. И если для первой страницы еще можно загрузить из базы данных по максимальному числу моделей (сколько помещается на странице) каждого типа, то для последующих страниц необходимо еще задавать нижнюю границу &#8211; а это намного сложнее.</li>
	<li>Можно использовать наследование и хранить обе модели в одной таблице. Этот способ хорош, но не всегда применим (например, модели могут быть слишком разными, чтобы их хранить в одной таблице или схема базы данных уже может быть зафиксирована). Кроме того, в случае если в разных местах должны выводится различные пары моделей это может привести к тому, что все модели приложения придется поместить в одну таблицу.</li>
	<li>Можно использовать операцию <tt><span class="caps">UNION</span></tt> и особенности загрузки моделей в Rails. Об этом и пойдет речь ниже. Основным минусом этого подхода является необходимость использования метода <tt>find_by_sql</tt> с достаточно громоздким запросом для загрузки моделей.</li>
</ul>
<p>Итак, метод основан на использовании операции <tt><span class="caps">UNION</span></tt>. Она поддерживается основными БД, используемыми при разработке:</p>
<ul>
	<li>Sqlite &#8211; <a href="http://www.sqlite.org/syntaxdiagrams.html#compound-operator">http://www.sqlite.org/syntaxdiagrams.html#compound-operator</a></li>
	<li>MySQL &#8211; <a href="http://dev.mysql.com/doc/refman/5.0/en/union.html">http://dev.mysql.com/doc/refman/5.0/en/union.html</a></li>
	<li>PostgreSQL &#8211; <a href="http://www.postgresql.org/docs/8.3/interactive/queries-union.html">http://www.postgresql.org/docs/8.3/interactive/queries-union.html</a></li>
</ul>
<p>Суть операции состоит в том, что результаты нескольких запросов объединяются в один, к которому могут быть применены <tt>OFFSЕT</tt> и <tt><span class="caps">LIMIT</span></tt>.</p>
<p>Таким образом, для того, чтобы вывести в одном списке модели <tt>Post</tt> и <tt>Question</tt> мы можем сделать что-то вроде:</p>
<pre><code class="sql">
SELECT * FROM posts UNION SELECT * FROM questions
</code></pre><p>Однако, не все так просто, есть два подводных камня:</p>
<ul>
	<li>Необходимо, чтобы Rails как-то распознали к какому классу относится каждая запись.</li>
	<li>Необходимо, чтобы количество столбцов в каждом из объединяемых запросов совпадало.</li>
</ul>
<p>Сначала решим первую проблему. Распознавание класса записи будет производится за счет механизма, используемого в Rails для загрузки моделей с наследованием, а именно наличия поля <tt>type</tt>. Чтобы корректно это использовать, добавим в каждую модель поле <tt>type</tt> и установим ему в качестве значения по умолчанию имя класса модели.<br />
Тогда код миграция для создания моделей может выглядеть следующим образом:</p>
<pre><code class="ruby">
create_table :questions do |t|
  t.text :text
  t.string :type, :null =&gt; false, :default =&gt; 'Question'

  t.timestamps
end

create_table :posts do |t|
  t.string :title
  t.text :description
  t.string :type, :null =&gt; false, :default =&gt; 'Post'

  t.timestamps
end
</code></pre><p>В процессе загрузки класс будет определен на основе значения поля <tt>type</tt> в запросе, что и необходимо в нашем случае. Чтобы убедится, что это действительно так можно заглянуть в исходный код Rails &#8211; в методы <a href="http://github.com/rails/rails/blob/97e9d8860b277e5ca9f4187c213ddd35d7f76e9b/activerecord/lib/active_record/base.rb#L466">find_by_sql</a> и <a href="https://github.com/rails/rails/blob/97e9d8860b277e5ca9f4187c213ddd35d7f76e9b/activerecord/lib/active_record/base.rb#L907">instantiate</a></p>
<p>Теперь перейдем ко второй проблеме. Она решается достаточно просто &#8211; необходимо явно перечислить все необходимые столбцы и дополнить их значениями <tt><span class="caps">NULL</span></tt>:</p>
<pre><code class="sql">
SELECT id, type, created_at, updated_at, title, description, NULL AS text FROM posts
UNION SELECT id, type, created_at, updated_at, NULL AS title, NULL AS description, text FROM questions
ORDER BY created_at DESC
</code></pre><p>Все, практически все сделано. Можно еще добавить <tt><span class="caps">LIMIT</span></tt> и <tt><span class="caps">OFFSET</span></tt> для постраничного вывода и написать код в контроллере и виде:</p>
<pre><code class="ruby">
class IndexController &lt; ApplicationController

  def index
    @models = Post.find_by_sql(&quot;SELECT id, type, created_at, updated_at, title, description, NULL AS text FROM posts UNION SELECT id, type, created_at, updated_at, NULL AS title, NULL AS description, text FROM questions ORDER BY created_at DESC&quot;)
  end

end
</code></pre><pre><code class="ruby">
&lt;% @models.each do |m|%&gt;
  &lt;%=m.inspect%&gt;&lt;br /&gt;
&lt;%end %&gt;
</code></pre><p>Все! Для тех, кому интересно посмотреть это в действии я выложил <a href="/files/rails_test_multilist.zip">архив с проектом для Rails3</a></p>

		

		
	]]></content:encoded>
</item>
<item>
	<title>Создание билдеров (builders) в Ruby</title>
	<link>http://blog.alno.name/ru/2011/03/ruby-builders</link>
	<comments>http://blog.alno.name/ru/2011/03/ruby-builders#comments</comments>
	<pubDate>Thu, 10 Mar 2011 10:03:31 GMT</pubDate>
	<dc:creator>Alno</dc:creator>
	<guid isPermaLink="true">http://blog.alno.name/ru/2011/03/ruby-builders</guid>
	<description><![CDATA[<p>Давно я не писал ничего, постараюсь исправиться&#8230;</p>
<p>Сегодня я рассмотрю создание в Ruby билдера &#8211; подобного тому, который строит разметку (http://builder.rubyforge.org/), но чуть попроще. В частности, строить мы будем произвольные Ruby-объекты.</p>
<p>Как это должно выглядеть? В идеале вот так:</p>
<pre><code class="ruby">
obj = TestObj.build
  first_prop 11
  second_prop 'qwerty'
end
obj # &lt;TestObj:0x7fd655d04958 @first_prop=11 @second_prop=&quot;qwerty&quot;&gt;
</code></pre><p>Попробуем получить такой результат для произвольного класса.</p>]]></description>
	<content:encoded><![CDATA[
		<p>Давно я не писал ничего, постараюсь исправиться&#8230;</p>
<p>Сегодня я рассмотрю создание в Ruby билдера &#8211; подобного тому, который строит разметку (http://builder.rubyforge.org/), но чуть попроще. В частности, строить мы будем произвольные Ruby-объекты.</p>
<p>Как это должно выглядеть? В идеале вот так:</p>
<pre><code class="ruby">
obj = TestObj.build
  first_prop 11
  second_prop 'qwerty'
end
obj # &lt;TestObj:0x7fd655d04958 @first_prop=11 @second_prop=&quot;qwerty&quot;&gt;
</code></pre><p>Попробуем получить такой результат для произвольного класса.</p><p>Для начала попробуем сделать что-нибудь попроще &#8211; например, создать класс ObjectBuilder, который позволяет устанавливать из блока свойства объекта:</p>
<ul>
	<li>Поскольку такой билдер должен хранить в себе ссылку на объект мы унаследуем его от структуры с одним свойством <tt>object</tt>.</li>
	<li>Метод build будет вызывать переданный блок, передавая туда ссылку на билдер. Внутри блока мы будем обращаться к методам билдера, устанавливающим свойства объекта.</li>
	<li>Вместо того, чтобы описывать по методу для каждого объекта мы переопределим <tt>method_missing</tt> в котором все неизвестные методы будем направлять объекту.<br />
Получившийся класс:</li>
</ul>
<pre><code class="ruby">
class ObjectBuilder &lt; Struct.new( :object )

  def build
    yield self
  end

  def method_missing( name, *args, &amp;block )
    object.send &quot;#{name}=&quot;, *args, &amp;block
  end

end
</code></pre><p>Для использования такого класса сначала необходимо создать его экземпляр, передав туда объект, свойства которого должны быть установлены, затем вызвать метод <tt>build</tt> с соответствующим блоком, а затем получить объект назад:</p>
<pre><code class="ruby">
class A
  attr_accessor :a, :b, :c
end

builder = ObjectBuilder.new A.new
builder.build do |b|
  b.a 11
  b.c 12
end
builder.object # &lt;A:0x7fd655d04958 @a=11 @c=12&gt;
</code></pre><p>Поскольку мы перегрузили <tt>method_missing</tt> еще стоит перегрузить метод <tt>respond_to?</tt>. В нашем примере это ни на что не повляет, конечно, но пригодится если мы захотим проверять в рантайме какие методы поддерживает билдер. Итак:</p>
<pre><code class="ruby">
  def respond_to?( name, include_private = false )
    super || object.respond_to?( &quot;#{name}=&quot;, include_private )
  end
</code></pre><p>Теперь начнем улучшать наш код. Сначала неплохо бы избавиться от <tt>b.</tt> &#8211; для этого необходимо изменить метод <tt>build</tt>, и вместо передачи в блок билдера вызывать его (блок) в контексте билдера. Для этого используется метод <tt>instance_eval</tt>:</p>
<pre><code class="ruby">
  def build( &amp;block )
    instance_eval &amp;block
  end
</code></pre><p>Теперь можно писать следующий код:</p>
<pre><code class="ruby">
builder = ObjectBuilder.new A.new
builder.build do
  a 11
  c 12
end
builder.object # &lt;A:0x7fd655d04958 @a=11 @c=12&gt;
</code></pre><p>Уже более-менее похоже на то, что хотелось получить. Осталось убрать явное создание билдера и получение построенного объекта. Для этого выделим вышеприведенный код в отдельный модуль, которым будем расширять класс:</p>
<pre><code class="ruby">
module BuildingSupport

  def build( &amp;block )
    builder = ObjectBuilder.new self.new
    builder.build &amp;block
    builder.object
  end

end
</code></pre><p>Теперь если мы расширим класс этим модулем, то получим как раз необходимую функциональность:</p>
<pre><code class="ruby">
class A
  extend BuildingSupport

  attr_accessor :a, :b, :c
end

a = A.build
  a 11
  c 12
end
a # &lt;A:0x7fd655d04958 @a=11 @c=12&gt;
</code></pre><p>Или даже так (здесь все чуть сложнее, мне пришлось переопределить <tt>client=</tt>):</p>
<pre><code class="ruby">
class Client
  extend BuildingSupport

  attr_accessor :name, :address, :phone
end

class Order
  extend BuildingSupport

  attr_accessor :client, :name, :amount

  def client=( c=nil, &amp;block )
    @client = c || Client.build( &amp;block )
  end
end

order = Order.build do
  name &quot;Book&quot;
  amount 3
  client do
    name &quot;Client name&quot;
    address &quot;Client address&quot;
    phone &quot;000-000-000&quot;
  end
end
</code></pre>

		

		
	]]></content:encoded>
</item>
<item>
	<title>Разработка приложений с Akonadi: добавление задач в календарь</title>
	<link>http://blog.alno.name/ru/2010/01/creating-tasks-with-akonadi</link>
	<comments>http://blog.alno.name/ru/2010/01/creating-tasks-with-akonadi#comments</comments>
	<pubDate>Thu, 21 Jan 2010 11:03:31 GMT</pubDate>
	<dc:creator>Alno</dc:creator>
	<guid isPermaLink="true">http://blog.alno.name/ru/2010/01/creating-tasks-with-akonadi</guid>
	<description><![CDATA[<p>Недавно, благодаря хорошей идее на <span class="caps">KDE</span> Brainstorm я создал <a href="http://alno.name/projects/events-plasma-runner/">плагин для <span class="caps">KDE</span> Plasma Runner, позволяющий быстро добавлять задачи и события</a> в календарь, исходный код которого <a href="http://github.com/alno/plasma-runner-events">доступен на GitHub</a>.</p>
<p>Сегодня я хотел бы поделиться опытом создания, а конкретно рассмотреть тему написания приложений, использующих Akonadi.</p>
<p>В качестве примера я рассмотрю простое консольное приложение, которое позволяет добавлять задачи в календарь. Почему консольное приложение? Во-первых, чтобы не отвлекаться на аспекты, не имеющие прямого отношения к Akonadi. Во-вторых, чтобы</p>]]></description>
	<content:encoded><![CDATA[
		<p>Недавно, благодаря хорошей идее на <span class="caps">KDE</span> Brainstorm я создал <a href="http://alno.name/projects/events-plasma-runner/">плагин для <span class="caps">KDE</span> Plasma Runner, позволяющий быстро добавлять задачи и события</a> в календарь, исходный код которого <a href="http://github.com/alno/plasma-runner-events">доступен на GitHub</a>.</p>
<p>Сегодня я хотел бы поделиться опытом создания, а конкретно рассмотреть тему написания приложений, использующих Akonadi.</p>
<p>В качестве примера я рассмотрю простое консольное приложение, которое позволяет добавлять задачи в календарь. Почему консольное приложение? Во-первых, чтобы не отвлекаться на аспекты, не имеющие прямого отношения к Akonadi. Во-вторых, чтобы</p><h2>Требования</h2>

<p>Я использую Ubuntu 9.10 Karmic, в нем для работы необходимо наличие следующих пакетов:</p>
<ul>
<li><b>kdelibs5-dev</b> - библиотеки KDE</li>
<li><b>kdepimlibs5-dev</b> - библиотеки PIM KDE</li>
<li><b>libboost-dev</b> - Boost</li>
</ul>

<p>Соответственно:</p>
<pre><code class="bash">
sudo aptitude install kdelibs5-dev kdepimlibs5-dev libboost-dev
</code></pre>

<h2>Каркас приложения</h2>

<p>Итак, приступим к созданию такого приложения. Назовем его, например, addtodo. Для начала в директории будущего приложения создадим файлы для исходников:</p>

<p><b>CMakeLists.txt</b>, файл для конфигурации и сборки:</p>
<pre><code class="bash">
PROJECT(add-todo)

find_package(KDE4 REQUIRED) # Находим модули KDE4
find_package(KdepimLibs REQUIRED) # Находим модули KDE PIM

include(KDE4Defaults)

add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDEPIMLIBS_INCLUDE_DIR} ${KDE4_INCLUDES})

set(CMAKE_CXX_FLAGS &quot;-fexceptions&quot;)

kde4_add_executable(add-todo add_todo.cpp) # Добавляем цель

target_link_libraries(add-todo ${KDE4_PLASMA_LIBS} ${KDE4_KDEUI_LIBS} ${KDE4_AKONADI_LIBS} ${KDEPIMLIBS_KCAL_LIBS}) # Добавляем соответствующие библиотеки
</code></pre>

<p><b>add_todo_app.h</b>, заголовочный файл:</p>
<pre><code class="cpp">
#ifndef ADD_TODO_H
#define ADD_TODO_H

#include &lt;QCoreApplication&gt;

#include &lt;KJob&gt;

class AddTodo : public QCoreApplication {
    Q_OBJECT
public:
    AddTodo( int argc, char ** argv );
public slots:
    void collectionsFetched( KJob * job ); // Будет вызван, когда мы получим список коллекций
    void todoCreated( KJob * job ); // Будет вызван, когда мы создадим задачу
};

#endif // ADD_TODO_H
</code></pre>

<p><b>add_todo_app.cpp</b>, здесь будет содержаться основной код:</p>
<pre><code class="cpp">
#include &quot;add_todo.h&quot;

#include &lt;QTextStream&gt;

static QTextStream out( stdout ); // Поток для вывода данных

AddTodo::AddTodo( int argc, char ** argv ) : QCoreApplication( argc, argv ) {
    out &lt;&lt; &quot;Application started&quot; &lt;&lt; endl;
}

void AddTodo::collectionsFetched( KJob * job ) {
}

void AddTodo::todoCreated( KJob * job ) {
}

int main( int argc, char ** argv ) {
    AddTodo app( argc, argv ); // Создаем экземпляр приложения

    return app.exec(); // И входим в цикл обработки сигналов
}
</code></pre>

<p>Теперь можно проверить, что наше пока еще ничего не делающее приложение корректно собирается:</p>
<pre><code class="bash">
mkdir build
cd build
cmake ..
make
</code></pre>

<p>Если мы запустим приложение, то оно напишет "Application started" и уйдет в бесконечный цикл ожидания сигналов. Пускай, теперь будем добавлять полезную работу.</p>

<h2>Получения списка коллекций через Akonadi</h2>

<p>Для того, чтобы создать задачу через Akonadi, необходимо сначала получить ссылку на <b>коллекцию</b> (<a rel="nofollow" href="http://api.kde.org/4.x-api/kdepimlibs-apidocs/akonadi/html/classAkonadi_1_1Collection.html">Akonadi::Collection</a>), в которой мы будем ее создавать. Для этого мы получим все коллекции и выберем ту, которая поддерживает подходящий тип элементов. Получение коллекции в Akonadi осуществляется путем создания задачи <a rel="nofollow" href="http://api.kde.org/4.x-api/kdepimlibs-apidocs/akonadi/html/classAkonadi_1_1CollectionFetchJob.html">Akonadi::CollectionFetchJob</a>.</p>

<p>В начало add_todo.cpp добавим инклуды, импорт пространства имен Akonadi и объявим одну константу, в которой будет записан MIME-тип для задачи. Он необходим нам для того, чтобы выбрать подходящую задачу.</p>
<pre><code class="cpp">
#include &lt;QStringList&gt;

#include &lt;Akonadi/Collection&gt;
#include &lt;Akonadi/CollectionFetchJob&gt;

using namespace Akonadi;

static QString todoMimeType( &quot;text/calendar&quot; ); // MIME-тип задачи
</code></pre>

<p>В конструктор приложения добавляем:</p>
<pre><code class="cpp">
    CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::FirstLevel, this ); // Создаем задачу

    connect( job, SIGNAL(result(KJob*)), this, SLOT(collectionsFetched(KJob*)) ); // Связываем сигнал и слот
</code></pre>

<p>В первой строке аргументы означают то, что мы получаем подколлекции корневой коллекции, причем только первого уровня.</p>

<p>В метод <b>collectionsFetched</b> добавляем код обработки и выбора нужной нам коллекции:</p>
<pre><code class="cpp">
    out &lt;&lt; &quot;Collections fetched&quot; &lt;&lt; endl;

    if ( job-&gt;error() ) {
        out &lt;&lt; &quot;Error occurred: &quot; &lt;&lt; job-&gt;errorText() &lt;&lt; endl;
        exit( -1 );
        return;
    }

    const CollectionFetchJob * fetchJob = qobject_cast&lt;CollectionFetchJob*&gt;( job ); // Приводим задачу к нужному типу

    const Collection * selectedCollection = 0; // Переменная для выбранной коллекции

    foreach( const Collection &amp; collection, fetchJob-&gt;collections() ) {
        out &lt;&lt; &quot;Name: &quot; &lt;&lt; collection.name(); // Печатаем имя коллекции для отладки

        if ( collection.contentMimeTypes().contains( todoMimeType ) ) { // Проверяем, принимает ли коллекция нужный тип данных
            selectedCollection = &amp;collection;
            break;
        }
    }

    if ( !selectedCollection ) { // Если не нашли подходящей коллекции, то печатаем ошибку и выходим
        out &lt;&lt; &quot;Error occurred: no valid collection found&quot;&lt;&lt; endl;
        exit( -1 );
        return;
    }

    // А здесь будем создавать задачу
</code></pre>

<h2>Создание задачи</h2>

<p>Теперь у нас есть коллекция, в которую можно наконец добавить задачу. Для этого, необходимо сделать три вещи:</p>

<ul>
<li>Создать объект <a rel="nofollow" href="http://api.kde.org/4.x-api/kdepimlibs-apidocs/kcal/html/classKCal_1_1Todo.html">KCal::Todo</a>, описывающий нашу задачу</li>
<li>Создать объект <a rel="nofollow" href="http://api.kde.org/4.x-api/kdepimlibs-apidocs/akonadi/html/classAkonadi_1_1Item.html">Akonadi::Item</a>, представляющий элемент данных в Akonadi</li>
<li>Создать задачу создания нового элемента</li>
<li>Обработать ее результат</li>
</ul>

<p>Итак, приступим. Сначала подключим заголовочные файлы:</p>
<pre><code class="cpp">
#include &lt;Akonadi/Item&gt;
#include &lt;Akonadi/ItemCreateJob&gt;

#include &lt;kcal/todo.h&gt;

#include &lt;boost/shared_ptr.hpp&gt;
</code></pre>

<p>Теперь напишем код для первых трех пунктов в конце метода <b>collectionsFetched</b>:</p>
<pre><code class="cpp">
    KDateTime dueDate = KDateTime::fromString( arguments()[2], &quot;%d.%m.%Y&quot; ); // Парсим дату

    if ( !dueDate.isValid() ) { // Проверяем, что дата распарсилась
        out &lt;&lt; &quot;Error occured: invalid date '&quot; &lt;&lt; arguments()[2] &lt;&lt; &quot;'&quot; &lt;&lt; endl;
        exit( -2 );
    }

    KCal::Todo::Ptr todo( new KCal::Todo() );
    todo-&gt;setSummary( arguments()[1] ); // Текст
    todo-&gt;setDtDue( dueDate ); // Дата
    todo-&gt;setPercentComplete( 0 ); // Пока не выполнена
    todo-&gt;setHasStartDate( false ); // Начальная дата не установлена
    todo-&gt;setHasDueDate( true ); // Установлена дата выполнения

    Item item( todoMimeType );
    item.setPayload&lt;KCal::Todo::Ptr&gt;( todo );

    ItemCreateJob * itemCreateJob = new ItemCreateJob( item, *selectedCollection, this ); // Создаем задачу

    connect( itemCreateJob, SIGNAL(result(KJob*)), this, SLOT(todoCreated(KJob*)) ); // Связываем сигнал и слот
</code></pre>

<p>А в метод <b>todoCreated</b> добавим проверку:</p>
<pre><code class="cpp">
    if ( job-&gt;error() ) {
        out &lt;&lt; &quot;Error occurred: &quot; &lt;&lt; job-&gt;errorText() &lt;&lt; endl;
        exit( -1 );
        return;
    }

    out &lt;&lt; &quot;TODO created&quot; &lt;&lt; endl;
    quit();
</code></pre>

<p>Также, неплохо добавить в начало <b>main</b> проверку количества аргументов:</p>
<pre><code class="cpp">
    if ( argc &lt; 3 ) { // Проверяем количество аргументов
        out &lt;&lt; &quot;Usage: add-todo [text] [date]&quot; &lt;&lt; endl;
        return -2;
    }
</code></pre>

<p>Все, теперь программа завершена, можно скомпилировать ее и запустить следующим образом:</p>

<pre><code class="bash">
./add-todo &quot;Something&quot; 21.01.2010
</code></pre>

<p>После выполнения у вас в календаре должна появиться новая задача. Теперь можно что-то улучшать, например, добавить возможность распознавания ссылок на даты вида "today", "tomorrow", поддержку времени, категорий и много чего интересного...</p>

<p>Полный код <a href="http://gist.github.com/282046">можно посмотреть по ссылке</a>. </p>

<p>Код примеров построен на основе плагина для Plasma Runner, который можно найти <a href="http://www.kde-apps.org/content/show.php?action=content&content=118854">здесь</a> и <a href="http://github.com/alno/plasma-runner-events">здесь</a>.</p>


		

		<div id="links">
			<h2>Ссылки</h2>
			<ul>
	<li><a rel="nofollow" href="http://api.kde.org/4.x-api/kdepimlibs-apidocs/akonadi/html/index.html">Akonadi API (En)</a></li>
	<li><a rel="nofollow" href="http://techbase.kde.org/Development/Tutorials/Akonadi/Application">Tutorial: Akonadi Application (En)</a></li>
	<li><a rel="nofollow" href="http://techbase.kde.org/Development/Tutorials/Plasma/AbstractRunner">Tutorial: Runner (En)</a></li>
</ul>

		</div>
	]]></content:encoded>
</item>
<item>
	<title>Организация взаимодействия Java-приложений с помощью JGroups</title>
	<link>http://blog.alno.name/ru/2009/11/jgroups</link>
	<comments>http://blog.alno.name/ru/2009/11/jgroups#comments</comments>
	<pubDate>Sun, 08 Nov 2009 19:31:18 GMT</pubDate>
	<dc:creator>Alno</dc:creator>
	<guid isPermaLink="true">http://blog.alno.name/ru/2009/11/jgroups</guid>
	<description><![CDATA[<p>Сегодня я хочу рассказать о <a href="http://jgroups.org/">JGroups</a>. Это Java-библиотека для организации группового взаимодействия между различными процессами Java. Приложения, использующие JGroups могут:
<ul>
  <li>Создавать и уничтожать группы</li>
  <li>Присоединяться к группам и покидать их</li>
  <li>Получать оповещения о новых членах групп</li>
  <li>Отправлять сообщения конкретному процессу или всем процессам группы</li>
</ul>
Библиотека достаточно широко используется, в частности в сервере приложений JBoss, в кэше OSCache и в Grid-платформе Infinispan.
</p>

<p>Здесь я ограничусь начальной информацией и опишу создание простого группового чата на Java.</p>
]]></description>
	<content:encoded><![CDATA[
		<p>Сегодня я хочу рассказать о <a href="http://jgroups.org/">JGroups</a>. Это Java-библиотека для организации группового взаимодействия между различными процессами Java. Приложения, использующие JGroups могут:
<ul>
  <li>Создавать и уничтожать группы</li>
  <li>Присоединяться к группам и покидать их</li>
  <li>Получать оповещения о новых членах групп</li>
  <li>Отправлять сообщения конкретному процессу или всем процессам группы</li>
</ul>
Библиотека достаточно широко используется, в частности в сервере приложений JBoss, в кэше OSCache и в Grid-платформе Infinispan.
</p>

<p>Здесь я ограничусь начальной информацией и опишу создание простого группового чата на Java.</p>
<h2>Начало работы</h2>

<p>Итак, для начала надо скачать JGroups. Сделать это можно здесь: <a href="http://sourceforge.net/projects/javagroups/files/">http://sourceforge.net/projects/javagroups/files/</a>. При написании этой статьи я использовал версию <b>2.6.13.GA</b>. Также, для работы требуется Apache Commons Logging, или что-то его заменяющее (например, jcl-over-slf4j). Скачать можно здесь: <a href="http://commons.apache.org/downloads/download_logging.cgi">http://commons.apache.org/downloads/download_logging.cgi</a>.</p>

<p>Если же Вы используете Maven, то можно добавить репозиторий JBoss:

<pre><code class="xml">
  &lt;repository&gt;
    &lt;id&gt;jboss&lt;/id&gt;
    &lt;name&gt;jboss&lt;/name&gt;
    &lt;url&gt;http://repository.jboss.com/maven2&lt;/url&gt;
    &lt;snapshots&gt;
      &lt;enabled&gt;false&lt;/enabled&gt;
    &lt;/snapshots&gt;
  &lt;/repository&gt;
</code></pre>

и добавить в зависимости:

<pre><code class="xml">
  &lt;dependency&gt;
    &lt;groupId&gt;jgroups&lt;/groupId&gt;
    &lt;artifactId&gt;jgroups&lt;/artifactId&gt;
    &lt;version&gt;2.6.13.GA&lt;/version&gt;
  &lt;/dependency&gt;

  &lt;dependency&gt;
    &lt;groupId&gt;commons-logging&lt;/groupId&gt;
    &lt;artifactId&gt;commons-logging&lt;/artifactId&gt;
    &lt;version&gt;1.1.1&lt;/version&gt;
  &lt;/dependency&gt;
</code></pre>
</p>

<p>
Возможно, необходимо настроить сетевой интерфейс для работы с мультикастом. В Linux, для этого необходимо добавить соответствующий роут:

<pre><code class="bash">route add -net 224.0.0.0 netmask 240.0.0.0 dev lo</code></pre>
</p>

<h2>Иницализация</h2>

<p>
Для того,чтобы создать канал необходимо создать объект класса <tt><a href="http://jgroups.org/javadoc/org/jgroups/JChannel.html">JChannel</a></tt>, передав ему в конструктор параметры конфигурации. В примере в конфигурации я указываю адрес, который должен использоваться для канала. Полный список опций можно посмотреть <a href="http://www.jgroups.org/manual/html/protlist.html">в документации</a>.

<pre><code class="java">JChannel channel = new JChannel( &quot;UDP(bind_addr=127.0.0.1)&quot; );</code></pre>

Теперь можно подключаться к кластеру вызовом <tt>JChannel#connect()</tt>. В качестве параметра в него передается имя группы, можете выбрать любое, которое Вам нравится.

<pre><code class="java">channel.connect( &quot;MyCluster&quot; );</code></pre>
</p>

<h2>Отправка сообщений</h2>

<p>
Сообщения в JGroups представлены классом <tt><a href="http://jgroups.org/javadoc/org/jgroups/Message.html">Message</a></tt>, который содержит адрес отправителя, адрес получателя и данные. Адреса представлены объектами класса <tt><a href="http://jgroups.org/javadoc/org/jgroups/Address.html">Address</a></tt>, а данные - это любые сериализуемые объекты или просто массив байт. Например, чтобы создать сообщение для всей группой, содержащее строку можно написать:

<pre><code class="java">new Message( null, null, &quot;Some content&quot; )</code></pre>

Здесь первый параметр - получатель, затем - отправитель, а затем - содержимое.
</p>

<p>
Чтобы отправить сообщение необходимо вызвать метод <b>JChannel#send</b> и передать ему сообщение в качестве параметра. Например:

<pre><code class="java">channel.send( new Message( null, null, &quot;Some content&quot; ) )</code></pre>
</p>

<h2>Обработка сообщений</h2>

<p>Теперь необходимо написать код, для обработки сообщений, приходящих приложению. Для этого необходимо вызвать метод <tt>JChannel#setReceiver</tt>, передав в качестве параметра объект, реализующий интерфейс <tt><a href="http://jgroups.org/javadoc/org/jgroups/Receiver.html">Receiver</a></tt>. Например, чтобы просто выводить все полученные сообщения на печать достаточно написать следующий код:

<pre><code class="java">
channel.setReceiver( new ReceiverAdapter(){

	@Override
	public void receive( Message m ) {
		System.out.println( m.getObject() );
	}

} );
</code></pre>

Здесь, чтобы уменьшить объем кода, объект наследуется от класса <tt>ReceiverAdapter</tt>, который предоставляет пустые реализации различных методов интерфейса <tt>Receiver</tt>. Как видно из примера, для обработки сообщения используется метод <tt>receive</tt>, в который сообщение передается в качестве параметра.
</p>

<h2>Простейший чат</h2>

<p>Теперь можно собрать простейший чат. Он будет рассылать группе строки, принятые с консоли и печатать полученные строки на консоль. Пример код:</p>

<pre><code class="java">
import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;

public class SimplestChat {

	public static void main( String[] args ) throws Exception {
		JChannel channel = new JChannel( &quot;UDP(bind_addr=127.0.0.1)&quot; );
		channel.setReceiver( new ReceiverAdapter() {

			@Override
			public void receive( Message m ) {
				System.out.println( m.getObject() );
			}

		} );
		channel.connect( &quot;MyCluster&quot; ); // Подключаемся к группе

		/**
		 * Цикл обработки команд с консоли
		 */
		BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) );
		while ( true ) {

			String line = in.readLine();

			if ( line.equalsIgnoreCase( &quot;quit&quot; ) || line.equalsIgnoreCase( &quot;exit&quot; ) ) {
				break;
			}

			channel.send( new Message( null, null, line ) );
		}

		channel.close(); // Отключаемся от группы по завершению работы
	}

}
</code></pre>

<p>При запуске этот код должен выводить что-нибудь вроде:</p>

<pre>
  Nov 8, 2009 4:18:27 PM org.jgroups.JChannel init
  INFO: JGroups version: 2.6.13.GA
</pre>

<h2>Что дальше?</h2>

<p>
Здесь я рассмотрел только создание канала и отправку сообщений группе. В стороне остались достаточно много моментов: адресация, сообщения конкретному процессу и, главное, управление группой. Желающие ознакомиться с ними могут заглянуть в документацию JGroups: <a href="http://jgroups.org/ug.html">http://jgroups.org/ug.html</a>.
</p>


		

		<div id="links">
			<h2>Ссылки</h2>
			<ul>
	<li><a href="http://jgroups.org/" title="En">Сайт проекта</a></li>
	<li><a href="http://jgroups.org/ug.html" title="En">Руководство пользователя</a></li>
</ul>
		</div>
	]]></content:encoded>
</item>
<item>
	<title>Irwi - Wiki в Rails-приложениях</title>
	<link>http://blog.alno.name/ru/2009/08/irwi-plugin</link>
	<comments>http://blog.alno.name/ru/2009/08/irwi-plugin#comments</comments>
	<pubDate>Fri, 28 Aug 2009 08:43:27 GMT</pubDate>
	<dc:creator>Alno</dc:creator>
	<guid isPermaLink="true">http://blog.alno.name/ru/2009/08/irwi-plugin</guid>
	<description><![CDATA[<p>
Сегодня я хочу представить свой Rails-плагин, позволяющий добавить в приложение функциональность вики (не просто расширение моделей, а полноценную вики которая бы <b>сразу работала</b>)
</p>

<p>
Я обнаружил то, что для такой, казалось бы, стандартной задачи в Rails нет полноценного готового решения, которое бы легко интегрировалось с существующим приложением. В связи с этим был написан свой.
</p>

<p>
Основными критериями при разработке были:
</p>
<ul>
  <li>Возможность быстрой интеграции в приложение.</li>
  <li>Хорошая расширяемость.</li>
  <li>Отсутствие чужого кода (в смысле кода плагина) в приложении, к чему часто приводит использование генераторов. В этом смысле я пытался равняться на Authlogic.</li>
</ul>

<p>
То, что получилось представляет из себя что-то среднее между генератором (что обеспечивает хорошую расширяемость и модифицируемость) и engine'ом (чтобы можно было легко обновлять его).
</p>
]]></description>
	<content:encoded><![CDATA[
		<p>
Сегодня я хочу представить свой Rails-плагин, позволяющий добавить в приложение функциональность вики (не просто расширение моделей, а полноценную вики которая бы <b>сразу работала</b>)
</p>

<p>
Я обнаружил то, что для такой, казалось бы, стандартной задачи в Rails нет полноценного готового решения, которое бы легко интегрировалось с существующим приложением. В связи с этим был написан свой.
</p>

<p>
Основными критериями при разработке были:
</p>
<ul>
  <li>Возможность быстрой интеграции в приложение.</li>
  <li>Хорошая расширяемость.</li>
  <li>Отсутствие чужого кода (в смысле кода плагина) в приложении, к чему часто приводит использование генераторов. В этом смысле я пытался равняться на Authlogic.</li>
</ul>

<p>
То, что получилось представляет из себя что-то среднее между генератором (что обеспечивает хорошую расширяемость и модифицируемость) и engine'ом (чтобы можно было легко обновлять его).
</p>
<h2>Установка и использование</h2>

<p>
Для работы плагина (в частности истории страниц) необходимо наличие гема <tt>diff-lcs</tt>. Для форматирования по умолчанию используется <tt>RedCloth</tt>, однако можно выбрать и другой форматтер. Таким образом:
</p>

<pre><code class="bash">
gem install diff-lcs RedCloth
</code></pre>

<p>
Для установки плагина достаточно выполнить в директории Rails-приложения:
</p>

<pre><code class="bash">
script/plugin install git://github.com/alno/irwi
</code></pre>

<p>
После установки необходимо сгенерировать миграцию, моделиь и контроллер... <b>Стойте, это вовсе не значит, что у вас в приложении появится куча чужого кода, который непонятно как поддерживать!</b> Контроллер и модель содержат всего по одной строке вида <tt>acts_as_*</tt> и генерируется для того, чтобы в последствии Вам было бы проще расширять функциональность. Вся собственная функциональность вики реализуется в файлах плагина, что позволяет безболезненно обновлять ее.
</p>

<p>
Итак, чтобы сгенерировать необходимые файлы необходимо вызвать соответсвующий генератор:
</p>

<pre><code class="bash">
script/generate irwi_wiki
</code></pre>

<p>
После вызова генератора в приложение будут произведены следующие изменения:
</p>

<ul>
  <li>Добавлен WikiPageController и соответствующий хелпер для обработки страниц</li>
  <li>Добавлены модели WikiPage и WikiPageVersion для представления страниц</li>
  <li>Будет сгенерирована миграция, создающая таблицы в БД</li>
</ul>

<p>
Также в роуты будет добавлена следующая строка:
</p>

<pre><code class="ruby">
  map.wiki_root '/wiki'
</code></pre>

<p>
Как несложно догадаться, в ней указывается корень wiki в вашем приложении. Вы можете легко его изменить на тот, который Вам больше нравится.
</p>

<p>
Все, работающая вики в вашем приложении сгенерирована. В принципе, можно уже использовать ее, но наверное вам все-таки хотелось бы изменить некоторые ее аспекты под свое приложение, например, изменить шаблоны, используемые для отображения или привязать ее к своей системе пользователей и ограничить права на редактирование страниц. Об этом и пойдет речь далее.
</p>

<h2>Изменение внешнего вида</h2>

<p>
Изменение внешнего вида можно производить двумя путями:
</p>

<ul>
  <li>Замена шаблонов</li>
  <li>Замена стилей в дефолтных шаблонах</li>
</ul>

<p>
Для замены какого-то шаблона (включая partial'ы) необходимо определить шаблон с соответствующим именем в директории видов для вашего контроллера. Ничего необычного, правда? Если вы не знаете, что же туда писать, можете посмотреть на дефолтные шаблоны в исходниках плагина (они там в app/views), благо они крайне простые.
</p>

<p>
Скорее всего, вы будете применять первый способ, но о первом все же стоит упомянуть. По умолчанию в каждый дефолтный шаблон добавляется CSS с описанием дефолтного стиля. Наверное, вам захочется его выкинуть (и подключить свой собственный в layout'е). Для этого достаточно переопределить в хелпере метод <tt>wiki_page_style</tt> своим, который возвращает пустую строку. Таким образом вы просто уберете стили из страниц.
</p>

<h2>Привязка к пользователям</h2>

<p>
Что необходимо для того, чтобы привязать вики к существующей системе пользователей в приложении?
</p>

<p>
Самый простой случай, если модель вашего пользователя называется User и у вас в контроллере определен метод current_user, который возвращает текущего пользователя. Согласитесь, это достаточно распространенный случай. В этом случае вики автоматически привяжется к пользователям и считать текущего пользователя автором изменений.
</p>

<p>
Единственная проблема - на всех страницах имя пользователя будет отображаться как-нибудь вроде <b>User123</b>. Наверное, это все-таки не то, что вы хотите. Чтобы исправить эту ситуацию достаточно определить метод <tt>wiki_user</tt> в классе WikiPagesHelper. Например, следующим образом:
</p>

<pre><code class="ruby">
module WikiPagesHelper
  include Irwi::Helpers::WikiPagesHelper

  def wiki_user( user )
    user ? link_to( user.login, user_path( user ) ) : &quot;Guest&quot;
  end

end
</code></pre>

<p>
Если же ваша модель называется как-то по-другому, придется добавить еще одну строчку при инициализации приложения:
</p>

<pre><code class="ruby">
Irwi.config.user_class_name = 'Account' # Разумеется, если ваша модель называется именно так
</code></pre>

<p>
Я рекомендую для этого создать отдельный initializer, чтобы потом туда же добавлять и прочие опции, которые вы, быть может захотите установить, но в принципе, вы вольны поступать как вам хочется.
</p>

<h2>Ограничение прав на выполнение операций</h2>

<p>
Скорее всего, Вам захочется добавить ограничение прав на просмотр или редактирование страниц вики (вообще говоря, я считаю, что это весьма неплохая идея). Например, как минимум, дать права на редактирование только зарегистрированным пользователям.
</p>

<p>
Для этого необходимо всего-навсего переопределить в контроллере три метода, осуществляющие проверку прав: <tt>show_allowed?</tt>, <tt>history_allowed?</tt> и <tt>edit_allowed?</tt>. В каждом из методов необходимо проверить имеет ли текущий пользователь права на совершение действия (просмотр, просмотр истории, редактирование) с текущей страницей (<tt>@page</tt>) и соответственно вернуть что-то, что расценивается как исттина или как ложь. В случае, если соответствующий метод возвращает ложь, то действие не совершается и в контроллере вызывается метод <tt>not_allowed</tt>, который по умолчанию рендерит текст об ошибке, но скорее всего вам захочется переопределить и его (например, чтобы редиректить пользователя на страницу логина).
</p>

<p>
Таким образом, примерный код контроллера может выглядеть следующим образом:
</p>

<pre><code class="ruby">
class WikiPagesController &lt; ApplicationController

  acts_as_wiki_pages_controller

  def show_allowed?
    true # Показывать всем
  end

  def history_allowed?
    true # И историю пусть все смотрят
  end

  def edit_allowed?
    current_user # А редактируют только те, кто залогинены
  end

  def not_allowed
    redirect_to login_path # Всех нарушителей редиректим на страницу логина
  end

end
</code></pre>

<h2>Итого</h2>

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


		<div id="related">
			<h2>Записи на схожие темы</h2>
			<ul>
	<li><a href="http://blog.alno.name/2009/07/rails-testing-tools/">Средства тестирования в Ruby on Rails</a></li>
	<li><a href="http://blog.alno.name/2008/05/periodic-tasks-in-ruby-on-rails/">Периодические задачи в Ruby on Rails</a></li>
	<li><a href="http://blog.alno.name/2008/04/errors-using-rails-with-fastcgi/">Ошибки при использовании Rails и FastCGI</a></li>
	<li><a href="http://blog.alno.name/2009/03/radiant-extensions-development/">Разработка расширений для Radiant</a></li>
	<li><a href="http://blog.alno.name/2009/05/typus/">Typus &#8211; админка в Rails-приложениях</a></li>
</ul>
		</div>

		
	]]></content:encoded>
</item>
<item>
	<title>Средства тестирования в Ruby on Rails</title>
	<link>http://blog.alno.name/ru/2009/07/rails-testing-tools</link>
	<comments>http://blog.alno.name/ru/2009/07/rails-testing-tools#comments</comments>
	<pubDate>Thu, 23 Jul 2009 10:06:48 GMT</pubDate>
	<dc:creator>Alno</dc:creator>
	<guid isPermaLink="true">http://blog.alno.name/ru/2009/07/rails-testing-tools</guid>
	<description><![CDATA[<p>Как известно, тестирование является одной из важнейших составляющих разработки приложений на основе Ruby on Rails. Поддержка тестирования была включена в Rails с самого начала, для этого использовалась библиотека Test::Unit. Однако, со временем, появилось много альтернативных средств для тестирования в Rails-приложениях.</p>
<p>В этой заметке я проведу небольшой обзор средств, используемых в настоящий момент для тестирования приложений на базе Ruby on Rails. Я не ставлю себе целью выбрать &#8220;лучшее&#8221; &#8211; я считаю, что этот выбор должен делать каждый сам, однако я попытаюсь показать различные варианты &#8211; чтобы было из чего выбирать.</p>]]></description>
	<content:encoded><![CDATA[
		<p>Как известно, тестирование является одной из важнейших составляющих разработки приложений на основе Ruby on Rails. Поддержка тестирования была включена в Rails с самого начала, для этого использовалась библиотека Test::Unit. Однако, со временем, появилось много альтернативных средств для тестирования в Rails-приложениях.</p>
<p>В этой заметке я проведу небольшой обзор средств, используемых в настоящий момент для тестирования приложений на базе Ruby on Rails. Я не ставлю себе целью выбрать &#8220;лучшее&#8221; &#8211; я считаю, что этот выбор должен делать каждый сам, однако я попытаюсь показать различные варианты &#8211; чтобы было из чего выбирать.</p><h2>Модульные и функциональные тесты</h2>

<p>Здесь я рассмотрю основные средства, используемые для модульного и функционального тестирования в Rails-приложениях. Напомню, что модульные тесты используются для тестирования логики приложения, которая расположена в моделях, а также для тестирования различных независимых участков приложения, например, хелперов (helpers).</p>

<h3>Test::Unit</h3>

<p>
<b>Test::Unit</b> является классическим средством тестирования в стиле <a href="http://en.wikipedia.org/wiki/XUnit">xUnit</a>. В Rails-приложениях именно он и используется по умолчанию. Набор тестов с его использованием описывается в виде класса, методы которого представляют различные тесты. В коде методов в необходимых точках добавляются проверки, представленные вызовами <tt>assert_*</tt>.
</p>

<p>Ниже приведен пример простейшего теста с использованием <b>Test::Unit</b>:</p>

<pre><code class="ruby">require 'test/unit'

class MyTest &lt; Test::Unit::TestCase
  # def setup
  # end

  # def teardown
  # end

  def test_fail
    assert(2 + 2 == 4, 'Assertion was false.')
  end
end
</code></pre>

<p>RDoc-документация для Test::Unit: <a href="http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html">http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html</a></p>

<h3>RSpec</h3>

<p><b>RSpec</b> - средство, предназначенное для спецификации поведения кода. Я не хочу вдаваться в отличия спецификации от тестирования (и BDD от TDD), просто приведу пример спецификации модели с использованием <b>RSpec</b>:</p>

<pre><code class="ruby">describe &quot;A new account&quot; do

  before do
    @account = Account.new
  end

  it &quot;should have a balance of $0&quot; do
    @account.balance.should eql(Money.new(0, :dollars))
  end

end
</code></pre>

<p>Как видно из примера, RSpec представляет специальный язык в рамках Ruby для описания спецификаций. Следствием этого является то, что хорошо написанные спецификации легко читаются, практически как текст на английском языке.</p>

<p>Одним из минусов RSpec является то, что для проверки спецификаций требуются создавать специальные Rake-задачи в приложении, в отличии от тестов на основе Test::Unit, задачи для которого включены в Rails.</p>

<p>Сайт проекта: <a href="http://rspec.info">http://rspec.info</a></p>
<p>RDoc-документация для RSpec: <a href="http://rspec.rubyforge.org/rspec/1.2.8/">http://rspec.rubyforge.org/rspec/1.2.8/</a></p>

<h3>Test/Spec</h3>

<p><b>Test/Spec</b> предназначен для описания спецификаций, аналогичных RSpec на базе Test::Unit, что позволяет использовать те же самые стандартные задачи, что и для Test::Unit, а также совмещать тесты и спецификации.</p>

<pre><code class="ruby">require 'test/spec'

describe &quot;Foo&quot; do
  it &quot;should bar&quot; do
    (2 + 3).should.equal 5
  end
end
</code></pre>

<p>Сайт проекта Test/Spec и RDoc-документация: <a href="http://test-spec.rubyforge.org/">http://test-spec.rubyforge.org/</a></p>

<h3>Shoulda</h3>

<p>Shoulda  - это средство, которое работает на базе Test::Unit и предоставляет некоторые дополнительные возможности, в частности специальный язык для описания конструкций в тестах и набор макросов для часто используемых проверок:</p>

<pre><code class="ruby">class UserTest &lt; Test::Unit::TestCase

  should_have_many :posts

  should_not_allow_values_for :email, &quot;blah&quot;, &quot;b lah&quot;
  should_allow_values_for :email, &quot;a@b.com&quot;, &quot;asdf@asdf.com&quot;
  should_ensure_length_in_range :email, 1..100
  should_ensure_value_in_range :age, 1..100
  should_protect_attributes :password

  context &quot;A User instance&quot; do
    setup do
      @user = User.find(:first)
    end

    should &quot;return its full name&quot; do
      assert_equal 'John Doe', @user.full_name
    end

    context &quot;with a profile&quot; do
      setup do
        @user.profile = Profile.find(:first)
      end

      should &quot;return true when sent #has_profile?&quot; do
        assert @user.has_profile?
      end
    end
  end
end
</code></pre>

<p><b>Update:</b> В настоящий момент Shoulda может быть использована и вместе с RSpec</p>

<p>Сайт проекта: <a href="http://www.thoughtbot.com/projects/shoulda/">http://www.thoughtbot.com/projects/shoulda/</a></p>

<h3>Remarkable</h3>

<p>Shoulda показала удобство использования макросов для распространенных задач в тестировании, однако она работала только над Test::Unit. <b>Remarkable</b> - это реализация набора макросов, аналогичных Shoulda для <b>RSpec</b>. Его использование позволяет описывать следующие спецификации:</p>

<pre><code class="ruby">describe Post do
  it { should belong_to(:user) }
  it { should have_many(:comments) }
  it { should have_and_belong_to_many(:tags) }

  it { should validate_presence_of(:body) }
  it { should validate_presence_of(:title) }
  it { should validate_uniqueness_of(:title, :allow_blank =&gt; true) }
end
</code></pre>

<p>Сайт проекта Remarkable и RDoc-документация: <a href="http://remarkable.rubyforge.org/">http://remarkable.rubyforge.org/</a></p>
<p>Блог проекта: <a href="http://www.nomedojogo.com/category/remarkable/">http://www.nomedojogo.com/category/remarkable/</a></p>
<p>Исходный код Remarkable: <a href="http://github.com/carlosbrando/remarkable/tree/master">http://github.com/carlosbrando/remarkable/tree/master</a></p>

<h2>Интеграционные тесты - Cucumber</h2>

<p>Cucumber, используемый для интеграционного тестирования приложений продолжает движение в сторону читабельности тестов - тестовые сценарии описываются буквально на естественном языке:</p>

<pre><code class="">  Scenario: Add two numbers
    Given I have entered 13 into the calculator
    And I have entered 22 into the calculator
    When I press &quot;+&quot;
    Then the result should be 35 on the screen
</code></pre>

<p>И не только на английском:</p>

<pre><code class="">  Сценарий: Сложение двух целых чисел
    Допустим я ввожу число 50
    И затем ввожу число 70
    Если я нажимаю &quot;+&quot;
    То результатом должно быть число 120
</code></pre>

<p>Написание сценариев в таком виде делает Cucumber просто незаменимым инструментом для тестирования Rails-приложений. Разумеется, некоторое количество кода писать все-таки приходится, для того чтобы описать что означают те или иные приложения.</p>

<h2>Заключение</h2>

<p>Здесь я описал некоторые средства, используемые для тестирования Rails-приложений. Я сознательно не затрагивал здесь различные библиотеки для создания mock-объектов или замены fixtures, возможно, в дальнейшем им можно посвятить отдельную заметку.</p>


		<div id="related">
			<h2>Записи на схожие темы</h2>
			<ul>
	<li><a href="http://blog.alno.name/2008/05/periodic-tasks-in-ruby-on-rails/">Периодические задачи в Ruby on Rails</a></li>
	<li><a href="http://blog.alno.name/2008/04/errors-using-rails-with-fastcgi/">Ошибки при использовании Rails и FastCGI</a></li>
	<li><a href="http://blog.alno.name/2009/03/radiant-extensions-development/">Разработка расширений для Radiant</a></li>
	<li><a href="http://blog.alno.name/2009/08/irwi-plugin/">Irwi &#8211; Wiki в Rails-приложениях</a></li>
</ul>
		</div>

		<div id="links">
			<h2>Ссылки</h2>
			<ul>
	<li><a href="http://guides.rubyonrails.org/testing.html" title="En">Тестирование в Rails</a></li>
	<li><a href="http://giantrobots.thoughtbot.com/2007/4/6/shoulda-coulda-woulda" title="En">Введение в Shoulda</a></li>
	<li><a href="http://habrahabr.ru/blogs/testing/52929/" title="Ru">Экстремальное программирование, знакомство с Behavior Driven Development и RSpec</a></li>
	<li><a href="http://habrahabr.ru/blogs/ruby/62958/" title="Ru"><span class="caps">BDD</span> с помощью Cucumber</a></li>
</ul>
		</div>
	]]></content:encoded>
</item>
<item>
	<title>Навигация по сайту в пользовательском поиске Google</title>
	<link>http://blog.alno.name/ru/2009/07/google-cse-navigation</link>
	<comments>http://blog.alno.name/ru/2009/07/google-cse-navigation#comments</comments>
	<pubDate>Wed, 08 Jul 2009 21:29:27 GMT</pubDate>
	<dc:creator>Alno</dc:creator>
	<guid isPermaLink="true">http://blog.alno.name/ru/2009/07/google-cse-navigation</guid>
	<description><![CDATA[<p>Некоторое время назад я прикрутил к своему блогу поиск Google на основе <a href="http://www.google.com/cse/" title="Custom Search Engine">пользовательского поиска</a>. После некоторого осмотра возможностей пользовательского поиска я обнаружил &#8220;уточнения&#8221;, позволящие создавать дополнительные ссылки в интерфейсе поиска для уточнения результатов поиска.</p>
<p>Поскольку я пишу и о <a href="http://blog.alno.name/tags/cpp">C++</a>, и о <a href="http://blog.alno.name/tags/java">Java</a>, и о <a href="http://blog.alno.name/tags/ruby">Ruby</a>, естественным было бы добавить туда уточнения вида &#8220;C++&#8221;, &#8220;Java&#8221; и &#8220;Ruby&#8221;, позволяющие включать в результаты поиска только страницы с заданными тэгами. Как это выглядит можно посмотреть на картинке внизу, или по после нажатия кнопки &#8220;Найти&#8221; в блоге справа вверху.</p>
<p>Здесь я опишу, как такое можно осуществить.</p>]]></description>
	<content:encoded><![CDATA[
		<p>Некоторое время назад я прикрутил к своему блогу поиск Google на основе <a href="http://www.google.com/cse/" title="Custom Search Engine">пользовательского поиска</a>. После некоторого осмотра возможностей пользовательского поиска я обнаружил &#8220;уточнения&#8221;, позволящие создавать дополнительные ссылки в интерфейсе поиска для уточнения результатов поиска.</p>
<p>Поскольку я пишу и о <a href="http://blog.alno.name/tags/cpp">C++</a>, и о <a href="http://blog.alno.name/tags/java">Java</a>, и о <a href="http://blog.alno.name/tags/ruby">Ruby</a>, естественным было бы добавить туда уточнения вида &#8220;C++&#8221;, &#8220;Java&#8221; и &#8220;Ruby&#8221;, позволяющие включать в результаты поиска только страницы с заданными тэгами. Как это выглядит можно посмотреть на картинке внизу, или по после нажатия кнопки &#8220;Найти&#8221; в блоге справа вверху.</p>
<p>Здесь я опишу, как такое можно осуществить.</p><h2>Настройка системы поиска</h2>

<p>Для начала, разумеется, необходимо создать систему пользовательского поиска. Это можно сделать на странице <a href="http://www.google.com/cse/">Custom Search Engine</a>. При создании системы в список сайтов необходимо, разумеется включить свой сайт (или сайты). </p>

<p>Затем, в настройках системы зайти в раздел Уточнения(Refinements). Там можно добавить уточнение, возникает форма вида (например, сначала добавим раздел Ruby):</p>

<p style="text-align: center;"><img src="/images/google-cse/google-cse-ruby.png" /></p>

<p>Основная проблема состоит в том, что написать в строке слов, чтобы выбрать все страницы, содержащие тэг <b>Ruby</b>.</p>

<p>Пришлось поискать описание различных операторов расширенного поиска для Google. Наиболее подробное описание различных операторов поиска я нашел <a href=":http://www.googleguide.com/advanced_operators.html" rel="nofollow">здесь</a>. Однако, для решения задачи мне понадобились всего три:</p>

<ul>
<li><tt>|</tt> - альтернативные варианты (или).</li>
<li><tt>[</tt> и <tt>]</tt> - скобки для группировки элементов в запросах.</li>
<li><tt>*</tt> - оператор, который обозначает одно произвольное слово. Например в запросе <b>"Мама * раму"</b> вместо * может находится ровно одно слово, то есть по этому запросу найдется текст "мама мыла раму", но не "мама мыла большую раму" или "мама раму".</li>
</ul>

<p>Далее, в начале каждой записи после заголовка я вставил код, выводящий список всех тэгов записи, перед которым находилось слово "тэги". После этого пришлось подождать некоторое время, пока Google переиндексирует все страницы сайта =).

<p>После того, как все страницы переиндексированы, можно легко выделить все, у которых первым тэгом является Ruby с помощью запроса <b>"Тэги: Ruby"</b>

<p>Теперь необходимо учесть то, что тэг Ruby стоит не всегда на первом месте. Здесь-то и нужен оператор * - запрос для тэга на втором месте выглядит как <b>"Тэги: * Ruby"</b>. Аналогично для третьего, четвертого и далее.</p>

<p>Чтобы сделать запрос, который выбирает все страницы, содержащие тэг на любом из первых N мест можно задать следующую конструкцию: <b>["Тэги: Ruby"|"Тэги: * Ruby"|"Тэги: * * Ruby"|"Тэги: * * * Ruby"|"Тэги: * * * Ruby"]</b>. Тут N=5. Если Вы ставите больше тэгов, то запрос придется сделать несколько длинее.</b></p>

<p>Полученную строку запроса необходимо записать в "слова, добавляемые в поисковый запрос", а затем нажать "Сохранить". Повторив описанную процедуру для необходимых тэгов на страницу поиска можно получить следующую строку навигации:</p>

<p style="text-align: center;"><img src="/images/google-cse/google-cse-results.png" /></p>

<h2>Форма поиска</h2>

<p>Для поиска Google предлагает AJAX-форму, однако мне больше понравился вариант стандартной формы:</p>

<pre><code class="html">
&lt;form action=&quot;http://www.google.com/cse&quot; id=&quot;cse-search-box&quot;&gt;
&lt;input name=&quot;cx&quot; value=&quot;012005588861949235995:unlj7xfsgyy&quot; type=&quot;hidden&quot;&gt;
&lt;input name=&quot;ie&quot; value=&quot;UTF-8&quot; type=&quot;hidden&quot;&gt;
&lt;input name=&quot;q&quot; size=&quot;30&quot; type=&quot;text&quot;&gt;
&lt;input name=&quot;sa&quot; value=&quot;Найти&quot; type=&quot;submit&quot;&gt;
&lt;/form&gt;
</code></pre>

<p><b>012005588861949235995:unlj7xfsgyy</b> - это идентификатор моей системы поиска. Соответственно, для Вашей системы он будет другой, найти его можно, например, в URL при редактировании системы поиска.</p>


		

		
	]]></content:encoded>
</item>
<item>
	<title>Typus - админка в Rails-приложениях</title>
	<link>http://blog.alno.name/ru/2009/05/typus</link>
	<comments>http://blog.alno.name/ru/2009/05/typus#comments</comments>
	<pubDate>Tue, 26 May 2009 23:35:25 GMT</pubDate>
	<dc:creator>Alno</dc:creator>
	<guid isPermaLink="true">http://blog.alno.name/ru/2009/05/typus</guid>
	<description><![CDATA[<p>Я не люблю писать одинаковый код много раз, тем более тривиальный. Я не люблю писать админки для Rails-приложений, потому что они состоят по большей части как раз из такого кода. И я не люблю генераторы, создающие кучу кода, который необходимо менять только в некоторых местах (поэтому для аутентификации я использую AuthLogic, а не restful_authentication).</p>
<p>И поэтому я был очень рад обнаружить замечательный проект: <a href="http://intraducibles.com/projects/typus">Typus</a>. Это плагин для Rails, позволяющий значительно упростить процесс создания админки для приложений.</p>]]></description>
	<content:encoded><![CDATA[
		<p>Я не люблю писать одинаковый код много раз, тем более тривиальный. Я не люблю писать админки для Rails-приложений, потому что они состоят по большей части как раз из такого кода. И я не люблю генераторы, создающие кучу кода, который необходимо менять только в некоторых местах (поэтому для аутентификации я использую AuthLogic, а не restful_authentication).</p>
<p>И поэтому я был очень рад обнаружить замечательный проект: <a href="http://intraducibles.com/projects/typus">Typus</a>. Это плагин для Rails, позволяющий значительно упростить процесс создания админки для приложений.</p><p>Из его особенностей:</p>
<ul>
	<li>Позволяет практически мгновенно создать простейшую админку для моделей (создание, удаление, редактирование).</li>
	<li>Автоматически поддерживаются связи между моделями.</li>
	<li>Админка является расширяемой.</li>
	<li>Не генерирует огромного объема кода, который потом никогда не расширяется. Только то, что необходимо &#8211; конфиграционный файл и классы констроллеров для расширения. Только классы, без кода системы администрирования в них!</li>
	<li>Поддерживается локализация админки (пнглийский, испанский, португальский и <strong>русский</strong>).</li>
	<li>Поддерживается экспорт данных в различные форматы.</li>
</ul>
<h2>Установка</h2>
<p>Typus может быть установлен двумя способами:</p>
<ol>
	<li>Как обычный плагин Rails, для этого в директории приложения необходимо вызвать:<br />
  <pre><code class="bash">$ script/plugin install git://github.com/fesplugas/typus.git</code></pre></li>
	<li>Через Ruby Gems, для этого в <tt>config/environment.rb</tt> необходимо добавить строку:<br />
  <pre><code class="ruby">config.gem 'typus'</code></pre></li>
</ol>
<p>После установки необходимо в директории приложения вызвать команды:<br />
<pre><code class="bash">$ script/generate typus
$ rake db:migrate
</code></pre></p>
<p>В результате будут сгенерированы файлы конфигурации админки, контроллеры админки для всех существующих моделей и пара миграций, обеспечивающих работу системы пользователей Typus.</p>
<p>Также, для работы админки необходимо убедиться, что в конце файла <tt>config/routes.rb</tt> присутствуют строки:<br />
<pre><code class="ruby">map.connect ':controller.:format'
map.connect ':controller/:action/:id.:format'
</code></pre></p>
<h2>Конфигурация</h2>
<p>После установки плагина создается несколько файлов, хранящих конфигурацию:</p>
<ol>
	<li><tt>config/initalizers/typus.rb</tt> &#8211; здесь хранятся общие настройки админки</li>
	<li><tt>config/typus/*.yml</tt> &#8211; здесь хранятся настройки для различных моделей</li>
	<li><tt>config/typus/*_roles.yml</tt> &#8211; здесь хранятся права доступа к моделям</li>
</ol>
<p>В первом файле задаются имя приложения, используемая локаль и т.п. Вот часть опций из него:<br />
<pre><code class="ruby">Typus::Configuration.options[:app_name]
Typus::Configuration.options[:config_folder]
Typus::Configuration.options[:email]
Typus::Configuration.options[:locales]
Typus::Configuration.options[:recover_password]
Typus::Configuration.options[:root]
Typus::Configuration.options[:ssl]
Typus::Configuration.options[:templates_folder]
Typus::Configuration.options[:user_class_name]
Typus::Configuration.options[:user_fk]
</code></pre></p>
<p>Например, чтобы задать русскую локаль необходимо в соответствующей опции этого файла выставить:<br />
<pre><code class="ruby">Typus::Configuration.options[:locales] = [ [ &quot;Русский&quot;, :ru ] ]</code></pre></p>
<p>В файлах настроек для моделей хранится описание того, как модели должны отображаться в админке, в частности:</p>
<ul>
	<li>Какие поля должны отображаться при каждом из действий;</li>
	<li>Какие связи должны редактироваться;</li>
	<li>По каким полям должна осуществляться фильтрация и поиск;</li>
	<li>В каком порядке должны выводится записи;</li>
	<li>И тому подобное&#8230;</li>
</ul>
<h2>Модификация админки</h2>
<p>Сгенерированная админка может быть легко модифицирована одним из следующих способов:</p>
<ul>
	<li>(банальный) Добавление новых контроллеров в админку;</li>
	<li>Добавление новых действий к существующим контроллерам. Действие описывается в сгенерированном контроллере, а его указание в конфигурационном файле админки позволяет привязать действие к интерфейсу администрирования;</li>
	<li>Переопределение видов;</li>
	<li>Добавление блоков в виды. В последнем случае нет необходимости переопределять весь интерфейс, достаточно толко создать партиалы, которые будут встроены в соответствующие участки интерфейса. Список возможных партиалов приведен ниже (где <span class="caps">RESOURCE</span> &#8211; имя редактируемой модели):<br />
<pre><code class="bash">views/admin/login/_{bottom|top}.html.erb
views/admin/dashboard/_{bottom|sidebar|top}.html.erb
views/admin/RESOURCE/_edit.html.erb
views/admin/RESOURCE/_edit_{bottom|sidebar|top}.html.erb
views/admin/RESOURCE/_index.html.erb
views/admin/RESOURCE/_index_{bottom|sidebar|top}.html.erb
views/admin/RESOURCE/_new.html.erb
views/admin/RESOURCE/_new_{bottom|sidebar|top}.html.erb
views/admin/RESOURCE/_show.html.erb
views/admin/RESOURCE/_show_{bottom|sidebar|top}.html.erb
</code></pre></li>
</ul>
<h2>Пример приложения</h2>
<p>На сайте проекта выложен специальный шаблон, позволяющий сгенерировать простое демонстрационное приложение. С его помощью, чтобы попробовать Typus достаточно выполнить в консоли:</p>
<pre><code class="bash">$ rails example.com -m http://intraducibles.com/projects/typus/demo.txt
$ cd example.com &amp;&amp; script/server
</code></pre><p>После этого можно заходить в админку по адресу <tt>http://127.0.0.1:3000/admin</tt>.</p>

		<div id="related">
			<h2>Записи на схожие темы</h2>
			<ul>
	<li><a href="http://blog.alno.name/2009/07/rails-testing-tools/">Средства тестирования в Ruby on Rails</a></li>
	<li><a href="http://blog.alno.name/2008/05/periodic-tasks-in-ruby-on-rails/">Периодические задачи в Ruby on Rails</a></li>
	<li><a href="http://blog.alno.name/2009/03/radiant-extensions-development/">Разработка расширений для Radiant</a></li>
	<li><a href="http://blog.alno.name/2009/08/irwi-plugin/">Irwi &#8211; Wiki в Rails-приложениях</a></li>
</ul>
		</div>

		<div id="links">
			<h2>Ссылки</h2>
			<ul>
	<li><a href="http://intraducibles.com/projects/typus">Официальная страница проекта</a></li>
	<li><a href="http://github.com/fesplugas/typus">Репозиторий на GitHub</a></li>
</ul>
		</div>
	]]></content:encoded>
</item>
<item>
	<title>Использование Boost Pointer Container Library</title>
	<link>http://blog.alno.name/ru/2009/04/using-boost-ptr-containers</link>
	<comments>http://blog.alno.name/ru/2009/04/using-boost-ptr-containers#comments</comments>
	<pubDate>Thu, 09 Apr 2009 12:23:08 GMT</pubDate>
	<dc:creator>Alno</dc:creator>
	<guid isPermaLink="true">http://blog.alno.name/ru/2009/04/using-boost-ptr-containers</guid>
	<description><![CDATA[<p>Как часто Вам приходилось хранить в стандартных STL контейнерах указатели на объекты? Я думаю, достаточно. Но хранение обычных указателей приводит к тому, что приходится тратить дополнительные усилия на то, чтобы проверять, что память освобождается при уничтожении контейнера.</p>

<p>Обычное решение этой проблемы - <a href="http://blog.alno.name/2008/05/using-boost-smart-pointers/">умные указатели</a> с подсчетом ссылок, обеспечивающие автоматическое освобождение памяти. Однако, достаточно часто встречаются случаи, когда их использование избыточно, например, когда объекты имеют всего одного владельца и подсчет ссылок, вообще говоря не нужен.</p>

<p>А судя по замерам, накладные расходы по производительности и памяти на подсчет ссылок достаточно велики: <a rel="nofollow" href="http://www.boost.org/doc/libs/1_38_0/libs/smart_ptr/smarttests.htm">Boost smart pointer timings</a>.</p>

<p>Что же делать в таких случаях? На помощь приходит очередная библиотека в составе <a href="http://blog.alno.name/tags/boost/">Boost</a> - Pointer Container Library. Она предоставляет основные контейнеры, схожие со стандартными, специально предназнченные для хранения указателей.</p>
]]></description>
	<content:encoded><![CDATA[
		<p>Как часто Вам приходилось хранить в стандартных STL контейнерах указатели на объекты? Я думаю, достаточно. Но хранение обычных указателей приводит к тому, что приходится тратить дополнительные усилия на то, чтобы проверять, что память освобождается при уничтожении контейнера.</p>

<p>Обычное решение этой проблемы - <a href="http://blog.alno.name/2008/05/using-boost-smart-pointers/">умные указатели</a> с подсчетом ссылок, обеспечивающие автоматическое освобождение памяти. Однако, достаточно часто встречаются случаи, когда их использование избыточно, например, когда объекты имеют всего одного владельца и подсчет ссылок, вообще говоря не нужен.</p>

<p>А судя по замерам, накладные расходы по производительности и памяти на подсчет ссылок достаточно велики: <a rel="nofollow" href="http://www.boost.org/doc/libs/1_38_0/libs/smart_ptr/smarttests.htm">Boost smart pointer timings</a>.</p>

<p>Что же делать в таких случаях? На помощь приходит очередная библиотека в составе <a href="http://blog.alno.name/tags/boost/">Boost</a> - Pointer Container Library. Она предоставляет основные контейнеры, схожие со стандартными, специально предназнченные для хранения указателей.</p>
<h2>Использование</h2>

<p>В примере мы будем использовать ptr_vector. Для него необходимо подключить один заголовочный файл:</p>

<pre><code class="cpp">
#include &lt;boost/ptr_container/ptr_vector.hpp&gt;
</code></pre>

<p>Рассмотрим простейший пример использования, приведенный в документации библиотеки. Предположим, что у нас имеется классическая иерархия классов:</p>

<pre><code class="cpp">
class animal
{
public:
    virtual      ~animal()   {}
    virtual void eat()       = 0;
    virtual int  age() const = 0;
    // ...
};

class mammal : public animal
{
    // ...
};

class bird : public animal
{
    // ...
};
</code></pre>

<p>Теперь, нам необходимо создать список животных. Для этого мы будем использовать контейнер <b>ptr_vector</b>:</p>

<pre><code class="cpp">
boost::ptr_vector&lt;animal&gt; the_animals;
</code></pre>

<p>Сразу обращаем внимание: при объявлении контейнера * не указывается. Чтобы добавить объект в контейнер, вызываем метод push_back, аналогично стандартному контейнеру <b>std::vector</b>:</p>

<pre><code class="cpp">
the_animals.push_back( new mammal(&quot;joe&quot;) );
the_animals.push_back( new bird(&quot;dodo&quot;) );
</code></pre>

<p>Теперь о доступе к элементам: <b>контейнер предоставляет доступ к элементам не по указателям, а по ссылкам</b>. То, есть необходимо писать следующим образом:</p>

<pre><code class="cpp">
the_animals[0].eat(); // А в случае std::vector&lt;animal*&gt; это было бы vec[0]-&gt;eat();

boost::ptr_vector&lt;animal&gt;::iterator  i = vec.begin();
i-&gt;eat(); // Опять же, для std::vector это было бы (*i)-&gt;eat(); Не знаю как Вас, но меня такая запись всегда раздражала.
</code></pre>

<p>Еще одно отличие: обработка нулевых указателей. Стандартный <b>vector</b> позволяет добавлять элементы - нулевые указатели. <b>ptr_vector</b> - не позволяет (по умолчанию). То есть, следующий код вызывает исключение:</p>

<pre><code class="cpp">
the_animals.insert( the_animals.begin(), 0 ); // Исключение
</code></pre>

<p>Однако, если Вам необходимо, Вы можете разрешить хранение нулевых указателей. Делается это следующим образом:</p>

<pre><code class="cpp">
boost::ptr_vector&lt; boost::nullable&lt;animal&gt; &gt; the_animals_nullable;

the_animals_nullable.insert( the_animals_nullable.begin(), 0 ); // Это уже корректно
</code></pre>

<h2>Клонирование и перемещение данных</h2>

<p>Крайне важное отличие таких контейнеров от стандартных в том, что они не могут быть скопированы напрямую! В чем причина: предполагается, что каждый указатель в любой момент времени находится в одном из таких контейнеров, который и освободит память при необходимости. Если же скопировать содержимое, то Вы получили бы указатель в двух контейнерах, что привело бы к последующим проблемам с их удалением.</p>

<p>Однако, не все так плохо. Вместо копирования можно осуществлять другие операции:</p>

<ul>
  <li>Во-первых, контейнеры можно клонировать, вызывая клонирование всех входящих в них элементов.</li>
  <li>Во-вторых, данные можно извлечь из контейнера. И затем, например, переместить в другой.</li>
</ul>

<p>Сначала о клонировании. Для того, чтобы содержимео контейнера могло быть клонировано, для всех его элементов должны быть определены две функции:</p>

<pre><code class="cpp">
namespace boost
{
    inline T* new_clone( const T&amp; t )
    {
        // Здесь клонируем объект...
    }

    void delete_clone( const T* t )
    {
        // Здесь удаляем объект...
    }
}
</code></pre>

<p>Если Ваш компилятор поддерживает Argument-Dependent Lookup, то их можно объявлять не в пространстве имен boost, а в том, где расположен обрабатываемый ими тип.</p>

<p>После того, как такие функции объявлены, можно вызывать метод <tt>clone</tt> контейнера:</p>

<pre><code class="cpp">
the_animal_clones = the_animals.clone();
</code></pre>

<p>Теперь об извлечении и перемещении. Контейнеры предоставляют набор методов, позволяющих извлечь указатель по итератору и переместить набор элементов в другой контейнер:</p>

<pre><code class="cpp">
boost::ptr_vector&lt;animal&gt;::auto_type the_animal = the_animals.release( the_animals.begin() ); // Извлекаем первый элемент
the_animal-&gt;eat();
</code></pre>

<p>При этом <tt>auto_type</tt> представляет из себя аналог <tt>std::auto_ptr</tt>.</p>

<p>Для перемещения  существуют два вариант метода <tt>transfer</tt></p>

<pre><code class="cpp">
another_zoo.transfer( another_zoo.end(), // Добавляем последним элементом
                      zoo.begin(),       // Первый элемент
                      zoo );             // Из этого контейнера

another_zoo.transfer( another_zoo.begin(), // Добавляем в конец
                      zoo.begin(),       // От первого
                      zoo.end(),         // До последнего
                      zoo );             // Из этого контейнера

</code></pre>

<h2>Алгоритмы</h2>

<p>К сожалению, с этими контейнерами нельзя использовать стандартные алгоритмы STL. Однако, некоторые основные реализованы в виде методов:</p>

<pre><code class="cpp">
boost::ptr_vector&lt;animal&gt; zoo;
...
zoo.sort();                               // Предполагаем, что описан 'bool operator&lt;( const animal&amp;, const animal&amp; )'
zoo.sort( std::less&lt;animal&gt;() );          // То же самое, обращаем внимание на отсутствие *
zoo.sort( zoo.begin(), zoo.begin() + 5 ); // Сортируем участок

zoo.unique();                             // Предполагаем, что описан 'bool operator==( const animal&amp;, const animal&amp; )'
zoo.unique( zoo.begin(), zoo.begin() + 5, my_comparison_predicate() ); 

zoo.erase_if( my_predicate() );
</code></pre>

		<div id="related">
			<h2>Записи на схожие темы</h2>
			<ul>
	<li><a href="http://blog.alno.name/2008/05/using-boost-smart-pointers/">Умные указатели в C++: boost::shared_ptr, boost::weak_ptr, boost::intrusive_ptr</a></li>
	<li><a href="http://blog.alno.name/2008/04/using-boost-multi-index/">Использование контейнеров boost::multi_index</a></li>
</ul>
		</div>

		<div id="links">
			<h2>Ссылки</h2>
			<ul>
	<li><a href="http://www.boost.org/doc/libs/1_38_0/libs/ptr_container/doc/ptr_container.html">Официальная документация [En]</a></li>
	<li><a href="http://dvinogradov.blogspot.com/2008/11/storing-dynamic-created-objects-in.html">Запись в Raider&#8217;s Programming Blog [Ru]</a></li>
	<li><a href="http://sindicollo.blogspot.com/2008/09/boost.html">Boost на русском [Ru]</a></li>
</ul>
		</div>
	]]></content:encoded>
</item>
<item>
	<title>XML-RPC в Ruby</title>
	<link>http://blog.alno.name/ru/2009/03/xmlrpc-in-ruby</link>
	<comments>http://blog.alno.name/ru/2009/03/xmlrpc-in-ruby#comments</comments>
	<pubDate>Sat, 28 Mar 2009 00:20:34 GMT</pubDate>
	<dc:creator>Alno</dc:creator>
	<guid isPermaLink="true">http://blog.alno.name/ru/2009/03/xmlrpc-in-ruby</guid>
	<description><![CDATA[<p>Как известно, <span class="caps">XML</span>-<span class="caps">RPC</span> является достаточно распространенным методом вызова удаленных процедур, основанным на <span class="caps">XML</span>. Однако, несмотря на то, что он значительно проще, чем, например, <span class="caps">SOAP</span>, его использование все еще иногда достаточно сложно: необходимо сериализовать объекты в специальное <span class="caps">XML</span>-представление, затем распарсить полученный релзультат.</p>
<p>Однако, на самом деле, не все так плохо!</p>
<p>В стандартной библиотеке <a href="http://blog.alno.name/tags/ruby">Ruby</a> существует замечательная поддержка <span class="caps">XML</span>-<span class="caps">RPC</span>, о которой я немного расскажу далее.</p>]]></description>
	<content:encoded><![CDATA[
		<p>Как известно, <span class="caps">XML</span>-<span class="caps">RPC</span> является достаточно распространенным методом вызова удаленных процедур, основанным на <span class="caps">XML</span>. Однако, несмотря на то, что он значительно проще, чем, например, <span class="caps">SOAP</span>, его использование все еще иногда достаточно сложно: необходимо сериализовать объекты в специальное <span class="caps">XML</span>-представление, затем распарсить полученный релзультат.</p>
<p>Однако, на самом деле, не все так плохо!</p>
<p>В стандартной библиотеке <a href="http://blog.alno.name/tags/ruby">Ruby</a> существует замечательная поддержка <span class="caps">XML</span>-<span class="caps">RPC</span>, о которой я немного расскажу далее.</p><p>Для создания простейшего клиента необходимо всего лишь подключить файл <tt>xmlrpc/client</tt>, создать объект клиента и вызывать функции RPC, как методы этого объекта. А возвращаемым значением метода является хэш выходных параметров.</p>

<pre><code class="ruby">
  require 'xmlrpc/client'
  require 'pp'

  client = XMLRPC::Client.new2(&quot;http://xmlrpc-c.sourceforge.net/api/sample.php&quot;)
  result = client.call(&quot;sample.sumAndDifference&quot;, 5, 3)
  pp result
</code></pre>

<p>Это пример с сайта официальной документации. По идее, <tt>result</tt> должен представлять из себя хэш, в котором будут содержаться возвращаемые значения. Однако, этот пример не работает (как минимум у меня). При вызове он выдает исключение:</p>

<pre><code class="">/usr/lib/ruby/1.8/xmlrpc/client.rb:555:in `do_rpc': Wrong content-type (received 'text/html' but expected 'text/xml'):  (RuntimeError)</code></pre>

<p>Что же не так? Сервер, к которому обращается клиент почему-то возвращает в заголовке <tt>Content-Type</tt> значение <tt>text/html</tt>, хотя ожидается <tt>text/xml</tt>. Вообще говоря, это стоит считать багом настройки сервера, но если Вы осуществляете запросы к каким-то внешним сервисам, есть вероятность, что какой-то из них так же будет отсылать некорректный <tt>Content-Type</tt>.</p>

<p>Первое что мне пришло в голову - это отключить проверку в клиенте. Самый простой способ не требует никакой модификации кода библиотеки. Для этого перед использованием клиента необходимо поместить следующий код:</p>

<pre><code class="ruby">
  XMLRPC::Client.class_eval do
    def parse_content_type(a)
      ['text/xml']
    end
  end
</code></pre>

<p>То есть мы просто подменяем метод <tt>parse_content_type</tt> с тем, чтобы он всегда возвращал <tt>text/xml</tt>. Теперь можно спокойно делать запросы к различным сервисам и вышеприведенный пример выведет что-то вроде:</p>

<pre><code class="ruby">
{&quot;sum&quot;=&gt;8, &quot;difference&quot;=&gt;2}
</code></pre>

<p>Теперь можно использовать библиотеку для работы сразличными XML-RPC сервисами в сети.</p>

<h2>Ссылки</h2>

<ul>
  <li><a href="http://www.ruby-doc.org/stdlib/libdoc/xmlrpc/rdoc/index.html">Ruby XMLRPC Documentation (En)</a></li>
</ul>

		

		
	]]></content:encoded>
</item>


</channel></rss>
