问题 Ruby插件架构


我想要一个基本的小程序示例,它读入两个插件并注册它们。这两个插件以无冲突的方式以相同的方式挂钩到基本程序中。

我对任何编程语言中的元编程都很陌生,我不知道从哪里开始。


8259
2018-06-24 18:07


起源

很抱歉迂腐,但不是与Rails框架相关的插件,而不是ruby本身? - Andrew Grimm
rails框架有插件,但我在谈论在ruby应用程序的上下文中创建插件系统。所以说你有一个读取文本文件的类。我想创建一个插件系统,让你编写一个插入读者类的插件类,以便你可以说,一个UpCase插件或一个修改输出的1337插件。这是一个基本的例子,但这应该给你一个想法。
ruby比rails更重要,即使rails是最着名的框架。说插件与轨道相关就像是说汽油与跑车有关。还有一个使用汽油的世界,包括非机动车辆。 - Derick Bailey
@Derick:我实际上是在问,因为我不使用Rails(只是简单的旧Ruby对象),并且没有遇到过插件。 - Andrew Grimm


答案:


我一直在研究这个问题已经有一段时间了。我已经尝试了很多不同的方法来做这件事并向很多人寻求建议。我仍然不确定我拥有的是“正确的方式”,但它运作良好并且很容易做到。

在我的情况下,我特别关注配置并引入配置插件,但即使我的术语特定于配置,原理也是一样的。

在一个非常基本的层面上,我有一个没有任何内容的Configuration类 - 它是空的。我还有一个Configure方法,它返回配置类,并允许您调用它的方法:

# config.rb
class Configuration
end

class MySystem
  def self.configure
    @config ||= Configuration.new
    yield(@config) if block_given?
    @config
  end

  Dir.glob("plugins/**/*.rb").each{|f| require f}
end

MySystem.configure do |config|
  config.some_method
  config.some_value = "whatever"
  config.test = "that thing"
end

puts "some value is: #{MySystem.configure.some_value}"
puts "test #{MySystem.configure.test}"

为了获得配置类上的some_method和some_value,我让插件通过模块扩展配置对象:

# plugins/myconfig.rb
module MyConfiguration
  attr_accessor :some_value

  def some_method
    puts "do stuff, here"
  end
end

class Configuration
  include MyConfiguration
end

# plugins/another.rb
module AnotherConfiguration
  attr_accessor :test
end

class Configuration
  include AnotherConfiguration
end

要加载插件,您只需要一个代码来查找特定文件夹中的.rb文件并“需要”它们。只要在包含它的文件被加载时它立即运行,这个代码可以存在任何地方...我可能会把它放在MySystem的类定义或类似的东西开始。当有意义时,可能会把它移到其他地方。

Dir.glob("plugins/**/*.rb").each{|f| require f}

运行config.rb,你会得到如下所示的输出:

做点什么,这里
一些价值是:无论如何
测试那件事

有很多选项来实现这个的各个部分,但这应该让你走上正轨。


9
2018-06-24 19:24





看起来这个项目可能会有所帮助: https://github.com/eadz/plugman

但是,我没有找到任何可以处理嵌入式(gem)依赖关系的东西。包含Ruby文件很简单,但是一旦你的插件开始需要其他库,那么这个简单的模型就会崩溃(要么所有的deps必须与你的应用程序一起安装,要么你需要一些其他的机制来将插件依赖性gems带入进程)。


2
2018-01-26 05:16