Есть ли команда для добавления драгоценного камня в gemspec?

Я знаю, что есть команда для добавления драгоценного камня в Gemfile: bundle add GEMNAME, но есть ли аналогичная команда для добавления зависимости в gemspec?

Есть что-то вроде bundle add rails --gemspec? Есть ли команда, которая добавляет эту строку в gemspec?:

  spec.add_dependency 'GEMNAME', 'GEMVERSION'

🤔 А знаете ли вы, что...
Ruby поддерживает различные парадигмы программирования, включая функциональное программирование.


106
2

Ответы:

Решено

Я думаю, что ответ «нет». По крайней мере, не с помощью «стандартных» инструментов Ruby; Возможно, кто-то создал для него CLI, но я не могу его найти.


Я почти никогда не использовал команду bundle add. Я ожидаю, что в большинстве случаев использования это лишь немного быстрее, чем редактирование Gemfile и запуск bundle, с дополнительным недостатком, заключающимся в том, что вам нужно запомнить эту специальную команду (и, вероятно, еще проверить, что она внесла правильные изменения в Gemfile!).

Возможно, было бы полезно, если бы я добавлял одну и ту же зависимость к дюжине проектов или чему-то еще, но для меня это не «обычная» задача.

Точно так же я не могу себе представить сценарий, в котором использование CLI для обновления gemspec будет действительно полезным. Неужели так же быстро, если не быстрее, просто открыть gemspec и отредактировать его напрямую?

Вот почему (насколько я могу найти?) не существует популярного инструмента для автоматизации этого процесса.


Кстати, команда bundle gem <gemname> очень полезна при создании нового драгоценного камня, поскольку она автоматизирует большое количество шаблонного кода для запуска новой библиотеки, аналогично запуску rails new <app-name>.


ТЛ;ДР

Ваш вопрос предполагает, что Gem::Specification, хранящийся в файле .gemspec, должен содержать тот же уровень информации о зависимостях, что и Gemfile, анализируемый Bundler. Обычно это не так, и, вероятно, именно поэтому отсутствует поддержка, которую вы хотите.

Вы правы, в настоящее время такой поддержки нет. Тем не менее, я привожу примеры переноса минимального набора зависимостей из Gemfile в вашу Gem::Specification, а также некоторую пищу для размышлений о том, как и зачем сохранять в Gemfile столько управления зависимостями, сколько это разумно для вашего конкретного пакета или библиотеки.

Изменения CLI в файле .gemspec не поддерживаются

Я не видел опции CLI, похожей на Bundle-add на сайте RubyGems Command Reference для добавления строк непосредственно в файл .gemspec из командной строки, но синтаксис драгоценных камней в gemspec очень похож если только вы не используете более продвинутый синтаксис от Bundler.

Однако различия для простейших случаев тривиальны, и, вероятно, поэтому они не поддерживаются. Ниже я привожу несколько примеров того, как их конвертировать с помощью sed или текстового редактора.

Кроме того, любой драгоценный камень, который, скорее всего, будет включен в виде библиотеки, а не исполняемого файла, технически может не нуждаться в определенных зависимостях времени выполнения. Возможно, будет достаточно (хотя это не обязательно лучшая практика) включить в ваш .gemspec только зависимости разработки, а остальное оставить Bundler и Gemfile.

Gemfile против Gem::Спецификация

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

Основное различие в спецификациях заключается в том, что Bundler Gemfile поддерживает гораздо больше функций (например, группировку) и может использовать более продвинутые спецификации, чем файл .gemspec. Некоторыми примерами этого являются определение нестандартных операторов require в Gemfile или использование Install_if; это то, с чем Bundler может справиться, а стандартный .gemspec - нет.

Однако в более простых случаях использования синтаксис спецификации двух форматов файлов достаточно эквивалентен, и вы можете использовать sed или текстовый редактор, чтобы внести незначительные изменения, необходимые для переноса файлов из вашего Gemfile в ваш Gem::Specification.

Пример преобразования спецификаций Gemfile в Gem::Specification

