После перехода перехода с PHP на Ruby on Rails и отказа от изобретения собственных велосипедов, у меня появилась новая страсть, которую необходимо как-то контролировать - встраивание чужих велосипедов в свою систему. При создании Rails-приложений довольно сложно обойтись без использования сторонних библиотек. Сложно не потому, что эти библиотеки обязательны, а потому, что грех не использовать такие замечательные вещи, как acts_as_authenticated, better_nested_set, acts_as_dropdown, rspec и многие другие.

Создатели языка Ruby и фреймворка Rails на славу потрудились над общей схемой использования чужого кода в проектах. В большинстве случаев достаточно просто установить gem на сервере или скопировать необходимый плагин в папку vendor/plugins. Однако, даже при такой упрощенной схеме есть тонкие моменты, которые видны не сразу и могут проявиться в самый неподходящий момент.

В одном из наших проектов используется около десятка сторонних библиотек, включая Edge Rails, подключенных через svn:externals. Для тех кто слабо знаком с тонкостями работы Subversion, я поясню: такая схема подключения означает, что в локальном репозитории хранится только код приложения, а сторонние библиотеки упоминаются исключительно в виде ссылок на сторонние репозитории. При скачивании последней актуальной версии кода приложения SVN-клиент автоматически подключается к внешним репозиториям библиотек и скачивает оттуда последнюю актуальную версию.

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

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

Немножко порывшись в книжках и статьях по Subversion, я откопал замечательный способ - отдельный локальный репозиторий под внешние библиотеки. Делается это так:

  1. Создаем репозиторий, например с именем vendor, на том же сервере, где вы храните основной репозиторий приложения.
  2. В репозитории создаем папки под каждую из сторонних библиотек:

     -+-vendor
      |-acts_as_dropdown
      |-netter_nested_set
      |-rails
    
  3. Выкачиваем необходимые версии библиотек на свой локальный компьютер через svn checkout

  4. В репозитории в папках библиотек создаем папки с названиями, соотвествующими номерам текущих ревизий выкачаных библиотек (можно посмотреть с помощью команды svn info в папке библиотеки):

     -+-vendor
      |-acts_as_dropdown
      |  +-12
      |-better_nested_set
      |  +-127
      |-rails
         +-8557
    
  5. Импортируем код из локальных папок в репозиторий в соответствующие папки с номерами версий

  6. Делаем ветки от папок с номерами версий в папку current для каждой библиотеки:

     -+-vendor
      |-acts_as_dropdown
      |  +-12
      |  +-current
      |-better_nested_set
      |  +-127
      |  +-current
      |-rails
         +-8557
         +-current
    

Собственно, это все. С этого момента уже можно начинать использовать локальный репозиторий в своих целях. Для этого нужно в проектах поменять ссылки svn:externals с внешних репозиториев на соответствующие папки current локального репозитория. При следующем обновлении библиотеки будут тянуться уже из локального репозитория.

Если вам потребуется обновить какую-то библиотеку до последней актуальной версии, то достаточно обновить локальные папки с библиотеками командой svn update (или заново выкачать эти библиотеки через svn checkout) и повторить пункты 4-6:

    -+-vendor
     |-acts_as_dropdown
     |  +-12
     |  +-15
     |  +-current
     |-better_nested_set
     |  +-127
     |  +-131
     |  +-current
     |-rails
        +-8557
        +-8560
        +-current

На мой взгляд, схема более чем удобная. Как итог мы получаем сразу ворох выгод: 1. Держим все библиотеки в своем кармане и не зависим от сторонних источников 2. Можем обновлять версии библиотек одновременно во всех проектах используя папки current в качестве основных источников 3. Можем использовать строго определенную версию библиотеки, ссылаясь на папку, соответствующую нужному номеру версии 4. Можем модернизировать исходный код библиотек и использовать его независимо от того, принимает автор патчи к своим библиотекам или нет

Удачного чекаута!

Полезные ссылки: * Бесплатная книга по Subversion * Ставим Subversion за 5 минут * Клиенты для Subversion (тем, кто пользуется Eclipse, я рекоммендую использовать Subclipse) * Хостинг для Subversion