Буду на DevConf, Москва, 17-18 мая

Опубликовано 05.05.2010

Нынче у меня весна конференций – еду на 17-18 мая на DevConf::Ruby. Буду участвовать по полной – читать доклад и проводить мастер-класс.

Доклад: Разработка приложений под Facebook на Ruby on Rails

Я когда-то уже читал этот доклад в Омске на RuPyRu. С тех пор много чего случилось: я создал свой собственный полноценный движок для создания текстовых RPG-игр, продал его довольно большому количеству клиентов, перешел с разработки FBML-приложений на IFRAME-приложения, раскурил концепцию Facebook for Websites и наткнулся на многие подводные камни разработки приложений для социальных сетей. Всем этим опытом я и хочу поделиться.

Доклад может быть полезен тем разработчикам, кто планирует разрабатывать приложения под Facebook или интегрировать с ним существующее приложение. При нынешней аудитории в 400 миллионов пользователей платформа приложений Facebook сама по себе уже является очень привлекательным рынком разработки. Очень многие западные проекты уже в той или иной степени интегрированы с этой социальной сетью и еще больше будет интегрировано в ближайшие год-два. Если вы занимаетесь разработкой на западный рынок – вам сам Бог велел приехать и послушать про грабли, на которые вам 100% предстоит наступить при интеграции вашего приложения с Facebook.

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

Мастер-класс: Оптимизируем Ruby on Rails приложение под высокие нагрузки

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

Мастер-класс будет полезен в первую очередь тем разработчикам, кто разрабатывает или планирует разрабатывать приложения, которые будут обслуживать десятки и сотни тысяч человек в сутки. Если б мне год-два назад предложили посетить подобный мастер-класс и сэкономить себе нервы в будущем – я бы и минуты не стал раздумывать.

Помимо меня на конференции будет еще небезызвестный Yehuda Katz – один из ведущих разработчиков Rails 3. Будет интересно посмотреть, у кого из нас борода длиннее :) Также доклады будут читать Евгений Хлызов, Дмитрий Галинский, Иван Самсонов и многие другие

В общем, приглашаю и вас принять участие. Я уверен, вы узнаете для себя много интересного.

Консоль класса Люкс

Опубликовано 05.04.2010

При работе с Rails-приложениями довольно много времени я провожу в консоли. Стандартный функционал, предоставляемый IRB по умолчанию, не сказать чтобы шибко богат. Вчера нашлось у меня немного свободного времени, чтобы улучшить свое скромное житие-бытие.

Настройки IRB-консоли задаются в файле .irbrc в домашней директории пользователя. Это по сути обычный ruby-файл, в нем можно подключать различные gem-ы, добавлять методы и т.д.

Вот несколько маленьких трюков для улучшения стандартной консоли, которые мне удалось раскопать (итоговый файл конфигурации см. в конце статьи):

  • wirble – добавляет посветку в стандартный вывод IRB и добавляет поддержку истории выполнения команд
  • awesome_print – позволяет выводить объекты на экран в удобном формате и с подсветкой. Использование в консоли:
    ap this_is_my_object
  • looksee – позволяет посмотреть список методов объекта, разбитый по классам/модулям, из которых эти методы происходят. Очень удобно при исследовании внутренностей классов и модулей фреймворка при отладке или разработке плагинов. Использование в консоли:
    lp this_is_my_object
  • Вывод лога SQL-запросов в консоль – при работе с моделями ActiveRecord все запросы к БД будут выводиться прямо на экран:
    show_log # включаем логи
    hide_log # выключаем логи
  • Замер скорости выполнения кода:
    time { ... }      # Код выполнится 1 раз
    time(100) { ... } # Код выполнится 100 раз
  • Выполнение SQL-запроса:
    sql "SELECT count(*) FROM users"

Надо сказать, работать в консоли стало в разы удобнее.

Итоговый файл .irbrc можно взять здесь

А вы используете какие-нибудь дополнения для IRB-консоли?

РИТ++ 12-14 апреля, Москва

Опубликовано 01.04.2010

Итак, 12-14 апреля я буду читать доклад о Ruby on Rails на конференции РИТ++ 2010. Программа конференции уже официально сформирована, поэтому я могу заявить об этом официально.

Доклад будет предназначен для людей, не знакомых с Rails или знакомых очень поверхностно. Больших откровений и срывания покровов я не планирую делать (во всяком случае в самом докладе, про вопросы и общение в кулуарах ничего обещать не могу ;), однако если вы (или кто-то из ваших знакомых) хотели бы узнать о том, чем хороши рельсы и где их можно использовать, то мой доклад – именно для вас. Приходите, будет интересно. Примерные тезисы доклада можно посмотреть здесь (блин, знал бы что их опубликуют – оформил бы поприличнее).

