如何使用 ruby​​gems 构建模块化命令行界面?

发布于 2024-12-01 23:40:43 字数 458 浏览 1 评论 0原文

我编写了一个命令行工具,用于操作名为“Scaffolder”的基因组支架。目前我想要使用的所有工具都被硬编码到库中。例如,这些工具“验证”或“构建”脚手架。我想将这些工具拆分成自己的 gem,使其更加模块化,并允许第三方编写自己的命令。

理想的情况是我运行“gem installscaffolder-validate”,然后这个 gem 捆绑的命令将作为脚手架的一部分可用。我知道有几个库可以轻松构建命令行界面:thor、commander、gli……但是我不认为它们中的任何一个都适合这种类型的功能。

我的问题是如何使用 gem 结构来创建用于安装这些命令的模块结构?具体来说,如何自动检测并加载已安装的命令?在gem名称scaffolder-*中添加一些前缀然后搜索rubygems?我如何用黄瓜测试这个?

I've written a command-line tool for manipulating with genome scaffolds called "Scaffolder". At the moment all the tools I want to use are hard-coded into the library. For instance these tools "validate" or "build" the scaffold. I'd like to split these tools out into their own gems, make it more modular, and to allow third parties to write their own commands.

The ideal case would be that I run "gem install scaffolder-validate" and this gem-bundled command would then be available as part of scaffolder. I know a couple of libraries make it easy to build a command-line interface: thor, commander, gli, .... However I don't think any of them cater for this type of functionality.

My question is how can I use a gem structure to create a module structure for installing these commands? Specifically how can the installed commands be auto-detected and loaded? With some prefix in the gem name scaffolder-* then searching rubygems? How could I test this with cucumber?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

£噩梦荏苒 2024-12-08 23:40:43

因此,您可以做的一件事就是为您的插件确定一个规范名称,然后使用该约定来动态加载内容。

看起来您的代码都在模块 Scaffolder 下,因此您可以按照以下规则创建插件:

  • Scaffolder gems 必须命名为 scaffold-tools-plugin-pluginname
  • 所有插件都公开一个名为 Scaffolder::Plugin::Pluginname
  • 类,该类必须符合您记录的某个接口,并可能提供一个基类 鉴于

此,您可以接受要加载的插件的命令行参数(假设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是一个<配置插件的类对象的 code>Array。假设它们都符合一些通用的构造函数和一些通用的方法:

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

显然,在进行大量这样的动态类加载时,您需要小心并信任您正在运行的代码。我假设对于这么小的域,这不会是一个问题,但只是要警惕需要随机代码。

So, one thing you can do is to decide on a canonical name for your plugins, and then use that convention to load things dynamically.

It looks like your code is all under a module Scaffolder, so you can create plugins following the following rules:

  • Scaffolder gems must be named scaffold-tools-plugin-pluginname
  • All plugins expose one class, named Scaffolder::Plugin::Pluginname
  • That class must conform to some interface you document, and possibly provide a base class for

Given that, you can then accept a command-line argument of the plugins to load (assuming OptionParser):

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

Then:

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

Now plugin_classes is an Array of the class objects for the plugins configured. Supposing they all conform to some common constructor and some common methods:

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

Obviously, when doing a lot of dynamic class loading like this, you need to be careful and trust the code that you are running. I'm assuming for such a small domain, it won't be a concern, but just be wary of requireing random code.

毁梦 2024-12-08 23:40:43

嗯,棘手的一个。我的一个简单想法是,主 gem 只是尝试需要所有其他宝石,并在它们不存在时捕获加载错误并禁用相应的功能。我在我的一颗宝石中这样做。如果 HighLine 存在,系统会提示用户输入密码,如果不存在,则必须输入密码一个配置文件。

begin
  require 'highline'
rescue LoadError
  highline = false
end

如果你有很多宝石,这可能会变得丑陋......

Hm, tricky one. One simple idea I have is that the main gem just tries to require all the others and catches the load error when they are not there and disables the respective features. I do this in one of my gems. If HighLine is present, the user gets prompted for a password, if it isn't there has to be a config file.

begin
  require 'highline'
rescue LoadError
  highline = false
end

If you have a lot of gems this could become ugly though...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文