Подкоманды Thor в разных драгоценных камнях
Я видел несколько примеров подкоманд в Ruby, но еще не нашел ни одного, которые бы использовали Thor, я думаю, что возможно, что это невозможно, но я был бы удивлен. То, что я пытаюсь сделать, это иметь программу foo
у него есть свой драгоценный камень, и когда он установлен, его команды можно назвать обычным способом, а затем я хочу отдельный драгоценный камень bar
который регистрируется с foo
, поддерживается в другом хранилище, но устанавливается так, что я могу вызвать его с помощью foo bar
,
Из того, что я могу сказать из документации Thor для того, чтобы foo
быть в курсе bar
это должно было бы сделать это:
desc "bar ...", "do bar"
subcommand "bar", Bar
Но я не хочу foo
знать о bar
и для этого просто загрузить его, когда он вызывается. Я не совсем понимаю, как контролировать, как устанавливаются гемы, так что это может быть ответом, возможно, подкоманды должны следовать некоторым правилам установки, а родительская команда выполняет общую загрузку этого пути. Есть ли шанс, что есть удивительный пример, на который я могу указать?
1 ответ
1. Никаких проблем с Тором
У нас есть жемчужина
foo
, предоставляя только один исполняемый файл с тем же именем
foo
, из этого содержания:
require 'thor'
class FooCli < Thor
desc 'hello', 'print a salutation'
def hello
puts 'hello'
end
end
FooCli.start ARGV
Если я его выполню, я получу такой результат
Commands:
foo.rb hello # print a salutation
foo.rb help [COMMAND] # Describe available commands or one specific command
Нет проблем с добавлением новых подкоманд в исполняемый файл в коде за пределами
foo
драгоценный камень. Давай еще один камень
bar
, предоставляя только один "библиотечный" файл с тем же именем
bar(.rb)
, из этого содержания:
require 'thor'
class BarCli < Thor
desc 'hi', 'print another salutation'
def hi
puts 'hi'
end
end
# Reopen the FooCli class and add the new subcommand
class FooCli < Thor
desc 'bar', 'subcommand added by the foo-bar plugin'
subcommand 'bar', BarCli
end
Единственная реальная проблема - как получить
bar
загружается при выполнении
foo
команда.
2. Решение проблемы с загрузкой кода
2.0 Ручное решение
Начнем с загрузки
bar
вручную, используя
-r
вариант
ruby
переводчик:
$ ruby -r bar path/to/foo
Commands:
foo.rb bar # subcommand added by the foo-bar plugin
foo.rb hello # print a salutation
foo.rb help [COMMAND] # Describe available commands or one specific command
Мы видим, что код в
bar.rb
вступили в силу. Поскольку он был загружен раньше
foo
сам (вот как
-r
вариант работает),
bar
подкоманда появляется в списке команд перед стандартным
foo
подкоманды.
Часть нашей миссии выполнена:
foo
ничего не знает о
bar
, но
bar
удалось добавить подкоманду в
foo
. Очевидным недостатком является то, что пользователь должен явно указать
bar
при каждом призыве
foo
(и обратитесь к
foo
по полному пути к файловой системе, что тоже... не очень удобно.)
2.1 Система плагинов с настройкой
Загрузка
bar
не будет работать "волшебным образом" из коробки всякий раз, когда драгоценный камень доступен, но пользователи
foo
нужно будет где-то указать (например, в файле конфигурации), что
bar
должен быть загружен при выполнении
foo
, и вы добавите к
foo
фрагмент кода, загружающий файл конфигурации и впоследствии требующий указанные там плагины.
У этого есть один общий недостаток с начальным решением, о котором говорилось выше: загрузка
bar
требует вмешательства пользователя, но в этом случае вмешательство требуется, по крайней мере, "один раз в жизни", а не при каждом вызове.
2.2 Магия именования драгоценных камней
Если ты действительно хочешь иметь
bar
загружается автоматически, когда это возможно, но без
foo
Зная (конкретно) об этом, вы можете использовать средства RubyGems для проверки установленных гемов в сочетании с соглашением об именах. В
bar
драгоценный камень не будет называться
bar
, но
foo-bar
, новое имя означает, что это расширение
foo
, а
foo
исполняемый файл будет содержать дополнительный фрагмент кода для поиска всех драгоценных камней, имя которых начинается с
foo-
и загружая их:
Gem::Specification
.select {|gemspec| gemspec.name =~ /^foo-/ }
.collect(&:name)
.uniq # multiple versions of a single gem may be installed at once
.each {|name| require name }
У этого решения тоже есть обратная сторона: если ваш гем предназначен для общего использования и выпущен на Rubygems, у вас нет контроля над людьми, создающими драгоценные камни с именами, начинающимися с
foo-
. Таким образом, пытаясь автоматически загрузить все
foo
плагины, вы можете загружать и выполнять сторонний код, о котором вы никогда не хотели. Хуже того, если возникает такая проблема, пользователь
foo
, даже если ему посчастливилось понять, что происходит, у него нет другого способа предотвратить загрузку оскорбительных
foo-*
драгоценный камень, но удалив его.
Это не как thor
работает. С помощью foo
а также bar
что бы у вас было module foo
с классом Cli
что наследует от Thor
т.е.
class Cli < Thor
В вашем Cli
класс вы бы определить метод bar
Что-то вроде
module foo
class Cli < Thor
desc 'bar', 'do stuff'
def bar
# all the rad stuff I do
end
end
end
Затем вы должны собрать и установить один драгоценный камень foo
который будет доступен после вас rbenv rehash
, Как только они появятся
$ foo bar arg1="bang" arg2="boom"
будет делать все то, что вы сказали.
Вы также можете написать свои команды в отдельных файлах. Вы могли бы иметь lib
структура следующим образом:
lib
| - foo.rb
| - foo
| | cli
| | - bar_cli.rb
| | - baz_cli.rb
В foo.rb
module Foo
module Cli
require_relative 'cli/bar_cli'
require_relative 'cli/baz_cli'
end
class FooCli < Thor
register(Foo::BarCli, 'bar', 'bar', 'bar commands')
register(Foo::BazCli, 'baz', 'baz', 'baz commands')
end
end
В bar_cli.rb
module Foo
class BarCli < Thor
desc 'display', 'Display the bar'
def display
# bar display stuff
end
end
end