Как я могу построить модульный интерфейс командной строки, используя rubygems? - PullRequest
0 голосов
/ 31 августа 2011

Я написал инструмент командной строки для манипуляции с каркасами генома под названием "Scaffolder" .На данный момент все инструменты, которые я хочу использовать, жестко запрограммированы в библиотеке.Например, эти инструменты «проверяют» или «строят» эшафот.Я хотел бы разделить эти инструменты на их собственные гемы, сделать их более модульными и позволить третьим сторонам писать свои собственные команды.

В идеальном случае я бы запустил "gem install scaffolder-validate"и эта команда в комплекте с самоцветами будет доступна как часть scaffolder.Я знаю, что несколько библиотек позволяют легко создавать интерфейс командной строки: thor, commander, gli, .... Однако я не думаю, что какая-либо из них предназначена для такого типа функций.

MyВопрос в том, как я могу использовать структуру gem для создания структуры модуля для установки этих команд?В частности, как установленные команды могут быть автоматически обнаружены и загружены?С каким-то префиксом в имени самоцвета scaffolder- * затем поискать rubygems?Как я могу проверить это с огурцом?

Ответы [ 2 ]

1 голос
/ 08 сентября 2011

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

Похоже, что весь ваш код находится под модулем Scaffolder, поэтому вы можете создавать плагины, следуя следующим правилам:

  • Scaffolder драгоценные камни должны быть названы scaffold-tools-plugin- pluginname
  • Все плагины предоставляют один класс с именем Scaffolder::Plugin:: Имя плагина
  • Этот класс должен соответствовать некоторому интерфейсу, который вы документируете, и, возможно, предоставлять базовый класс для

Учитывая это, вы можете принять аргумент командной строки загружаемых плагинов (при условии OptionParser):

plugin_names = []
opts.on('--plugins PLUGINS','List of plugins') do |plug|
  plugin_names << plug
end

Тогда:

plugin_classes = []
plugin_names.each do |plugin_name|
  require "scaffold-tools-plugin-#{plugin_name}"
  plugin_classes << Kernel.const_get("Scaffold::Plugin::#{plugin_name}")
end

Теперь plugin_classes - это Array объектов класса для настроенных плагинов. Предположим, что все они соответствуют некоторому общему конструктору и некоторым распространенным методам:

plugin_classes.each do |plugin_class|
  plugin = plugin_class.new(args)
  plugin.do_its_thing(other,args)
end

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

1 голос
/ 31 августа 2011

Хм, хитрый.У меня есть одна простая идея: основной драгоценный камень просто пытается require всех остальных и ловит ошибку загрузки, когда их там нет, и отключает соответствующие функции.Я делаю это в одном из моих драгоценных камней.Если присутствует HighLine , пользователю будет предложено ввести пароль, в противном случае должен быть файл конфигурации.

begin
  require 'highline'
rescue LoadError
  highline = false
end

Если у вас много драгоценных камней, это можетхотя и становится некрасивым ...

...