自戒です
tl;dr
- gemspecの中でRubyのバージョンによってインストールしたいgemのバージョンを変えたい時は、gemspecではなくGemfileでif文書くのがおそらく正解
発端
先月くらいのFacebook内のちょっとした会話がきっかけでした *1
- activesupportやactiverecord 5系以降ではRuby 2.2.2以降必須になった
- 自分のgemがactivesupport (activerecord)に依存していた場合、そのままだとRuby 2.2.2未満で
gem install
やbundle install
しようとすると5系をインストールしようとして、バージョンが古いって怒られる - 自分のgemはactivesupport 5系の機能は必要としていないので、もしRuby 2.2.2未満でインストールしようとしたら自動的にactivesupport 4系をインストールしてほしかった
間違った対処法
if Gem::Version.create(RUBY_VERSION) >= Gem::Version.create("2.2.2") spec.add_dependency "activesupport", ">= 4.0.0" else # NOTE: activesupport 5.x supports only ruby 2.2.2+ spec.add_dependency "activesupport", ">= 4.0.0", "< 5.0.0" end
https://github.com/sue445/rubicure/blob/v0.4.7/rubicure.gemspec#L23-L28
gemspecでこんなif文を書いておけば、ruby 2.2.2未満の時はactivesupport 4系がインストールされる、、、
Travis CIのビルドの各Rubyのバージョンのログを見ても意図したバージョンがインストールされていたし、間違いなんてあるわけないと思っていました。
だがしかし
https://rubygems.org/gems/rubicure/versions/0.4.7 の表示、てっきり表示だけこうなってて gem install
はいい感じに分岐してくれるのかと思ってた。。。
sonots先生曰く
rubygems は jruby と cruby (platform) で別々の gem をリリースすることはできるんですが、残念なことに cruby のバージョンで別々の gem をリリースすることはできないのですよねぇ
リリースする gem 自体の依存は緩くして、アプリ側の Gemfile であなたの rubyバージョンに合わせてactivesupport 絞ってね、というしかないのかなぁと思ってました。rubygems に機能が足りてないと思うんですよねぇ。
travis を通す時も、gemspec は緩くしておいて、Gemfile 二つ用意して、build matrix を頑張る
検証結果
ruby 2.1.9で上記gemspecの分岐が入ったrubicureのバージョンをインストールしようとすると、「activesupport requires Ruby version >= 2.2.2.」とエラーが出ているのが分かると思います
上記エラーは rubygems.orgからのインストールログですが、geminabox にホスティングされた社内gemでも検証をしましたが同様の結果でした。
所感
add_dependency
(add_runtime_dependency
)で書くruntime dependency(アプリの実行時に必要なgem)に関しては上記の理由で意味が無い- もしバージョンを絞りたい場合はgemspecではゆるくしておいてGemfileでバージョンを絞る
- 必要ならドキュメントにもそれを明記する。*2
add_development_dependency
で書くdevelopment dependency(gemの開発時に必要なgem)に関してはgem installでインストールされないため、if文を書いても意味なくはない- とはいえ、
add_development_dependency
はOKでadd_dependency
はNGというのは普通分からないし混在すると紛らわしいため、gemspecにRUBY_VERSION
のif文は一切書かずに Gemfileに書くよう統一するのが今の自分の中では正解だと思ってます。(異論は認める)
Rubyのバージョンによる分岐を全部Gemfileに寄せた結果
こんな風になりました
source "https://rubygems.org" # Specify your gem's dependencies in rubicure.gemspec gemspec if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.1.0") # NOTE: build is failed when use ruby 2.0 and rspec-parameterized 0.3.0+ # https://travis-ci.org/sue445/rubicure/jobs/114266855 gem "rspec-parameterized", "< 0.3.0" end if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.1.0") # NOTE: unparser v0.2.5 drop support ruby < 2.1 gem "unparser", "< 0.2.5" end if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.2.2") # NOTE: activesupport 5.x supports only ruby 2.2.2+ gem "activesupport", ">= 4.0.0", "< 5.0.0" end if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.3.0") gem "backport_dig" end
https://github.com/sue445/rubicure/blob/v0.4.9/Gemfile
rubicureもそろそろRuby 2.0系サポートきってもいいかなぁ。。。
謝辞
上記Facebookの会話の転載を快諾してくださった id:koic さんと id:sonots さん、ありがとうございます m(_ _)m
*1:FBキャプチャ転載に関しては事前にお二人の許可はとっています
*2: rubicureだとこんな感じ https://github.com/sue445/rubicure/blob/v0.4.9/README.md#installation