在 rails 应用程序中实现插件的设计模式

Design pattern for implementing plugins in rails applications

我正在编写一个 rails 应用程序,它必须允许第三方创建插件(即 gems)来为我的应用程序添加服务支持。这些插件都将实现相同的方法。

我读到这样做的最佳方法是使用单一 Table 继承 (STI),将所有逻辑保留在模型子类中并远离控制器。

这方面的一个例子是指标仪表板应用程序,它使用 Pingdom 插件将最近的网站响应时间添加到图表中。该插件将添加一个 Pingdom 子类,扩展一个 Metric 模型,实现一个 run 方法。此方法将由运行每个插件的 run 方法的任务中的应用程序调用。

制作这样一个基于插件的应用程序的正确方法是什么?它应该为所有逻辑使用模型吗?有没有更好的方法?

当然,单一 Table 继承在这种情况下会很有用。

# /app/models/user.rb
class User < ActiveRecord::Base
  # define your base methods, variables and constants
end
# /app/models/pluginuser.rb
class PluginUser < User
  # The gem builders can extend your methods, variables and constants or define their own
end

需要注意的是,所有插件代码都将 运行 在同一个命名空间中。插件一可以调用插件二的子类方法。这是要求吗?

只要我的 0.02 美元,并且知道会有有效的替代答案。

我认为我编写的 gems 是完全独立的服务。这意味着:

  1. 您的代码无法 demands/assumptions gem 如何处理其功能。它应该将 gem 视为一个黑盒子。
  2. gem 无法对应用程序其余部分的设计或行为做出任何 demands/assumptions。

合乎逻辑的结果是,只有 你的应用程序和他们的 gem 应该达成一致的事情是共同的 API - 他们的数据是什么gem 命令作为输入以及它们如何响应。

因此,您可以向正在构建新 gem 的人员提出这些请求似乎是合理的(您可以根据需要更改方法名称等):

  • 任何正在构建的 gem 都应该有一个命令调用 run!,它接收一组 specification/schema 对您有意义的参数。
  • 那个 gem 必须 return 来自该命令的一个响应对象,该响应对象响应一个通用的 方法,例如success?bodyerrors

无论您决定保留该数据、操作该数据等,都需要由您的应用程序负责处理。 gem 不应该对您的应用程序数据库架构、它如何处理持久性等做出假设。这种方法可能会奏效一段时间,但不可避免地会遇到大问题,并且会成为放松的噩梦。

如果您对我为什么赞成这种方法有任何疑问,或者想讨论任何替代方法,请告诉我。

编辑:

我想补充一点,上述建议是基于您在评论中给出的条件,即需要通过安装 gems 才能运行。我应该澄清一下,这种情况有很多与之相关的安全风险(以及其他问题)。

您最好构建功能以允许 add-ons/extensions 以可以构建应用程序功能但实际上不会与应用程序代码交互的方式编写。

如果您查看如何为 Heroku 构建附加组件 (https://devcenter.heroku.com/articles/building-a-heroku-add-on) or how to build a Chrome extension (https://developer.chrome.com/extensions/getstarted),您会发现应用程序和扩展的关注点是如何完全分离的。

为什么不将插件实现为通过 http 服务调用的微服务?例如,一个插件需要实现一个像 /data 这样的单一端点,它根据用户使用标准参数调用。如果您需要安全性,您可以加密用户凭据。

优点:

  • 你不必审查你添加为 gems 的代码(我不相信,我什至不相信代码审查)
  • 插件的提供者不必使用 ruby
  • 您可以更改插件,而无需重新部署整个应用程序。
  • 测试版插件将在 heroku 上免费。

heroku 博客上有一篇很棒的文章: