По историческим причинам, утилита gem не умеет управлять зависимостями конкретного проекта. С ее помощью нельзя легко указать зависимости, поставить их одной командой и разделить на основные зависимости и зависимости для разработки. Поэтому ее, в основном, используют для установки гемов, которые нужны в системе глобально, например линтера.
Для полноценного управления зависимостями используется гем bundler, который включен в RubyGems и поставляется с ним по умолчанию. В этом уроке, мы научимся подключать его к проекту и разберемся с некоторыми особенностями работы.
Установка зависимостей
Работа с Bundler начинается с создания файла Gemfile в котором указываются зависимости. Сам файл использует Ruby-код для своего описания:
# В реальности тут вызываются функции
# source(), gem() и тому подобные
# Источник гемов
source 'https://rubygems.org'
gem 'rails'
gem 'rack'
gem 'puma'
gem 'ransack'
Когда в файл добавлены зависимости, нужно выполнить их установку:
# В директории с Gemfile
bundle install
Во время установки bundler создаст файл Gemfile.lock, в котором фиксируются версии всех зависимостей включая транзитивные. Любой последующий запуск команды install будет ориентироваться на версии указанные в этом файле. Сам lock-файл должен храниться в git-репозитории.
Благодаря lock-файлу, в Ruby не принято указывать версии гемов в Gemfile, они и так зафиксированы. Технически указать версии можно и иногда это бывает важно, когда, по какой-то причине нужно зафиксировать версию:
# ~> означает что фиксируется major и minor, а patch может обновиться
# https://semver.org/
gem 'rails', '~> 6.1'
Для обновления зависимостей используется команда bundle update
:
bundle update
Добавляют гемы прямым прописыванием в файле Gemfile. В Bundler есть команда добавления гема, но она мало полезна из-за того, как Bundler работает с разделением сред.
Группировка гемов
В Bundler нет предустановленного разделения на продакшен- и девелопмент-зависимости, как, например, в JavaScript. Единственное, что он предоставляет, это механизм группировки гемов.
# Гемы указанные вне групп ставятся всегда
gem 'rails'
gem 'pg'
# имя группы выбирается произвольно
# стандартных имен групп нет
group :development do
gem 'rubocop'
gem 'solargraph'
gem 'spring'
end
group :test do
gem 'capybara'
gem 'webdrivers'
end
# Можно даже так
# Зависимости которые будут использоваться
# и для :test среды и для :development
group :test, :development do
gem 'capybara'
gem 'webdrivers'
end
Группировка - обобщенный механизм, с его помощью можно создавать любое количество сред, в которых мы хотим запускать код. В примере выше есть не только продакшен (все что вне групп) и разработка, но и группа для тестов. Эти зависимости используются и подключаются только во время запуска тестов.
По умолчанию команда bundle install
устанавливает все гемы, включая те что указаны внутри групп. Чтобы установить только конкретную группу, ее можно указать явно:
# Так
bundle install --with development
# Или так
bundle install --with test,development
Гемы указанные вне групп будут установлены в любом случае, так как, по смыслу, они нужны всем.
Сами гемы устанавливаются в стандартные пути RubyGems, а не локально в проект. Это позволяет экономить место если проектов на Ruby много, но не подходит в том случае, если мы работаем, например, в Docker. Поэтому путь для установки гемов иногда меняют на vendor/bundle.
bundle config --local path vendor/bundle
Эта команда создаст файл .bundle/config, в котором будет указан путь установки. Bundler использует эту конфигурацию во время своей работы.
Интеграция в проект
Bundler появился намного позже чем RubyGems, поэтому он не включается автоматически. Для подключения Bundler, во входном файле нужно добавить сверху такой код:
require 'rubygems'
# Настраивает LOAD_PATH
require 'bundler/setup'
# Теперь можно подключать установленные гемы
require 'rails'
# Здесь код
В этом случае в пути загрузки добавятся все гемы из всех групп. Если нужно указать конкретные группы, то код меняется на такой:
require 'rubygems'
require 'bundler'
# Внутрь передается список групп
# :default - гемы вне групп
Bundler.setup(:default, :ci)
require 'nokogiri'
Когда гемов становится много, то постоянные включения зависимостей начинают сильно разрастаться. Bundler может взять эту работу на себя и включить все нужные зависимости сразу во время настройки:
require 'rubygems'
require 'bundler'
Bundler.require(:default, :test)
# Теперь гемы доступны напрямую из всех файлов проекта
# Этот способ подключения может замедлять старт приложения
В целом, прямая работа с подключением Bundler нужна редко. В прикладных проектах типа Rails, весь процесс инициализации скрыт от программиста. А для библиотек используется автоматическая генерация кода.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.