В несложных случаях можно сделать что-то вроде этого:

  1. Скопируйте и вставьте строки из Gemfile в .gemspec.
  2. Измените строку Bundler gem, чтобы она соответствовала синтаксису Gem::Specification.
    1. В vim: :%s/^gem/spec.add_dependency/ для общих зависимостей.

    2. Вы можете сделать то же самое из командной строки с помощью GNU sed:

      # for items in your global group
      sed -ri "s/^gem/spec.add_dependency/" .gemspec
      
      # for items your Gemfile's development group
      sed -ri "s/^gem/spec.add_development_dependency/" .gemspec
      
      # for items in your various runtime groups
      sed -ri "s/^gem/spec.add_runtime_dependency/" .gemspec
      

      Примечание. #add_dependentness прагматично является синонимом #add_runtime_dependentency, но в большей степени раскрывает намерения. Вы можете использовать любой из них, в зависимости от ваших семантических намерений, но он ничем не отличается от Бандлера.

Поскольку драгоценный камень обычно определяет свои собственные операторы require в файле gem_name/lib/gem_name.rb, отсутствие сложности в файле .gemspec редко является реальной проблемой. В таких случаях, перечислив зависимости разработки в Gem::Specification, а затем полагаясь на свой Gemfile или Gemfile.lock (были споры о включении или исключении Gemfile.lock из библиотек, но большинство людей в настоящее время, похоже, включают его), тогда вы может рассматривать .gemspec как информационный и позволить Bundler управлять реальными зависимостями, группами, условиями и другими причудливыми вещами перед сборкой и отправкой драгоценного камня.

Примеры

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

# foo.gemspec
spec.add_dependency "bundler"
spec.add_development_dependency "standardrb"
# Gemfile
gemspec

group :development, :test do
  gem "standard", "~> 1.36"
end

В этом примере я сообщаю RubyGems, что некоторая версия Bundler является основной зависимостью и что для разработки потребуется Standardrb (мета-гем для стандартного гема). Я мог бы указать ограничение для Bundler, если мне нужно, но обычно я этого не делаю, особенно в библиотеках, а стандартному Rb оно на самом деле не нужно, потому что вместо этого я добавлю все необходимые ограничения для стандартного драгоценного камня или других конкретных зависимостей в Gemfile. .

Между тем, в Gemfile метод gemspec извлекает Standardrb и ​​его зависимости, а группы :development и :test включают как сам стандартный драгоценный камень, так и его зависимости. Я делаю это, потому что Gem::Specification#add_development_dependentity назначается только одной группе (обычно :development, но настраивается в Bundler), тогда как сам Bundler может добавлять элементы в несколько групп или список групп.

Спецификация Standard внутри группы ограничивает Standard до второстепенной семантической версии, которую я определил (например, 1.36). Затем распознаватель Bundler обрабатывает зависимости и для этой конкретной версии Standard!

Обычно гораздо удобнее делать подобные вещи в Bundler, чем в Gem::Specification, но ваш опыт и вариант использования могут отличаться. Тем не менее, это разумный шаблон, который не только решает вашу проблему, убирая лишний вес в вашем файле .gemspec и упрощая определение минимально необходимых метаданных о зависимостях вашего драгоценного камня, но также предоставляет в ваше распоряжение всю мощь Bundler при необходимости. стоимость наличия Gem::Specification, которая не точна.

Популярный драгоценный камень в дикой природе

Чтобы разобраться во всем этом, обратите внимание, что в основном репозитории RubyGems вы найдете множество драгоценных камней, версии которых Gem::Specification указаны как >=0 или которые просто перечисляют их зависимости во время выполнения или разработки. Например, в RSpec 3.13.0 перечислены только три зависимости времени выполнения:

rspec-core ~> 3.13.0
rspec-expectations ~> 3.13.0
rspec-mocks ~> 3.13.0

и необходимая версия Ruby >= 0. Это может быть или не быть идеальным, но перечисление в Gem::Specification только зависимостей времени выполнения или разработки является на удивление распространенным явлением. Помимо метаданных о самом драгоценном камне, Bundler предлагает больше опций конфигурации и, возможно, более мощные возможности разрешения зависимостей, поэтому часто выгодно указывать в Gem::Specification как можно меньше о зависимостях драгоценного камня, а все остальное оставить Gemfile Bundler.