proper way to specify optional dependencies of a gem in ruby Gemfile

bundler, gemfile, ruby, rubygems, testing

Solution

Don't include the `a` gem in your dependencies, but `require` it anyway. If that fails, it will raise `LoadError`, from which you can rescue.

begin
  require 'a'
rescue LoadError
  # The 'a' gem is not installed
  require 'b'
end

I believe this is the best way to use and test this setup:

Define an interface for your backend and allow a custom implementation to be easily plugged in.

module YourGem
  class << self
    attr_accessor :backend

    def do_something_awesome
      backend.do_something_awesome
    end
  end
end

Implement the `a` and `b` backends.

# your_gem/backends/a.rb
require 'a'

module YourGem::Backends::A
  def self.do_something_awesome
    # Do it
  end
end

# your_gem/backends/b.rb
require 'b'

module YourGem::Backends::B
  def self.do_something_awesome
    # Do it
  end
end

Set the one you want to use.

begin
  require 'your_gem/backends/a'
  Gem.backend = YourGem::Backends::A
rescue LoadError
  require 'your_gem/backends/b'
  Gem.backend = YourGem::Backends::B
end

This will use `YourGem::Backend::A` even if `b` is installed.

Before testing, make sure both `a` and `b` gems are installed, `require` both backends in the test code, run the tests with one backend, then run the tests again with the other backend.

Problem

a gem intends to support gems `a` or `b` as alternatives for a functionality. In code I check with `defined?(A)` if I fall back to `b` that's fine. But as a gem developer how to specify these dependencies? 1) what do I put in the Gemfile. ``` group :development, :test do gem 'a', :require => false gem 'b', :require => false end ``` This allows `Bundle.require(:test)` not to auto-require a,b? 2) How can explicitly require `a` and `b` separately to mimic (or mock) the scenario when we fall back to `b` in my tests? 3) Also how do I specify that either `a` or `b` is prerequisite for the gem. thanks

Original source