根据配置在运行时选择Ruby gem

时间:2015-03-05 00:14:40

标签: ruby-on-rails ruby dependency-injection gem

我们正在编写一个Rails应用程序,我们希望能够通过统一的适配器接口与任何一个外部数据存储库进行通信,我们希望以后能够通过写入来添加更多内容该适配器接口的新实现(参见ActiveRecord适配器)。

应用程序的任何一个安装只需要运行一个适配器,而且每当我们引入新的适配器时,我们都不想要修改代码甚至是Gemfile。假设我们将每个适配器编写为一个独立的gem,根据配置,在运行时只包含一个适配器gem的正确方法是什么?

3 个答案:

答案 0 :(得分:2)

通常在Rails中,您只能访问Gemfile中列出的gem; Bundler强制执行此操作。但是,这并不能阻止您手动加载Gems。

如果您的适配器gem安装在一个众所周知的位置,那么加载适配器gem可能就像这样简单:

$LOAD_PATH << "/path/to/adapters/my_adapter/lib"
require "my_adapter"

“my_adapter”是您在运行时通过配置发现的内容。

答案 1 :(得分:2)

将您的配置放在环境中,就像您可能已经在做的那样。

例如,我使用dotenv gem。

将环境变量设置为适配器gem名称。

结果是Rails可以访问的ENV var,例如:

ENV["adapter"] #=> "my_custom_gem_name"

使用环境选择适配器,例如在你的Rails配置中:

require ENV['adapter'] 

这假设您的宝石名称与require名称相同。如果您的宝石名称不同,请使用require名称。

部署新适配器时,可以将其放在Rails加载路径上的任何位置。或者您可以根据需要调整Rails加载路径,例如添加$ LOAD_PATH的路径。

如何调整加载路径的示例:

# /config/application.rb
module MyApp
  class Application < Rails::Application
  $LOAD_PATH << "/path/to/custom/gem" 
  require("my_custom_gem_name")
  …

例如,将gem代码放在./lib中,或使用您喜欢的任何工具(例如./vendorscp或{将gem安装到rsync或系统范围内{1}},因此绕过了典型的ansible命令。

您无需更新Gemfile。

答案 2 :(得分:0)

我不认为这是一个很好的方式来完全按照你的要求去做。 Bundler系统的一部分(使用Gemfiles的原因)是你的gem文件中提到的gem(及其依赖项)只有你自己的应用程序可用,它只与它们隔离。如果这听起来像是一件坏事,我们可以就该系统成功解决的预捆绑器依赖管理问题进行长时间的讨论。

选项1 - 无论如何你还在担心什么?在你的Gemfile中包含所有可能的gems可能没有任何缺点,即使给定的安装只使用一个。如果使用Rails,您需要确保这些宝石 require'd on launch。 (如果不使用Rails,这可能不是必要的,因为其他环境不一定要求捆绑者在启动时要求所有宝石(这是明智的),但不会受到伤害)。

因此,所有的宝石都将安装在每个安装中,是的,但是只有你想要使用的宝石才会被加载,当你在运行时发出require时,你仍然想要做这在应用程序加载时,可能是为了响应ENV变量,以避免任何并发或加载时间的怪异。无论如何,未使用的宝石将只是坐在磁盘上卸载。安装但未使用它们有多大的缺点?您愿意为了摆脱这种不利因素而增加设置的混乱程度多少?

选项1就是我要做的。

选项2 - 单独的Gemfiles 。另一个选择是为每种类型的设置准备一个单独的Gemfile。 Gemfiles只是红宝石代码,所以你可以有一个基础&#39; gemfile包括常见的gem,然后为每种类型的设置单独的Gemfile,它使用ruby加载/包含基本gemfile,然后添加特定于设置的gem。您为每个Gemfile分别命名Gemfile_adapter1或其他名称。

当您添加对新适配器类型的支持时,您将不得不向源代码提交某些内容,不是吗?这个适配器来自哪里?我不明白你如何在不修改任何代码的情况下做到这一点。无论如何,在添加新适配器时为该适配器类型添加新的Gemfile并不是一个巨大的障碍,但我不知道。

您可以启动Rails,指定将哪些gem文件与BUNDLE_GEMFILE env变量一起使用:BUNDLE_GEMFILE=./Gemfile_one rails server。在其他所有命令中,您将使用Gemfile。 BUNDLE_GEMFILE=./SOME_GEMFILE bundle installBUNDLE_GEMFILE=./SOME_GEMFILE RAILS_ENV=production bundle exec rake assets:precompile。如果你使用capistrano为你做了一些这样的事情,弄清楚如何确保cap在执行时使用正确的BUNDLE_GEMFILE。了解如何让您的应用服务器在启动您的应用时执行此操作。等等。

这样做会很好 - 但最终会很难跟踪并确保它在你的整个devops堆栈中正常工作。

我想你甚至可以在安装时生成 Gemfiles,而不是将它们放在你的源代码库中,因为更加令人困惑的情况和另一件可能出错的事情并且让人混淆调试! (我不会)。

我考虑过这个选项2,但更喜欢选项1,除非有充分的理由它不会起作用。

选项3 - 不要使用Bundler 。你可以完全抛弃bundler和gemfiles。也许,如果你能说服Rails以某种方式做到这一点。可能很难说服Rails这样做。

如果可以的话,您需要手动在部署系统上安装所有宝石,确保它们是正确的版本,并确定哪些版本与其他版本兼容,并且不再需要您不想要的最新版本已安装。

然后,在运行时,您只需要require所需的所有宝石,该应用程序将获得系统上安装的最新版本的宝石。

我永远都不会这样做。我不知道你是否可以让Rails做到这一点,但即使你可以,我也记得在Bundler存在之前依赖地狱,并且永远不想回去。