Почему всё-таки стоит пользоваться RVM, а не rbenv+ruby-build

Original author: Michał Papis
  • Translation
В противовес к этой статье. Мне удалось немного покопаться с rbenv, наткнуться на несколько граблей, и решить копнуть поглубже. И очень ко времени попалась на глаза вот эта статья, перевод которой я на ваше обозрение и предоставляю.

На сегодняшний день уже не осталось тех, кто не слышал о RVM или rbenv. Как содействовавший разработке RVM, я подумал, что стоит выяснить, что именно делает rbenv. Чтение исходного кода дало мне представление об rbenv, и, к сожалению, я не нашёл никакого нового трюка для работы в оболочке командной строки. Тем не менее, я понимаю, как обе этих утилиты работают, и могу найти разницу в их работе. Обе утилиты предоставляют возможность переключать активную версию Ruby.

Основная разница состоит в способе переключения Ruby.

Для RVM всё происходит в момент смены текущей директории, только один раз, и в тот самый момент все исполняемые файлы и gem'ы Ruby становятся доступны. Можно также запустить эту процедуру вручную.
Для rbenv, все исполняемые файлы Ruby и gem всегда доступны в оболочке, но загрузят нужный код только в подходящем контексте, то есть вычисление происходит каждый раз при запуске исполняемого файла.
Глубже в детали: RVM потратит 60мс на каждый запуск cd, что составит секунду времени на 16 смен директории. Во время смены директории, RVM установит все переменные окружения таким образом, чтобы те указывали на текущую версию Ruby и текущий gemset, позволяя отделить их от других версий Ruby, gem'ов и исполняемых файлов. Вы не сможете запустить исполняемый файл, который относится к gem'у другого проекта.

Для rbenv смена директоий не несёт временных затрат, то есть в вопросе смены директорий rbenv быстрее. После установки новых gem'ов потребуется 60мс для запуска rbenv rehash. Смена текущей версии Ruby или смена директории не меняет окружение, кроме переменной RBENV_VERSION, но при этом каждый раз при запуске исполняемого файла будет затрачено 50мс на вычисление обёрткой того, какой же именно файл нужно исполнить для текущей версии Ruby и gemset'а.

Чтобы подробнее объяснить, что же именно делает rbenv, вот цитата моего сообщения на reddit:
Есть один большой недостаток в способе с обёртками — все исполняемые файлы и бинарные файлы Ruby и gem'ов всегда доступны в оболочке, что не всегда удобно.
Если, например, HAML, установлен для одной версии Ruby, его исполняемый файл будет доступен всем остальным версиям.
Способ с обёртками работает вопреки системе — строить абстракцию, не уважая принципы работы UNIX, такие как поиск по PATH, это одурение системы (и вас).
Невозможно запустить обёртку/Ruby без rbenv, который принципиально необходим, чтобы окружение работало так, как ожидается. RVM же, наоборот, по умолчанию строит окружение так, что система понимает, как должна работать без вмешательства RVM.


Окружение готово к запуске исполненяемых файлов в обоих случаях, но слегка по разному для рассматриваемых утилит:
— есть разница в моменте загрузки окружения. Для RVM можно запустить 'rvm info', для rbenv запускать нечего; (прим перев. на самом деле можно запустить 'rbenv version')
— для RVM только выбранные и необходимые Ruby/gem исполняемые файлы будут доступны в оболочке. Для rbenv все исполняемые файлы будут доступны, только некоторые из них не будут ничего делать при запуске; нет возможности проверить, доступен ли исполняемый файл в системе, так как проверка будет наталкиваться на обёртку и сообщать о её существовании;
— тем, кого беспокоит время загрузки и исполнения файлов, не заметят особой разницы, её можно лишь измерить. Задержки менее 300мс не заметны для пользователя оболочки (прим. перев. есть мнение, что 150мс, но обе утилиты укладываются и в этот диапазон тоже).

Автор rbenv, Сэм Стивенсон, приводит различия между утилитами, критикуя RVM, давайте рассмотрим их:
1. Необходимость загрузки в оболочку. Напротив, способ rbenv с обёртками работает, добавляя папку в ваш PATH. RVM также нет необходимости загружать в оболочку. Он позволяет работать лишь загружая файл окружения, что происходит только раз и позволяет единожды и быстро проинициализировать текущую версию Ruby и gemset.

2. Перегружает команды оболочки, такие как cd. Это опасно и может привести к ошибкам. Перегрузка cd опциональна. Я искал суммарно почти 8 часов за последний месяц, чтобы найти проект, который перегружает cd — и что вы думаете? Не нашёл. В любом случае, RVM предоставляет возможность смотреть на тот код, который будет исполнен при запуске cd до момента его исполнения и выбирать, доверять ему или нет.

3. Имеет конфигурационный файл. Нечего конфигурировать, кроме версии Ruby, которую вы хотитие использовать. rbenv в настоящий момент может выставлять до 4х переменных оболочки, которые влияют на запущенные процессы, и нет конфгурационного файла для их настройки, все они должны быть настроены в rc файлах, по одному на каждый тип оболочки из тех, что вы используетет. (прим перев. не понимаю, о каких четырёх переменных идёт речь, зачем тут нужен конфигурационный файл, и как смена версии Ruby в одной из сессий может повлиять на запущенные процессы, и почему автор не знает, что для всех оболочек можно использовать один общий файл, но мнение есть мнение).

4. Устанавливает Ruby. Вы можете установить Ruby самостоятельно, или использовать ruby-build для автоматизации процесса. За своё долгое существование RVM собрал немалый багаж знаний о том, как устанавливать и управлять версиями Ruby, включая патчи для различных окружений.

5. Управляет gemset'ами. Bundler — лучший способ управлять зависимостями приложения. Если проект ещё не использует Bundler, есть возможность установить плагин rbenv-gemset. Использование gemset'ов опционально, но рекомендовано, так как оно упрощает процесс разделения. Само существование плагина rbenv-gemset только подтверждает это. Даже могучий Bundler не всегда может разобраться в запутанных отношениях gem'ов. Ко всему прочему, не всегда удобно запускать Rake вызывая 'bundle exec rake'.

6. Требует внесения в библиотеки Ruby для совместимости. Простота rbenv позволяет лишь держать его в PATH, больше ничего не требуется. RVM не требует изменений в gem'ах и библиотеках.



В ответ Сэму Стивенсону я дам несколько контраргументов против использования rbenv:
1. Выполнение исполненяемого файла на 50мс медленнее, чем для RVM, так что если вы используете много вызовов Ruby исполняемых файлов, можете получить выигрыш с RVM.
2. Выполнение исполненяемого файла не требует, чтобы нужный исполняемый файл был доступен, оно втихую сорвётся без единого предупреждения, что файл не доступен.
3. Невозможно проверить наличие того или иного исполняемого файла в системе, требуются дополнительные ухищрения. С RVM достаточно настроить окружение — и всё работает в соглашении с идеологией UNIX. С rbenv же окружение обмануто обёртками.

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

Часто упоминается сложность и размер RVM. rbenv — довольно новый проект, и он пока относительно мал и его код относительно прост. С ростом количества возможностей растёт и сложность проекта. Мы с нетерпением ждём, как rbenv вырастет в замечательный продукт, сохраняя простоту, чтобы оба проекта могли заимствовать друг у друга лучшее.

Есть ещё одна вещь, которая стала доступна пользователям OSX, это официальный GUI для RVM, Jewelery Box, спрос на которую оказался велик.

Подводя итог хочется заметить, что мы в курсе, что RVM вырос, и некоторый рефакторинг бы не помешал тем, кто пытается начать участвовать в его развитии, а также сделать исходный код более читаемым и поддерживаемым. Мы проектируем RVM2 как расширение SM, ещё одного замечательного инструмента от того же автора, Уэйна Сегуина, на который стоит обязательно посмотреть, а чтобы описать который понадобится не одна статья.
Мы слышим наших пользователей громко и отчётливо, и у нас большие планы на будущее RVM.
Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 20

    +1
    >>наткнутся на несколько граблей, и копнуть поглубже. И очень ко времени попалась на глаза вот эта статья, перевод которой я на ваше обозрение и предоставляю.

    Вы взорвали мой мозг.
      +3
      1. RVM хорош на девелоперской машине, но его поддержка в capistrano — это сплошная боль.
      2. Гемсеты нужны для старых приложений на rails 2.x и без bundler (RedMine, например).
      3. Чтобы использовать rbenv в capistrano нужно прописать всего лишь одну строчку:

      set :default_environment, { "PATH" => "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH", "RBENV_VERSION" => "1.9.3-p0" }

      И если на сервере rbenv ещё не установлен, то в capistrano ничего не сломается (в отличии от rvm, который заменяет default_shell).
        0
        у нас production практически весь переезжает на system wide RVM. никаких проблем не отмечено.
          +2
          Я сейчас тоже использую system-wide RVM на всех серверах. Но попробовав на одном rbenv, могу сказать, что настройка и развертывание весьма упрощаются (как по объему работы на серверах, так и по настройке рецептов capistrano). Так что буду мигрировать потихоньку на rbenv.
          +1
          на мой взгляд rvm c capistrano работает просто прекрасно, вот какая настройка в моем проект:

          require 'rvm/capistrano'

          set :ruby_version, '1.9.2'
          set :rvm_ruby_string, "#{ruby_version}@#{application}"
          set :rvm_path, "/usr/local/rvm"
          set :rvm_bin_path, "/usr/local/rvm/bin"
          set :rvm_type, :system

          и это еще много, так как я храню все в /usr/local, если бы я использовал дефолтное положение в $HOME/.rvm то обойтись можно было первыми двумя строчками
            0
            А теперь попробуйте сделать cap deloy:check или cap deploy:setup на сервере без rvm.
              +1
              Ясное дело что rvm с начала нужно поставить. Думаю вы не станете деплоить приложение на сервер на котором, скажем, не установлен ruby. Ну а там где установить возможности нет, например взять всеми любимый Heroku, то потдержка RVM в нем имеется из коробки, чего нельзя сказать о rbenv.
                –2
                Что делает невозможным автоматизацию установки rvm через capistrano. Выполнить наш абстрактный cap rvm:install никак уже не получится. Так же как и любую задачу, где нужно сделать что-то ДО установки rvm.
                  +3
                  А вы ось на сервер тоже через cap ставите, да?
                +6
                Купили суровые сибирские мужики умную японскую бензопилу. Попробовали суровые сибирские мужики японскую умную пилу на березке стройной.
                — Вжик, — сказала умная японская пила.
                — Оооо, — сказали суровые сибирские мужики.
                Попробовали суровые сибирские мужики умную японскую пилу на дубе вековом.
                — Вжжик, — сказала умная японская пила.
                — Ооооо, — сказали суровые сибирские мужики.
                Попробовали суровые сибирские мужики умную японскую пилу на рельсе железной.
                — Хр… хрррр… — сказала умная японская пила.
                — Агааа! — воскликнули суровые сибирские мужики.

                Это я к тому, что зачем запускать деплой на сервере без RVM, если деплой настроен на RVM? Давайте еще на сервере без руби его запустим.
                  0
                  Пока писал длиннную притчу, основную мысль сказали в комменте выше =)
                +1
                Проблема в
                require 'rvm/capistrano'

                Недавно наткнулся на это, пытаясь задеплоить с машины с rbenv на сервер с rvm. Поскольку rvm локально не был установлен, require вылетал с ошибкой. Проще было перевести сервер на rbenv, чем пытаться это исправить.
              +1
              Чтобы добавить rbenv в whenever нужно дописать 3 строчки в config/schedule.rb:

              set :rbenv_root, "/usr/local/rbenv"
              env :PATH, "#{rbenv_root}/shims:#{rbenv_root}/bin:$PATH"
              env :RBENV_VERSION, "1.9.3-p0"


              С rvm так не выйдет.
                +2
                set :job_template, «rvm-shell 1.9.3-p0 -c ':job'»
                  +2
                  Пардон, тэг code забыл
                  set :job_template, "rvm-shell 1.9.3-p0 -c ':job'"
                  +1
                  Даа, это тяжело:

                  set :whenever_command, "bundle exec whenever"
                  set :whenever_environment, defer { stage }
                  set :whenever_identifier, defer { "#{application}_#{stage}" }


                  А в config/schedule.rb у меня вообще не в курсе rvm там или rbenv. Там просто написаны задачи на выполнения :)
                  +1
                  Как я понял, основной аргумент против rbenv — лишние 50 мс для запуска обёртки (почему именно 50? сколько раз нужно запускать исполняемые файлы, чтоб это было хоть сколько-то заметно?). Между тем сложность решения с rvm выше.
                  Кстати, в rvm уже исправили проблему с «висом» mc при переходе в каталог с недоверенным .rvmrc? И исправима ли она в принципе без отключения .rvmrc?
                    +1
                    2. Перегружает команды оболочки, такие как cd. Это опасно и может привести к ошибкам.

                    И это регулярно и приводило к проблемам. И закончилось тем что автор выключил фичу про доверие к новым файлам .rvmrc. Вот коммит github.com/wayneeseguin/rvm/commit/9a9fef58731e822b03789445a859fcdb69e57fff и он был сделан примерно через пару дней после релиза первой версии rbenv.
                      +1
                      5. Управляет gemset'ами. Bundler — лучший способ управлять зависимостями приложения. Если проект ещё не использует Bundler, есть возможность установить плагин rbenv-gemset. Использование gemset'ов опционально, но рекомендовано, так как оно упрощает процесс разделения. Само существование плагина rbenv-gemset только подтверждает это. Даже могучий Bundler не всегда может разобраться в запутанных отношениях gem'ов. Ко всему прочему, не всегда удобно запускать Rake вызывая 'bundle exec rake'.

                      Кого не устраивает писать каждый раз bundler exec могут поставить mpapis.github.com/rubygems-bundler/ и писать после только .
                        0
                        Полезная и удобная штука, однозначно. Вроде как и хак, а вроде как и сахар просто.

                      Only users with full accounts can post comments. Log in, please.