Targeting Multiple Platforms (JRuby etc) with a RubyGems .gemspec

Recently we had a user who runs the relish gem on JRuby, and needed jruby-openssl to be loaded. He kindly submitted this patch, which I merged in without really thinking about it too much. Then the problems started.

That’s not the right way to express dependencies for different platforms using RubyGems and Bundler. I’ve done some research and I think I understand the current good practice for this, so I’m going to document it here.

The .gemspec is read at the time you build and release your gem, so any conditional logic in that file will be evaluated once when the gem is built and released on your machine. So a line like this:

s.add_runtime_dependency('jruby-openssl') if RUBY_PLATFORM == 'java'

Will bake-in the dependency based on whatever platform you run gem build on.

What you need instead is to evaluate the platform at runtime. Bundler offers you a way to do this, in your Gemfile:

platforms :jruby do
  gem "jruby-openssl"
end

What a jruby user will now experience is that when your gem is loaded, they’ll see a warning:

→ relish help
JRuby limited openssl loaded. http://jruby.org/openssl
gem install jruby-openssl for full support.
...

It’s now up to the user to manually install the gem themselves. It seems a shame that there isn’t any way to specify this information in the gem’s manifest, so that it can be installed on a platform-specific basis when you gem is installed, but as far as I can tell there’s no way to do that right now.