5

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

Viktor Trón
  • 8,774
  • 4
  • 45
  • 48

2 Answers2

4

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:

  1. 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
    
  2. 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
    
  3. 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.

  4. 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.

Matheus Moreira
  • 17,106
  • 3
  • 68
  • 107
0

I got this same question a while ago. My solution was to think that the developer should specify this behavior. I would not specify it on the gem, but on the wiki. I would recommend you to document it clearly that the developer need to define one of the dependencies.

To make it better, you can make a check on initialization of the gem, looking for the dependencies, if none can be found, just raise a runtime exception or if you prefer, your own exception. =)

lulalala
  • 17,572
  • 15
  • 110
  • 169
Paulo Henrique
  • 1,025
  • 8
  • 12