Однако, конференция – это не единственное ради чего я поеду в столицу, есть еще масса интересных дел, которым я хотел бы посвятить часть времени. Например, мне бы хотелось пообщаться с коллегами-рельсовиками и/или прийти в гости в какую-нибудь компанию, где активно занимаются разработкой проектов на Ruby on Rails. Уверен, у нас найдутся общие темы для общения.

Я буду в Москве с утра 9 апреля и до вечера 14 апреля. Если у вас есть желание встретиться или пригласить меня в гости – пишите письма или стучитесь в Jabber/GTalk rene.dekart@gmail.com

Кофейные ссылки переехали в Twitter

Опубликовано 25.11.2009

Как выяснилось, у меня не так много времени чтобы регулярно писать посты в блог, однако вполне хватает чтобы постить интересные ссылки в мой Twitter.

Вот некоторые из последних:

Все, что я найду интересное для rails-разработчиков, в первую очередь попадает в мой твиттер – @dekart1234

Что вам интересно?

Опубликовано 22.10.2009

В голове роится аж сразу несколько идей для новых постов, даже не знаю какую выбрать :) Поможете?

Помочь Декарту выбрать тему »

Расширение ассоциаций в Rails

Опубликовано 12.10.2009

Не прошло и полугода с моей последней более-менее пристойной заметки :) Надо сказать, последние несколько месяцев выдались настолько насыщенные, что даже не было желания что-либо такое интересное написать – все мозги уходили на разработку действительно классных вещей. Однако сегодня, наконец, пришло понимание, что дальше так продолжаться не может – пора делиться мыслями, бешено роящимися в голове.

Давайте немного поговорим об ассоциациях. Если вы программируете на Rails дольше 15 минут, то уже наверняка знаете, что рельсы поддерживают несколько типов ассоциаций – belongs_to, has_one, has_many и has_and_belongs_to_many. Каждый тип ассоциаций решает определенную задачу, у каждого свой API. Но есть у них одна общая замечательная особенность – ассоциации можно расширять.

Допустим, у нас есть проект “Доска объявлений”, где пользователь может опубликовать до 5 объявлений каждое стоимостью 10 долларов. Давайте взглянем на модель пользователя, содержащую несколько методов для проверки возможности публикации объявлений:

class User < ActiveRecord::Base
  has_many :classifieds

  def can_publish_classified?
    has_free_classified_slots? and enough_money_to_publish_classified?
  end

  def has_free_classified_slots?
    self.classifieds.size <= 5
  end

  def enough_money_to_publish_classified?
    self.balance >= 10
  end
end

Вот так выглядят вызовы методов проверок:

@user.can_publish_classified?
@user.has_free_classified_slots?
@user.enough_money_to_publish_classified?

Обратите внимание на постоянно повторяющееся слово classified в названиях методов. Было бы гораздо удобнее, если бы методы вызывались в контексте того, к чему они непосредственно относятся, а именно – в контексте ассоциации classifieds. Именно для таких случаев в Rails реализована возможность расширения ассоциаций, а точнее – прокси-классов, которые эти ассоциации обслуживают. Делается это следующим образом:

class User < ActiveRecord::Base
  has_many :classifieds do
    def can_publish?
      free_slots? and enough_money_to_publish?
    end

    def free_slots?
      self.size <= 5
    end

    def enough_money_to_publish?
      proxy_owner.balance >= 10
    end
  end
end

Теперь проверки возможности публикации объявлений выглядят у нас так:

@user.classifieds.can_publish?
@user.classifieds.free_slots?
@user.classifieds.enough_money_to_publish?

Существенно лучше, не так ли? Обратите внимание на метод proxy_owner, который вызывается в методе enough_money_to_publish?. Его описание, а так же описание других полезных методов прокси-класса ассоциации можно найти в документации (см. Association extensions):

  • proxy_owner – объект, частью которого является данная ассоциация (в нашем случае экземпляр класса User)
  • proxy_reflection – объект класса ActiveRecord::Reflection::AssociationReflection, хранящий в себе сведения об ассоциации
  • proxy_target – объект или коллекция объектов, которые загружаются ассоциацией (в нашем случае это коллекция объявлений, привязанных к пользователю); загрузка этих данных выполняется вызовом метода load_target

Однако давайте допустим, что авторами объявлений у нас могут стать не только пользователи, но еще и компании (модель Company). Есть два пути добавления методов в ассоциацию второй модели – copy-paste и вынос методов в отдельный модуль. Я думаю, нет сомнений, что второй путь более правильный.

Выносим код расширения в отдельный модуль:

module ClassifiedExtension
  def can_publish?
    free_slots? and enough_money_to_publish?
  end

  def free_slots?
    self.size <= 5
  end

  def enough_money_to_publish?
    proxy_owner.balance >= 10
  end
end

А затем расширяем им ассоциации:

class User < ActiveRecord::Base
  has_many :classifieds, :extend => ClassifiedExtension
