После перехода перехода с 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, я откопал замечательный способ - отдельный локальный репозиторий под внешние библиотеки. Делается это так:
- Создаем репозиторий, например с именем
vendor, на том же сервере, где вы храните основной репозиторий приложения.
В репозитории создаем папки под каждую из сторонних библиотек:
-+-vendor
|-acts_as_dropdown
|-netter_nested_set
|-rails
Выкачиваем необходимые версии библиотек на свой локальный компьютер через svn checkout
В репозитории в папках библиотек создаем папки с названиями, соотвествующими номерам текущих ревизий выкачаных библиотек (можно посмотреть с помощью команды svn info в папке библиотеки):
-+-vendor
|-acts_as_dropdown
| +-12
|-better_nested_set
| +-127
|-rails
+-8557
Импортируем код из локальных папок в репозиторий в соответствующие папки с номерами версий
Делаем ветки от папок с номерами версий в папку 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
На мой взгляд, схема более чем удобная. Как итог мы получаем сразу ворох выгод:
- Держим все библиотеки в своем кармане и не зависим от сторонних источников
- Можем обновлять версии библиотек одновременно во всех проектах используя папки
current в качестве основных источников
- Можем использовать строго определенную версию библиотеки, ссылаясь на папку, соответствующую нужному номеру версии
- Можем модернизировать исходный код библиотек и использовать его независимо от того, принимает автор патчи к своим библиотекам или нет
Удачного чекаута!
Полезные ссылки: