Dependency issues with Rubygems - ruby

I created a gem that has some dependency. I published that gem on Rubygems.org. My gem gets installed on other system, but the gem package is not located. I figured out that this is likely due to my gem dependency not getting installed. Do I need to do any specific thing to get all my dependency installed?

Looking at existing gems I see these components: Gemfile, Gemfile.lock, and whatever.gemspec. Note the gem name is lowercase to match the gemspec file name (I think this was a problem for me before)
Gemfile
source 'https://rubygems.org'
# Specify your gem's dependencies in whatever.gemspec
gemspec
whatever.gemspec
Gem::Specification.new do |spec|
spec.name = "whatever"
spec.add_dependency "hashie"
end

Related

How to work with gemspec add_runtime_dependency and `bundle install`

I have a codebase which has a gemspec file like:
require "rubygems"
::Gem::Specification.new do |specification|
specification.add_development_dependency "yard", "~> 0.9"
specification.add_runtime_dependency "dry-validation", "~> 0.13"
end
bundle install will install both dependency types. So I want to just install the runtime dependencies for my CI scripts. I see bundle install --with <group> exists, but I don't have groups. Run interactively, the returned specification has an empty result returned from .groups. I would love to rationalize these two worlds. Must I explicitly add a group for each gem dependency? Does add_runtime_dependency and add_development_dependency even make a difference?
from bundler's documentation
Because we have the gemspec method call in our Gemfile, Bundler will automatically add this gem to a group called “development” which then we can reference any time we want to load these gems with the following line:
Bundler.require(:default, :development)
in your case, if you wish to install all rubygems that are not for development, then try
bundle install --without development
for future bundler version, you can configure it locally (or globally)
bundle config set --local without 'development'
to make it all work, verify that you have a Gemfile in your project, which will look like
# frozen_string_literal: true
source 'https://rubygems.org'
gemspec

How can I install a gem (via bundler using gemspec) before parsing the gemspec?

I have a gem that exists for the purpose of helping with versioning. It's useful to have this gem available when defining the version in the gemspec file.
The problem, however, is that running bundle install first causes the gemspec to be parsed, which results in an error because the required gem isn't installed yet.
I can get around it by running gem install <other_gem> before bundle install, but I'd much prefer bundler manage it, especially when taking into account that I'm using a custom gem server.
I've tried adding the gem to the Gemfile directly before the gemspec line, but no luck.
Gemfile:
source 'https://my.gemserver.com/gems'
gemspec
mygem.gemspec:
require 'external/dependency'
Gem::Specification.new do |spec|
spec.name = 'mygem'
spec.version = External::Dependency.version_helper
....
spec.add_development_dependency 'external-dependency'
end
EDIT:
Another workaround is to rescue the LoadError and specify a default version if the dependency isn't loaded. Also, not ideal
begin
require 'external/dependency'
rescue LoadError; end
Gem::Specification.new do |spec|
spec.name = 'mygem'
spec.version = defined?(External::Dependency) ? External::Dependency.version_helper : ''
....
spec.add_development_dependency 'external-dependency'
end
I think you're stuck with gem install. But I would solve this by adding that step to the Dockerfile I use for the project.
Maybe it's possible to do something like this using rbenv or rvm? Haven't used either of those since migrating to Docker, but rvm gemset is kind of a bootstrap...
I got around it by making the gemspec install the gem during a bundle update or install.
EXTERNAL_DEPENDENCY = Gem::Dependency.new('external-dependency', '~> 0.1')
if File.basename($0) == 'bundle' && ARGV.include?('update') || ARGV.include?('install')
require 'rubygems/dependency_installer'
Gem::DependencyInstaller.new.install(EXTERNAL_DEPENDENCY)
end
and then...
spec.add_development_dependency EXTERNAL_DEPENDENCY.name, EXTERNAL_DEPENDENCY.requirements_list

How can I avoid bundlers warning about multiple sources when I have all gems in my .gemspec?

In my own gem, I have a Gemfile that looks basically like this:
source 'https://my.gemserver.com'
source 'https://rubygems.org'
gemspec
My .gemspec has all dependencies listed as add_dependency and add_development_dependency.
As of Bundler 1.8, I get the warning:
Warning: this Gemfile contains multiple primary sources. Using `source` more than
once without a block is a security risk, and may result in installing unexpected gems.
To resolve this warning, use a block to indicate which gems should come from the
secondary source. To upgrade this warning to an error,
run `bundle config disable_multisource true`.
Is there a way to resolve this warning (without muting via bundle config)? I cannot find anything about a source option in the Rubygems specification.
No, you'll either need to mute the warning or add the source block to your Gemfile with the specific gems you want to come from your private server. There isn't a need to duplicate the ones that come from rubygems.org (or you could do it the other way around, if you depend on more private gems than public ones, and your private gems do not themselves depend on public ones).
The problem is that the gemspec format has no support for specifying the source for each gem, so without duplicating them into the Gemfile, there is no way to specify which gems come from each source.
Kind of sad, but one has to move it out to Gemfile :-(
Gemfile:
source 'https://my.gemserver.com' do
your_gem1
your_gem2
#...
end
source 'https://rubygems.org'
gemspec
but then, if some of your gems should be included in :development or :test group, following could be used
Gemfile:
your_gem1, :source => 'https://my.gemserver.com'
#...
group :development do
your_gem2, :source => 'https://my.gemserver.com'
#...
end
source 'https://rubygems.org'
gemspec
To elaborate on the discussion on the bundler issue, as previous answers have stated, you must include the gem in you Gemfile. However, you only need to specify the version of the gem in your .gemspec. If you change versions more often than private dependencies this isn't a terrible solution.
Reference the gem without version in Gemfile:
# Gemfile
source 'https://rubygems.org'
source 'https://xxx#gem.fury.io/me/' do
gem 'my-private-dependency'
end
gemspec
Reference the gem with version specification in the .gemspec:
# my-gem.gemspec
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
Gem::Specification.new do |spec|
spec.add_dependency 'my-private-dependency', '~> 0.1.5'
end

Gem and bundler: Adding a development dependency with a relative path

I'm helping on the development of a series of interrelated gems. As such, I don't want them to have a hard dependency on each other, but I do want them to run tests in development that use each other. Simple right? Just use add_development_dependency in the gemspec, right? Well, there is one little wrinkle - the git repository contains all the gems, and so I want the Gemfile to point to the local copy of the gem. This works with a hard dependency. In the gemspec, I have this line for my hard dependency:
s.add_dependency "mygem-core"
And then in the Gemfile, I have this line:
gem "mygem-core", :path => "../mygem-core"
This works PERFECT. The dependency exists for when I push this package out, and when I'm testing, it will use the local copy of mygem-core. The problem is that when I put THIS in the gemspec:
s.add_development_dependency "mygem-runtime"
And then this in the Gemfile:
gem "mygem-runtime", :path => "../mygem-runtime"
Then I get an error when I run bundle:
You cannot specify the same gem twice coming from different sources. You specified that mygem-packager (>= 0) should come from source at ../mygem-packager and
Yes, that's not a typo at the end. There is literally a blank, empty space at the end for the second 'location'. Is there any smart ways to work around this? I want to add this as a development dependency, and use the local source. What am I missing?
It's probably better that you leave that gem out of the .gemspec manifest and put it in the Gemfile under the :development group.
# Gemfile
source :rubygems
gemspec
gem "mygem-runtime", :path => '../mygem-runtime', :group => :development
If your using Gemfile to specificy a local path to a gem you will need to remove it from gemspec. Bundler will parse gemspec and add the dependencies those bundler is installing, so its like having the gem specified twice.

Gem development with Bundler: include or exclude Gemfile?

I'm developing a gem locally. It's a command-line utility that only has test dependencies, and my Gemfile looks like this:
source :rubygems
gemspec
group :test do
gem "cucumber"
gem "aruba"
gem "rspec"
end
My gemspec looks like this:
Gem::Specification.new do |s|
# authorship stuff...
s.files = `git ls-files`.split("\n")
end
That's the default gemspec created by Bundler. I know we're supposed to keep Gemfile and Gemfile.lock in source control, but I'm wondering about including them in the packaged gem through the Gem::Specification#files attribute. Are there arguments for/against including Gemfile and Gemfile.lock in the distributed gem? It seems weird or at least unnecessary to me.
Yehuda Katz just blogged on this topic! : Clarifying the Roles of the .gemspec and Gemfile

Resources