end

class Company < ActiveRecord::Base
  has_many :classifieds, :extend => ClassifiedExtension
end

Как видите, расширять ассоциации совсем не сложно. Однако, как ни странно, далеко не все разработчики, даже опытные, используют эту возможность. А зря :)

DoS-уязвимость в Ruby

Опубликовано 10.06.2009

В ruby найдена уязвимость к DoS-атаке. Злоумышленник может вызвать нарушение работы интерпретатора, передав очень большое значение в функцию, использующую BigDecimal. Уязвимости подвержены следующие версии ruby:

  • Ruby 1.8.6-p368 и более ранние
  • Ruby 1.8.7-p160 и более ранние
  • Ruby Enterprise Edition 20090520 и более ранние

Код, с помощью которого можно выявить наличие проблемы (можно выполнить, например, в irb-консоли):

require 'bigdecimal'
BigDecimal("9E69999999").to_s("F")

Программисты из Phusion буквально только что выпустили исправленную версию Ruby Enterprise Edition. Советую всем обновиться до новой версии.

Как выбрать хостинг для Rails

Опубликовано 27.04.2009

После установки на сайте чата ко мне стали периодически стучаться люди и задавать разные вопросы. Особенно часто я получаю вопрос о том, где лучше всего хостить Rails-приложения. Вопрос, прямо скажем, очень похож на “какую машину лучше всего купить”. Ответ на него очень сильно зависит от того, что вы собираетесь с этой машиной делать – дрифтовать по улицам, лазать по горам, возить холодильники или катать инвестиционных банкиров. Выбор хостингы в этом отношении нисколечки не отличается и тоже очень сильно зависит от того, какие приложения вы собираетесь хостить. Давайте рассмотрим наиболее важные параметры, которые нужно учесть при выборе хостинга.

Расположение

Перед тем, как выбирать хостинг-провайдера, вы должны четко осознавать, где живут ваши основные посетители. Если это россияне или жители СНГ – нет смысла покупать хостинг в США. Равно как и наоборот. Несмотря на то, что скорость каналов растет, разница будет сильно чувствоваться, особенно если в вашем проекте много графики, видео или другого тяжелого контента. Поэтому хостера лучше выбирать среди тех, чьи сервера располагаются в стране, в которой живут ваши посетители.

Тип хостинга

Платформа Rails гораздо более требовательна в плане хостинга, чем, скажем, PHP, поэтому далеко не каждый провайдер поддерживает эту платформу. А среди тех провайдеров, которые заявляют о поддержке, далеко не все предлагают приемлимый вариант развертывания. Поэтому shared-хостинг – не лучший вариант для размещения рельсового приложения. Если вы действительно очень сильно ограничены в средствах (хотя я уверен, что выделить лишние $10 на нормальный хостинг – надуманная проблема), то при выборе shared-хостинга с поддержкой Rails обратите внимание на то, чтобы хостинг-провайдер предоставлял для развертывания модуль Passenger, а не FastCGI или CGI. Два последних варианта – большой геморрой. Если не уверены в том, каким образом вам предстоит поднимать приложения на данном провайдере – обязательно проконсультируйтесь со службой поддержки.

Если все же нашлась лишняя десятка, то уже можно покупать VPS – виртуальный сервер с позможностью настройки. Этот вариант хорош тем, что вы не ограничены тем набором программного обеспечения, которое хостер счел необходимым, и вольны устанавливать необходимое ПО по своему вкусу. VPS приемлем для небольших и средних проектов со сравнительно небольшой нагрузкой и является идеальным вариантом для старта нового проекта.

Если же проект достаточно крупный или ресурсоемкий, то вам потребуется выделенный сервер (dedicaded) или даже cloud-платформа вроде Amazon EC2. Хотя, если вы работаете с таким проектом, вы наверняка уже все знаете сами :)

Далее речь пойдет преимущественно о VPS и Dedicaded.

Специализация

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

Объем памяти

Среднее Rails-приложение занимает в оперативной памяти порядка 100 мегабайт на один процесс, на меньшее вряд ли стоит рассчитывать (особенно если вы используете RMagic, но тут готовьтесь к худшему). Поэтому минимальный объем оперативной памяти, который можно считать приемлимым – 256 мегабайт. С ростом посещаемости вам придется поддерживать несколько процессов одновременно, поэтому оперативной памяти потребуется больше.

Объем дискового пространства

Необходимый объем дискового пространства зависит от того, будет ли в вашем проекте возможность загрузки изображений, видео и других файлов. Если нет – объем диска для вас практически не важен, само приложение занимает очень мало места. Если загрузка файлов имеет место, то объем диска нужно рассчитывать из общего количества файлов и их среднего объема. Например, для изображений с фотоаппарата можно посчитать так: до 5 мегабайт на изображение плюс еще 1 мегабайт на уменьшенные копии. Итого на каждые 1000 картинок – порядка 6 гигабайт.

Бэкап

Для VPS и dedicaded-хостинга весьма актуально то, как организовано резервное копирование данных. Если хостер берет эти заботы на себя и это включено в план – очень хорошо. Некоторые хостеры предлагают резервное копирование в качестве дополнительной услуги за небольшую плату. Если же резервного копирования не предусмотрено вовсе – можно относительно недорого организовать резервное копирование на Amazon S3. Так или иначе, этот момент обязательно нужно учитывать.

Масштабируемость

Хорошим проектам свойственно вырастать. Заранее покупать сверхмощный сервер не стоит, однако о возможности роста стоит задуматься заранее. При выборе хостера обратите постарайтесь узнать, насколько легко будет, например, увеличить объем оперативной памяти для проекта или объем диска. Если вы арендуете выделенный сервер, узнайте, насколько быстро хостер сможет поставить новый сервер для переезда.

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

Поддержка

Рано или поздно какие-то вопросы или проблемы с хостингом все равно возникнут, поэтому лучше выбрать хостера, который заранее дает ответы на наиболее часто возникающие вопросы. Установка и настройка ПО, обновление, бэкапы, масштабирование – чем больше ответов известно заранее, тем меньше нервов вы потратите в трудных ситуациях. Для решения особо сложных вопросов вам придется обращаться в службу поддержки, поэтому лучше заранее удостовериться, что с ней можно иметь дело. Сделать это проще всего попросив поделиться опытом тех, кто уже пользуется услугами выбранной вами компании.

Цена

Стоимость хостинга не должна являться оределяющим фактором. Если стоимость хостинга для вас важнее, чем надежность – возможно, вы выбрали не ту платформу. Подумайте, может еще не поздно перейти на PHP, его можно хостить практически бесплатно :)

Если вы все же уверены, что Rails – это ваш выбор, будьте готовы к тому, что стоимость нормального хостинга начинается где-то от $20. Это стоимость VPS с 256МБ оперативной памяти и 10ГБ диска. Найти дешевле довольно сложно, но можно.

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


Внимание! Это последняя заметка, которую вы можете прочитать в старой RSS-ленте. Если вам нравится читать Записки из тех.отдела через RSS – подпишитесь на новую ленту по адресу http://feeds2.feedburner.com/railorz

Пользовательский контент

Опубликовано 07.03.2009

Еще со времен запуска первой версии Записок я ношу в себе идею разработки собственного блогодвижка. Задача, казалось бы, не ахти какая сложная и очень многие ее успешно решают, движков понаписано великое множество на всех языках. Собственно, поэтому идея и пролежала в моей голове без дела почти 4 года. И вот буквально после новогодних праздников я наконец нашел достойный повод взяться за нее – блоги практически не представлены на Facebook.

Существует всего несколько приложений под Facebook, относящихся в той или иной степени к блоггингу. Стандартные записки (Notes) убивают всякий коммерческий интерес к разработке альтернатив. И тем не менее, я взялся такую альтернативу создать, в первую очередь ради эксперимента. Получилась вот такая штука: Blog Box (демонстрационный блог)

По ходу работы над приложением выявилось множество нюансов, которые встречаются от проекта к проекту. Одним из первых вопросов, с которыми я столкнулся – как работать с пользовательским контентом.

Разметка

Практически каждый человек хочет как-то разнообразить свои посты, добавить в них ссылки, выделить текст жирным или курсивом, вставить список или табличку. Надо понимать, что большинство пользователей не знают и знать не хотят что такое HTML. В блогоплатформах для пользователей обычно ставится WYSIWYG-редактор типа TinyMCE, FCKEditor, Xinha или любой другой. Однако в Facebook такой вариант не пройдет – платформа очень жестко контролирует JavaScript, используемый приложением, и практически сводит на нет возможность использования готовых библиотек.

Для ручной разметки текста и избавления от необходимости в визуальном редакторе можно использовать упрощенные системы разметки, которые часто применяются в wiki-системах. Для Ruby есть два реализованных формата разметки – Textile (с помощью RedCloth) и Markdown (с помощью BlueCloth). Textile более гибок в плане стилевой разметки, Markdown проще для освоения. Я решил остановиться на Textile.

Обработку текста я вынес в отдельный хелпер, единый как для постов, так и для комментариев:

def format_content(content)
  textilize(content.to_s)
end

Метод textilize – стандартный рельсовый, становится доступен после установки RedCloth. Для обработки постов и комментариев я создал отдельные хелперы, передающие соответствующий контент для форматирования:

def post_content(post)
  format_content(post.text)
end

def post_excerpt(post)
  post.excerpt.blank? ? post_content(post) : format_content(post.excerpt)
end

def comment_content(comment)
  format_content(comment.text)
end

