Skip to content

Instantly share code, notes, and snippets.

@hsbt
Last active August 28, 2017 06:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hsbt/968fcbb7acfa9405d82040a3c219f43a to your computer and use it in GitHub Desktop.
Save hsbt/968fcbb7acfa9405d82040a3c219f43a to your computer and use it in GitHub Desktop.
Gem activate issue with default gems.

いくつかの default gem を Bundler を利用してバージョンを固定して使おうとした場合、現在の Rubygems/Bundler/Ruby の仕様ではユーザーが指定したバージョンでは利用できないケースがあります。

例えば

$ ruby -v
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin17]
$ gem list | grep openssl
openssl (2.0.5, 2.0.4, default: 2.0.3)

というような環境の場合に require 'openssl' を実行したときに activate されるバージョンは openssl で探索して最初に見つかったバージョン、すなわち 2.0.5 が使用されます。

ruby -ropenssl -e 'p OpenSSL::VERSION'
"2.0.5"

この時、例えばユーザーがどうしても openssl 2.0.4 を使いたいと思って以下のような Gemfile を書いたとします。

> cat Gemfile
# frozen_string_literal: true
source "https://rubygems.org"

gem 'openssl', '2.0.4'

しかし残念ながら、bundler が動く前に rubygems が openssl を require してしまっているため、以下のように activated エラーとなります。

> bundle exec ruby -ropenssl -e 'p OpenSSL::VERSION'
/path/to/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.15.4/lib/bundler/runtime.rb:317:in `check_for_activated_spec!': You have already activated openssl 2.0.5, but your Gemfile requires openssl 2.0.4. Prepending `bundle exec` to your command may solve this. (Gem::LoadError)

この問題は pure ruby で実装されている default gem であれば bundler のリポジトリ配下に vendoring library として同梱することで回避できます。

https://github.com/bundler/bundler/blob/master/lib/bundler/vendor/fileutils/lib/fileutils.rb

bundler の場合、Bundler::FileUtils として名前空間を分けることでユーザーが指定したバージョンでも activate 時にコンフリクトしないで利用可能としています。しかし、この方法は C extention なライブラリでは利用することができません。

今回、bundler チームより json/psych をデータのシリアライズを行うために rubygems/bundler で利用したいので、C extension について Ruby 本体に何かしら回避する方法を実装できないかと相談を受けています。

@hsbt と @indirect で相談して、例えば

require_for_bundler 'json', '2.0.2'

したら、json-2.0.2(これは ruby 2.4.1 に同梱されているバージョン)が Bundler::JSON というような名前空間に置かれたら回避できそうという話はしました。なお、過去にも似たような issue はありました。

https://bugs.ruby-lang.org/issues/10320

この issue にあるような方法を拡張した require 'json', version: '2.0.2', into: :Bundler という書き方はそれっぽいと思います。また今回のユースケースでは default gem の使用を強制するような require 'json', version: :default, into: :Bundler という機能で事足りそうです。

このような feature について松本さんはどうお考えでしょうか

@hsbt
Copy link
Author

hsbt commented Aug 28, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment