jemminger
jemminger

Reputation: 5173

conflicting ruby gems

I need to use two gems in my project that both claim the PDF namespace: pdf-reader and htmldoc.

Is there any way to get them to play nice together? The only way I can think of is to rewrite my own version of htmldoc to give it a different namespace.

Upvotes: 7

Views: 2628

Answers (4)

bioffe
bioffe

Reputation: 6393

I answered this in linked https://stackoverflow.com/a/37311072/292780

Respectfully disagree with the answer above. Here is how I do it:

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

which generates list of dependencies for a specific version into 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'

now you can do load('locklist.rb') which will load a specific version of a gem along with its dependencies. Look ma, no Bundler.

Upvotes: 0

Theo
Theo

Reputation: 132902

There's probably no elegant solution to the problem. If you really need the two gems working side by side I think your best option is to fork one of them (or possibly both) and use your fork. This is how I'd go about it:

  • If either gem is hosted on Github then fork it, or if both are on Github fork the one that seems to be the least work.
  • If neither gem is on Github, see if you can get hold of the source (grabbing it from the gem is a possibility, but finding the real repository might be helpful as there may be other files there that are not included in the gem), and put it in a repository on Github. Make sure the gem's license allows this (which it almost certainly does if it's one of the common open source licenses).
  • Make your changes.
  • Make sure there is a .gemspec file in the root of the repository, the next step will not work otherwise.
  • Use Bundler to manage your projects dependencies. Instead of specifying the dependency to the library you've modified as

    gem 'the_gem'
    

    specify it like this:

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

    (but change the URL to the repository to the actual one)

  • Send an e-mail to the maintainer of the gem you modified and ask him or her to consider merging in your changes in the next release.

Bundler makes it really easy to use alternative versions of gems with minimal hassle. I often fork gems, fix bugs or add features, change my Gemfile to point to my version, then ask the maintainer to merge in my changes. When, or if, that happens I simply change my Gemfile back to just refer to the official version of the gem.

An alternative strategy, if the maintainer does not want to merge in your changes and you want to distribute your version to others is to push up your version to Rubygems as a new gem, but in that case prefix the gem name with your name, or some other string that identifies your gem as a variant.

Upvotes: 2

Jörg W Mittag
Jörg W Mittag

Reputation: 369508

Basically, there's nothing you can do. It is good practice in Ruby, to use distinctive names in the top-level namespace precisely for this reason, and you just happened to stumble upon two libraries that violate that practice.

One thing you could do is to use Kernel#load instead of Kernel#require. Kernel#load takes an optional boolean argument, which will tell it to evaluate the file within an anonymous module. Note, however, that this is in no way safe: it is perfectly possible to explicitly put stuff in the top-level namespace (using something like module ::PDF) and thus break out of the anonymous module.

Note also that the API is really crappy: load simply returns true or false, just like require does. (Actually, since load always loads, it always returns true.) There is no way to actually get at the anonymous module. You basically have to grab it out of the ObjectSpace by hand. Oh, and of course, since nothing actually references the anonymous module, it will be garbage-collected, so not only do you have to rummage around in the bowels of ObjectSpace to find the module, you also have to race the garbage collector.

Sometimes, I wish Ruby had a proper module system like Newspeak, Standard ML or Racket.

Upvotes: 5

Andrew Grimm
Andrew Grimm

Reputation: 81548

I've heard of a new piece of functionality called refinements. It's designed to avoid two different monkeypatches affecting the same class causing problems, but I'd see if it could help with your problem.

Upvotes: 0

Related Questions