Далее вызываем нужные нам хелперы хоть на странице просмотра поста, хоть при выводе списка постов.

Очистка HTML

Если честно, я ожидал, что мое приложение сразу кто-нибудь попробует использовать в гнусных целях, похакать, засунуть XSS или какой-нибудь мерзкий JavaScript. Однако я не ожидал, что это случится в первый же день жизни приложения. Хорошо, что я озаботился где возможно прикрыть вывод пользовательских данных с помощью хелпера h():

<%=h @post.title %>

Однако текст поста, который форматируется с помощью Textile, так не обезопасишь. Поэтому следующее что пришлось делать – это очистку текста постов от потенциально опасных тэгов и атрибутов. Для решения этой задачи существует несколько различных вариантов. В рельсах есть встроенный хелпер для очистки от ненужных тэгов – sanitize. Есть сторонние решения, такие как xss_terminate, Dryopteris или sanitize, использующие возможности более быстрых библиотек для работы с HTMLHpricot и Nokigiri.

Я решил использовать в своем проекте плагин Sanitize – он для меня оказался более удобен в конфигурировании. Для интеграции с моделями я написал небольшой модуль, в котором немного поменял стандартные схемы очистки тэгов и атрибутов, а также добавил метод sanitize_attributes для быстрого привязывания очистки атрибутов. Весь входящий HTML разбирается средствами Hpricot, вычищаются ненужные тэги и атрибуты, добавляются нужные атрибуты (например, rel=nofollow для ссылок из комментариев) и обработанный код сохраняется в БД.

Специальные тэги

Мое знакомство с блоггингом, как и у многих, началось с LiveJournal. Система во многом неудобная, но в ней есть одна изюминка, которая мне очень нравилась – специальные тэги. Например, если я вставлю в текст поста тэг <lj user="my_friend">, то в тексте поста будет показана красиво оформленная ссылочка на журнал моего друга со всплывающей по наведению мышки информацией. Фишка очень интересная и я обязательно хотел использовать ее в своем блогодвижке.

Что самое смешное, я был даже вынужден ее использовать, когда дело дошло до вставки видео. В Facebook есть суровое ограничение на то, какие тэги можно использовать на страницах, а какие – нет. Это ограничение касается и тэга <object>, который по факту запрещен. Вместо него для вставки Flash используется тэг <fb:swf>, однако он сильно ограничен в плане конфигурирования. Видео-хостинги же для вставки видео дают только код на основе тэга <object>, без вариантов. Я задумался.

Решение нашлось довольно быстро – выводить обычный код вставки видео внутри тэга <iframe>, на который ограничение не накладывается. Однако опять же, код для iframe нужно каким-то образом сформировать, да и сам iframe нужно как-то вставить. Для формирования кода я решил использовать плагин acts_as_unvlogable, позволяющий из URL страницы с видео сформировать код. Вопрос со вставкой iframe встал на первый план.

На помощь пришла библиотека Radius, ключевая фишка Radiant CMS. С ее помощью можно создавать свои тэги, которые затем парсятся и обрабатываются ruby-кодом. Для размещения видео пользователю необходимо сформировать и вставить в текст поста вот такой код:

<bb:video url="http://www.youtube.com/watch?v=v9Lj0lF9Lao"></bb:video>

Обработка radius-кода вынесена в отдельный модуль, упрощающий добавление новых тэгов и их использование. Метод класса define_tag объявляет новый тэг, создает из блока метод экземпляра, который вызывается при обработке тэга, а так же добавляет тэг в общий список спец-тэгов, который можно затем использовать для расширения схемы очистки HTML, используемой sanitizer’ом:

  • Blogbox::RadiusContext.tags_with_namespace – все спецтэги
  • Blogbox::RadiusContext.tag_attributes – все разрешенные атрибуты

Для того, чтобы подключить спец-тэги стразу везде, меняем хелпер форматирования контента и хелперы вывода постов и комментариев:

def format_content(content, assigns = {}, &block)
  textilized_content = textilize(content.to_s)

  @@radius_context ||= Blogbox::RadiusContext.new(&block)
  @@radius_context.set_current_context(self, 
    assigns.merge(
      :view       => self,
      :controller => controller
    )
  )

  Radius::Parser.new(@@radius_context,
    :tag_prefix => Blogbox::RadiusContext::NAMESPACE
  ).parse(textilized_content)
end

def post_content(post)
  format_content(post.text, :post => post)
end

def post_excerpt(post)
  return post_content(post) if post.excerpt.blank? 

  format_content(post.excerpt, :post => post)
end

def comment_content(comment)
  format_content(comment.text, :post => post, :comment => comment)
end

Что особенно хорошо в спец-тэгах, так это возможность использования вашего приложения (или сторонних приложений) для создания действительно богатого пользовательского контента. Например, постов со вставленными в них фотогалереями, списками пользователей, голосованиями и т.д. От вас требуется только немного фантазии для создания набора спец-тэгов.

