Unresolved or ambiguous specs during Gem::Specification.reset
I’m getting started writing a gem. I’ve worked on many gems but it turns out writing one from scratch comes with gotchas you just have to learn.
My gem is now using Railties so I can start hooking into some Rails ActiveSupport::Notifications. However, after requiring it, I was getting this problem in the place where I was using the gem:
~/git/mirth (brokepoint-upgrade) $ bundle
WARN: Unresolved or ambiguous specs during Gem::Specification.reset:
connection_pool (>= 2.2.5)
Available/installed versions of this gem:
- 2.5.3
- 2.5.0
- 2.4.1
minitest (>= 5.1)
Available/installed versions of this gem:
- 5.25.5
- 5.25.4
- 5.25.2
- 5.20.0
logger (>= 1.4.2)
Available/installed versions of this gem:
- 1.7.0
- 1.6.6
- 1.6.4
- 1.6.1
- 1.6.0
benchmark (>= 0.3)
Available/installed versions of this gem:
- 0.4.1
- 0.4.0
- 0.3.0
nokogiri (>= 1.8.5, >= 1.6)
Available/installed versions of this gem:
- 1.18.9
- 1.18.8
- 1.18.4
- 1.18.3
- 1.17.2
- 1.16.7
rack (>= 2.2.4, >= 3)
Available/installed versions of this gem:
- 3.1.16
- 3.1.14
- 3.1.12
- 3.1.10
- 3.1.8
rack-session (>= 1.0.1)
Available/installed versions of this gem:
- 2.1.1
- 2.1.0
- 2.0.0
rack-test (>= 0.6.3)
Available/installed versions of this gem:
- 2.2.0
- 2.1.0
rails-html-sanitizer (~> 1.6)
Available/installed versions of this gem:
- 1.6.2
- 1.6.0
useragent (~> 0.16)
Available/installed versions of this gem:
- 0.16.11
- 0.16.10
erubi (~> 1.11)
Available/installed versions of this gem:
- 1.13.1
- 1.13.0
rake (>= 12.2)
Available/installed versions of this gem:
- 13.3.0
- 13.2.1
- 13.1.0
thor (~> 1.0, >= 1.2.2)
Available/installed versions of this gem:
- 1.4.0
- 1.3.2
irb (~> 1.13)
Available/installed versions of this gem:
- 1.15.2
- 1.15.1
- 1.14.3
- 1.14.1
- 1.13.1
WARN: Clearing out unresolved specs. Try 'gem cleanup <gem>'
Please report a bug if this causes problems.
Bundle complete! 24 Gemfile dependencies, 130 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
It’s just a warning, but it’s a very noisy one.
The issue here, it turns out, is that I was requiring something in my gem (railties) before bundler had figured out what its dependencies were which means it cannot tell gem
which specific gems to be loading.
My debugging of this also answered another question I’ve had for a while: why is it so common to define your gem’s version number on a brokepoint/version.rb
? Why not just stick it right in brokepoint.rb
where the module is first defined?
Code that triggers the warning
I shall try to make this example as concise as I can, but it is unfortunately spread across a number of files.
# brokepoint.gemspec
require_relative 'lib/brokepoint'
Gem::Specification.new do |s|
s.name = 'brokepoint'
s.version = Brokepoint::VERSION
You see I just define the version below.
# lib/brokepoint.rb
require_relative "./brokepoint/railtie"
module Brokepoint
VERSION = '0.0.1'
end
# lib/brokepoint/railtie.rb
require 'rails/railtie'
module Brokepoint
class Railtie < Rails::Railtie
The problem
Loading lib/brokepoint.rb
when we ask for the version constant will also trigger that require rails/railtie
. Bundler isn’t ready for that yet! It’s still in the process of just collecting metadata for the gems being loaded, and suddenly you’re asking for all of Rails.
rubygems
will be quite happy to find a version of Rails for you, based on your require rails/railtie
, it’ll be a gamble what version of Rails you’ll get. It likely won’t match the version constraints you’ve set in your Gemfile.
So, it gives you that warning.
The fix
We need to avoid loading all of our library too early. That’s why people load only their version constant!
# brokepoint.gemspec
require_relative './lib/brokepoint/version'
Gem::Specification.new do |s|
s.name = 'brokepoint'
s.version = Brokepoint::VERSION
# lib/brokepoint/version.rb
module Brokepoint
VERSION = '0.0.2'
end
# lib/brokepoint.rb
require_relative "./brokepoint/version"
require_relative "./brokepoint/railtie" if defined?(Rails::Railtie)
module Brokepoint
end
So now loading the gem only triggers the constant. And then later calling other parts of the gem will trigger the full thing. Without the annoying warning message!