相互矛盾的红宝石

时间:2010-12-21 01:07:08

标签: ruby rubygems conflict

我需要在我的项目中使用两个宝石,它们都声称PDF命名空间:pdf-reader和htmldoc。

有没有办法让他们一起玩得很开心?我能想到的唯一方法是重写我自己的htmldoc版本,为它提供一个不同的命名空间。

4 个答案:

答案 0 :(得分:5)

基本上,你无能为力。在Ruby中,由于这个原因,在顶级命名空间精确地中使用独特的名称是一种很好的做法,而您恰巧偶然发现了两个违反该实践的库。

Kernel#load接受一个可选的布尔参数,它将告诉它在匿名模块中评估文件。但请注意,这绝不是安全的:完全可以显式将内容放入顶级命名空间(使用类似Kernel#require之类的内容),从而打破匿名模块。

另请注意,API非常糟糕:Kernel#load只返回module ::PDFload,就像true一样。 (实际上,由于false 始终加载,总是返回require。)实际上 无法实现在匿名模块。你基本上必须手动从load中取出它。哦,当然,因为没有任何实际引用匿名模块,它将被垃圾收集,所以你不仅需要在true的内容中翻找找到模块,你还必须竞赛垃圾收集器。

有时,我希望Ruby有一个合适的模块系统,如Newspeak,Standard ML或Racket。

答案 1 :(得分:2)

这个问题可能没有优雅的解决方案。如果你真的需要两个并排工作的宝石,我认为你最好的选择是分叉其中一个(或可能两个)并使用你的前叉。这就是我要做的事情:

  • 如果任何一个gem托管在Github上,那么将它分叉,或者如果两个都在Github上,那么叉子似乎是最不起作用的。
  • 如果两个gem都不在Github上,看看你是否可以获取源代码(从gem获取它是一种可能性,但找到真正的存储库可能会有所帮助,因为那里可能还有其他文件没有包含在内。 gem),并将其放在Github上的存储库中。确保gem的许可证允许这种情况(如果它是常见的开源许可证之一,几乎肯定会这样做。)
  • 进行更改。
  • 确保存储库的根目录中有.gemspec文件,否则下一步将无效。
  • 使用Bundler管理项目依赖项。而不是指定您已修改为

    的库的依赖项
    gem 'the_gem'
    

    像这样指定:

    gem 'the_gem', :git => 'git://github.com/you/the_gem.git'
    

    (但将存储库的URL更改为实际的URL)

  • 发送电子邮件给您修改过的gem的维护者,并要求他或她考虑在下一个版本中合并您的更改。

Bundler使得使用替代版本的宝石变得非常容易,而且麻烦极少。我经常分叉宝石,修复错误或添加功能,更改我的Gemfile以指向我的版本,然后要求维护者合并我的更改。何时,或者如果发生这种情况,我只需将Gemfile更改为仅仅参考宝石的官方版本。

另一种策略,如果维护者不希望合并您的更改并且您希望将您的版本分发给其他人,那么就是将您的版本作为新宝石推送到Rubygems,但在这种情况下,请将宝石名称加上前缀名称或其他字符串,用于将您的宝石标识为变体。

答案 2 :(得分:0)

我听说过一项名为优化的新功能。它旨在避免两个不同的monkeypatches影响同一个类导致问题,但我会看看它是否可以帮助解决你的问题。

答案 3 :(得分:0)

我在链接中回答了这个问题 https://stackoverflow.com/a/37311072/292780

恭敬地不同意上述答案。我是这样做的:

ruby -S gem list my_gem

`*** LOCAL GEMS ***
my_gem (1.0.1, 1.0.0, 0.0.2)
`

ruby -S gem lock my_gem-1.0.0 > locklist.rb

生成特定版本的依赖关系列表到locklist

require 'rubygems'
gem 'my_gem', '= 1.0.0'
gem 'gem_base', '= 1.0.0'
gem 'rest-client', '= 1.7.2'
gem 'savon', '= 1.1.0'
gem 'addressable', '= 2.3.6'
gem 'mime-types', '= 1.25.1'
gem 'netrc', '= 0.11.0'

现在您可以执行load('locklist.rb'),它将加载特定版本的gem及其依赖项。看马,没有Bundler。