Резюме

Пользовательский контент – далеко не банальная вещь. Здесь есть и подводные камни, и простор для творчества, и необходимость в разумном обстоятельном подходе. Если вы работаете с пользовательским контентом – заранее обдумайте, какие возможности вы хотите дать пользователю и какие точно не хотите давать. Исходя из специфики задачи и среды можно использовать те инструменты, которые уже разработаны ruby-сообществом, и сделать свой проект по истине уникальным.

Блог переехал

Опубликовано 27.02.2009

В связи с некоторыми неурядицами Записки из тех. отдела какое-то время были в дауне. Я был приятно удивлен, что ко мне стучались люди и дальнейшей интересовались судьбой блога. В конечном итоге я нашел в себе силы и таки пднял блог из пепла.

Новый домен

Когда этот блог только зарождался, у меня была идея сделать его ресурсом для новичков, желающих изучить HTML. Идея быстро себя изжила, я увлекся рельсами и блог сейчас фактически посвящен одной только этой теме. Так что я задумал ребрендинг :) Начиная с сегодняшнего дня блог работает на новом домене RAILORZ.RU. Идея названия принадлежит незабвенному Михаилу Клишину, а я это название нагло передрал и теперь буду использовать в своих гнусных целях.

Новый движок

Точнее, новая версия старого злобного Mephisto, я счекаутил последнюю версию из репозитория. Каких-то супер-мега усовершенствований пока не наблюдаю, видимо все они глубоко внутри и незаметны для невооруженного глаза.

Новый адрес фида

Меняя домен и название блога, я хочу также сменить адрес фида, чтобы совсем отойти от концепции HTML-блога, а заодно почистить счетчик количества подписчиков от мертвых душ. Новый адрес фида: http://feeds2.feedburner.com/railorz. Старый адрес очень скоро протухнет.

Я очень рад тому, что вы меня читаете, спасибо вам за это :)

Немного мыслей о CMS на Ruby on Rails

Опубликовано 29.01.2009

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

Мои мытарства с CMS-ками разного толка начались года четыре назад, когда я осознал, что для написания на PHP собственной системы мне потребуется если не продать душу дьяволу, то как минимум заложить ее в ломбард. Готовые системы меня по разным причинам не устраивали – одни были глючные, другие было страсть как сложно расширять, третьи заточены под одну конкретную задачу. С переходом на Rails ситуация упростилась в том плане, что стало легче создавать специализированные решения, но в плане работы над типовыми проектами все только усугубилось.

Фактически, сейчас для меня выбор систем свелся к одной – Radiant. У Radiant есть два больших плюса – наличие такой штуки как Radius и наличие большого числа плагинов. Однако есть и большущие минусы. Внутренности Radiant страшны для обычного человека и чего-то в нем править иногда бывает просто жутко. Система разрабатывалась довольно давно, поэтому большинство “вкусностей” последних версий Rails не доступны. Полностью отсутствует возможность локализации административного интерфейса. Да и сам административный интерфейс включает в себя только самые базовые возможности администрирования контента, даже загрузку картинок приходится цеплять отдельным плагином.

Есть еще, конечно, adva_cms, но это скорее компиляция из нескольких типовых модулей, чем универсальное расширяемое решение. Есть Tog, Spree, Insoshi, Typo, Mephisto и прочие, но все они заточены под конкретные задачи. В итоге получается, что либо использовать специализированные движки, либо подпирать костылями Radiant и худо-бедно юзать его, либо писать CMS самому.

