Подкоманды 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
Другие вопросы по тегам