Рано или поздно все равно кто-то начнет писать очередную собственную CMS, поэтому я постараюсь немного облегчить ему задачу, представив хотя бы свое собственное видение того, какой должна быть современная CMS на базе Ruby on Rails.

  1. Ядро и компоненты

    Основой системы должно являться ядро – модуль, связывающий воедино все части приложения. Ядро отвечает за загрузку и конфигурирование модулей, подключение общих зависимостей и предоставление точек интеграции. Как само ядро, так и компоненты должны быть выполнены в виде плагинов, чтобы их можно было интегрировать с уже существующими приложениями. Возможен вариант когда система ставится в формате gem’а, но это применимо лишь в частных случаях, когда необходимо развертывать на одном сервере десятки и сотни однотипных приложений. Обычному разработчику проще, когда система вся под рукой и можно при необходимости взглянуть на исходный код.

    Компоненты реализуются в формате мини-приложений, оформленных в плагины. В новой версии Rails этот функционал уже встроен, а в текущей версии (2.2.2) можно использовать, например, desert. Дополнительные плагины, требующиеся для работы компонентов (will_paginate, better_nested_set и прочие) необходимо упаковывать прямо в сами компоненты, а наиболее часто употребимые – включать в ядро.

  2. Конфигурирование

    Для хранения конфигурации системы и модулей должны использоваться отдельные файлы конфигурации. Значения по умолчанию хранятся в самом компоненте и перекрываются значениями из файла конфигурации, добавленного разработчиком. Хорошим вариантом для управления конфигурацией может послужить configatron – вполне достойное решение. При желании можно реализовать хранение настроек в БД (как сейчас это сделано в Radiant) и перекрытие ими данных в конфигурационых файлах. Однако это должно быть не встрено в ядро, а реализовано отдельным компонентом.

  3. Radius

    Как вы уже поняли, я поклонник этого средства разметки. И оно не случайно – более мощного решения я пока что не встречал. С его помощью можно создавать действительно гибкий контент, фактически дать пользователю язык программирования контента. Каждый компонент должен добавлять в этот язык собственные расширения, а так же использовать расширения, добавленные другими компонентами. Средства для регистрации расширений должно предоставлять ядро.

  4. Система аутентификации и разделения прав

    Эта часть востребована практически всеми компонентами и могла бы быть включена в ядро, однако из соображений чистоты и удобства обновления ее имеет смысл вынести в отдельный компонент. API этого компонента должно быть четко описано, чтобы разработчик при необходимости мог заменить его собственной системой, привязанной к конкретному приложению.

  5. Служебные компоненты

    Есть достаточно большой класс компонентов, которые не имеют особого смысла без привязки к конкретным данным. К таковым можно отнести загрузку изображений и файлов для скачивания, комментарии, метки, рейтинги. Все это имеет смысл только в привязке, например, к странице. Система должна предоставлять средства для осуществления такой привязки через точки интеграции. Для связывания компонентов можно использовать, например, файлы конфигурации (для фанатов чистоты подхода), либо напрямую указывать зависимости в коде моделей и контроллеров. Средства для перекрытия классов предоставляет тот же desert. Необходимо, чтобы привязка служебных моделей была возможна не только для страниц, но и для данных, добавляемых пользовательскими компонентами. Это довольно легко решается через полиморфизм, как на уровне моделей, так и на уровне контроллеров и шаблонов.

  6. Версии и зависимости

    Отслеживание версий компонентов и зависимостей одних компонентов от других – это задача разработчика. Упростить этот процесс можно за счет фиксации версии ядра и предоставления инструментов для установки расширений, адаптированных по данную версию. Реализовать это можно с помощью rake-задач, которые делали бы установку плагинов из заданной метки в репозитории компонента. Допустим, для версии ядра 0.1 установка компонента версии XX должна делаться из ветки tags/0.1-XX, а для установки самой последней версии – из tags/0.1-stable. В этом вопросе я еще пока не пришел к какому-то однозначному мнению. С одной стороны, в ruby-мире уже есть готовая инфраструктура доставки пакетов – gems. С другой, делать компоненты gem’ами неудобно. Тут нужно еще подумать.

  7. Локализация

    Тут, я думаю, без вопросов. Современная система управления сайтами, заточенная под один язык – это бред. Загрузку локализаций для компонентов делает либо ядро, либо сами компоненты. Кстати, буквально сегодня нашел плагин для редактирования локализаций через web-интерфейс – translate. Можно сделать нечто подобное.

Часть этих идей я уже реализовал в системе управления порталами, однако над ними еще нужно поработать, прежде чем можно будет говорить о формировании готовой системы. Тем не менее, я настроен оптимистично и в принципе даже готов к диалогу с теми, кто может быть заинтересован в разработке такой системы. Со своей стороны я готов предоставить свои наработки.

Буду рад услышать ваши мнения в специальной ветке на ror2ru »

Сервис доставки сообщений (концепт)

Опубликовано 13.12.2008

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

Задача

В моем случае задачу можно сформулировать следующим образом:

  1. Персональные данные пользователя (email, имя, все персональные настройки) хранятся в сервисе Паспорт
  2. Отправка сообщений пользователю должна осуществляться всеми сервисами портала, коих на данный момент 4
  3. Сервисы могут отправлять следующие типы сообщений:
    • Разовые сообщения о системных событиях адресованные конкретному пользователю - регистрация, восстановление пароля, уведомление о блокировке и т.д.
    • Периодические сообщения для группы пользователей - рассылки, подписка на комментарии и прочие
  4. Доставка сообщений может быть как по почте, так и через Jabber, ICQ, SMS и прочие
  5. Должна формироваться очередь отправки сообщений, позволяющая распределять загрузку по времени, чтобы не убивать канал отправки. Например, чтобы не попасть в spam-листы из-за большого количества исходящих сообщений.

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

Сервис сообщений должен состоять из двух частей:

  • Web-интерфейса для добавления сообщений в очередь, регистрации пользователей и подписок
  • Скрипта или демона для отправки сообщений

HTTP-интерфейс

HTTP-интерфейс используется сервисами для выполнения операций с пользователями и сообщениями. Интерфейс должен обладать следующим набором функций:

  • Добавление, изменение и удаление пользователя

    Очевидно, что для пользователя должны передаваться основные параметры, используемые при рассылке (полное имя, email, jabber и другие контактные данные), а так же дополнительные данные, которые могут быть использованы при формировании сообщений. Например, половая принадлежность, город проживания, дата рождения и прочие. Эти данные в Сервис сообщений могут передать различные сервисы. Например, паспорт может передать пол и возраст, а доска объявлений - количество добавленных пользователем объявлений. Данные пользователя привязываются к уникальному системному ID (в моем случае это ID пользователя в Паспорте)

  • Добавление пользователя в листы рассылки и удаление из них

    Фактически, листами рассылки может являться что угодно - почтовая подписка на новые материалы, уведомления о комментариях, сообщения в community, системные рассылки и так далее. Детали о том, что это за рассылка и каким образом она формируется, Сервиса сообщений не касаются - он занимается исключительно отправкой. Поэтому единственное что ему нужно знать о рассылке - это ее уникальный ID. В качестве такового можно использовать, например, комбинацию из названия сервиса и внутреннего ID объекта, к которому привязана рассылка. Например, уведомления о комментариях к статье номер 123 - publishing_article_123_comments, новые объявления раздела Недвижимость - classifieds_section_realty_ads.

  • Отправка персонального сообщения

    Сервис может отправить сообщение пользователю используя его уникальный системный ID. Сервису сообщений передается полный текст сообщения, при необходимости содержащий инструкции для вставки пользовательских данных, уже переданных другим сервисом (например, имени пользователя или его пола). Для обработки тела сообщения можно использовать обработчик шаблонов. Для этого хорошо подойдут Liquid и Radius. Обработка происходит непосредственно перед отправкой

  • Отправка сообщения для листа рассылки

    Сервис может добавить сообщение для листа рассылки, используя его уникальный ID (см. выше). Добавленное сообщение прогоняется через обработчик шаблонов для каждого пользователя, подписанного на рассылку, и результирующее сообщение ставится в общую очередь отправки.

  • Получение списка рассылок, на которые подписан пользователь

    Сервисы должны иметь возможность получить данные о том, на какие рассылки подписан пользователь. Данные используются для работы форм подписки на объекты в сервисах.

Скрипт рассылки

Чтобы разделить добавление и отправку сообщений, стоит вынести непосредственно рассылку в отдельный процесс. Запускать сервис можно как по крону (для небольших объемов сообщений), так в виде демона. Скрипт должен выполнять ряд функций, которые стоит вынести из HTTP-интерфейса:

  • Отправка сообщений

    Скрипт должен пачками забирать сообщения из очереди, отмечать их как находящиеся на обработке, а затем поочередно их отправлять используя указанные средства доставки - email, Jabber, ICQ или другие. В случае успешной отправки сообщения отмечаются как отправленные, в случае неудачи результат тоже сохраняется. Объем пакетов должен регулироваться для каждого средства доставки индивидуально.

  • Формирование очереди рассылок

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

  • Подключаемые средства отправки

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

Технические средства

Для реализации HTTP-интерфейса можно использовать Rails или один из альтернативных Ruby-фреймворков. Для скрипта рассылки - rake (для запуска через cron), BackgroundRB, delayed_job или rudeq.

Что думаете по этому поводу?

Кофейные ссылки #2008-12-13

Опубликовано 13.12.2008

Нет ничего приятнее, чем хлебнуть кофейку суботнним утром. Сегодня ссылки у нас исключительно русскоязычные:

Кофейные ссылки #2008-12-11

Опубликовано 11.12.2008

Давненько не было у нас выпусков Кофейных ссылок, пора восстанавливать справедливость. Интересное на сегодня:

Кратко о проектах

Опубликовано 09.12.2008

Я последние несколько месяцев занимаюсь разработкой системы управления информационными порталами. Буквально несколько дней назад открыли в общий доступ один из клиентских проектов - портал МедиаЗавод, дочерний проект ООО “ЧР-Менеджер” (газеты Челябинский Рабочий, Тумба и еще несколько газет Челябинской области).

Проект сделан на рельсах, используется Single Sign-on для единой авторизации на всех сервисах. Всего в портал пока что включены 4 сервиса - система публикаций, доска объявлений, паспорт и баннерная система. Сервисы включают от 4 до 10 desert-модулей. В качестве JavaScript-библиотеки используется jQuery.

В системе публикаций используется упрощенная разметка Textile. Система администрирования интегрируется напрямую в дизайн, управляющие элементы при авторизации появляются прямо на страницах портала.

Развертывается все под nginx+passenger, используется кэширование в статику и вставка блоков через SSI. Сессии хранятся в memcache, через него же передается единая авторизацая.