How to Specify Gem Dependency Version at Gem Install Time - ruby

I am working with a gem that has a dependency whose version must vary depending on which Ruby version is running when the gem is installed. Specifically, the current version of the nio4r gem requires Ruby version >= 2.2.2, and the async gem that needs nio4r should ideally be able to run on any Ruby version >= 2.0.
I tried putting some code into the gemspec file to vary the version, but it seems that it is executed when the gem is built, not when it is installed. In other words, the nio4r version is hardcoded into the gem based on whatever version of Ruby is used to build the gem. Here's the code I tried in the gemspec (say is an OS X command to speak a string and was very helpful in knowing what was happening and when; I would of course remove it later).
# Recent versions of nio4r require a Ruby version >= 2.2.2.
# If the version used is earlier, load an earlier version of nio4r.
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.2.2')
`say Installing latest version of nio4r`
spec.add_runtime_dependency "nio4r"
else
`say Installing nio4r version 1.2.1`
spec.add_runtime_dependency "nio4r", '1.2.1'
end
As I said, this was executed on gem build and not install. I am aware that I can use bundler for this kind of thing and put the specification in the Gemfile, but as far as I can tell that file will not be used when the gem is gem install'ed.
How can I get the dependent's gem version to vary at gem install time?
I'm looking for a solution that is automated, that is, does not involve any intervention by the user of this gem. Also, this gem will be installed as a result of installing another gem, so I don't want the user to have to keep up with changes to its dependent gem and have to modify the Gemfile manually. It would kind of be a violation of the Law of Demeter.

There is not a way that I know of to dynamically update the ruby version in the gem's gemspec file, however you can lock it to a specific ruby version:
spec.required_ruby_version = '>= 2.2.2'
Alternatively you could suggest to consumers of your gem (in the README perhaps) that they must force a specific version of nio4r if they are using versions of ruby older that 2.2.2.
In Gemfile for a new Ruby:
ruby "2.4.1"
gem "keiths_gem"
In a Gemfile with an old version of Ruby:
ruby "2.1.0"
gem "keiths_gem"
gem "nio4r", '1.2.1'

Related

Which version of Ruby is mintiest 5.14.1 gem compatible with?

When I run:
$ bundle install
I get this response:
Fetching gem metadata from https://rubygems.org/.........
minitest-5.14.1 requires ruby version ~> 2.2, which is incompatible with the
current version, ruby 3.0.0p0
I only have ruby 3.0.0p0 installed.
minitest gem page says:
Required ruby version >= 2.2, < 4.0
I assume this means anything up to Ruby 3.0.0p0 is compatible. However, that isn't what the error message says.
Not sure if this is a solution. I'd call it more of a work around.
Install ruby version 2.7.2
Update .ruby-version file in Jekyll to 2.7.2
That's it :)
Ideal solution would include identifying why Jekyll was dependent on minitest-5.14.1 (when only 5.14.4 was installed based on gem list).

What does the version of a ruby gem have to do with the version of ruby used during development?

I've been reading on rubygems.org about leveraging the semantic versioning pattern for Ruby gems.
For example, I might want to use some ruby gem named library that includes all minor updates starting from v2.2 up to but not including v3.0. I can add it as a run-time dependency in my gemspec or my Gemfile:
some_gem.gemspec:
spec.add_runtime_dependency 'library','~> 2.2'
Gemfile:
gem 'library', '~> 2.2'
I also know we can specify which version of ruby we'd like to use using a ruby version management like rbenv.
For example, I've also set the local ruby version to system (on macOS) before with:
rbenv local system
Which generated a .ruby-version file in the project root.
I'm confused about what the ruby version a gem author uses has to do with the gem version. Are they related? How, would a gem author manage the version of ruby? Do gem author publish gems for multiple ruby versions?
For example, if an author uses 2.3.1 to build her gem does that mean someone with only system ruby would not be able to use it?
The ruby version specification is optional, and just used to help developers consume the gem easier. For example there are many 2.3 gems that don't have the required_ruby_version set. If you try to run these on 1.9 you might get errors, but they won't explicitly say that the ruby version is wrong, so they'll be harder to debug.
By the way, you can have 2.3 as system ruby - sudo apt-get install ruby2.3

Is fuzzy matching with RubyGems gem install version possible?

Is there a way to specify that you want the latest version of gem within semantic versioning constraints? For instance, if I wanted the most recent version of the foo gem within the 2.x.x set of available gems, is there a way to have RubyGems install whatever the latest version before 3.0.0? Hypothetically something along the lines of: gem install foo --semantic-version 2 might make sense.
That's what pessimistic version constraints are for:
gem install --version '~> 2.0'

How to specify minimum bundler version for Gemfile?

When my Gemfile is using :mri_20, and previous versions of bundler do not support that, is it a good idea to add
gem 'bundler', '~>1.3.5'
to the Gemfile? Is there a better way to enforce a minimum bundler version?
This won't have any affect on the bundler used to manage the gems in the Gemfile. The version of bundler that's used is the one that's available in your current ruby environment.
The best way to manage this is with gemsets - you can create a gemset with a known, working version of bundler and always switch to that gemset when working with that project.
To check the bundler version, run:
$ bundle --version
Bundler version 1.3.5
If you want to enforce the bundler version when running bundle install, put this at the top of the Gemfile:
# Gemfile
if Gem::Version.new(Bundler::VERSION) < Gem::Version.new('1.3.5')
abort "Bundler version >= 1.3.5 is required"
end

Ruby: Rails: Which version of a gem is used?

I have a Rails 3 application which has the following line in the Gemfile.
gem 'sqlite3', '1.3.6'
However, if I list my local gems I get:
> gem list sqlite3 --local
*** LOCAL GEMS ***
sqlite3 (1.3.6, 1.3.4)
When my Rails apps does a
require 'sqlite3'
which version of the gem is selected? Is it that the first gem in the list is selected? Is there a way to tell the Ruby runtime to use version 1.3.4 even if version 1.3.6 is installed, and mandated by the Gemfile?
You could find out with
bundle exec gem list sqlite3
Either the Gemfile will specify a version or Gemfile.lock will have the version.
Examples:
Gemfile:
gem 'tiny_tds', '0.5.0'
Gemfile.lock:
tiny_tds (0.5.0)
Edit: if you want to see the version, use iltempos' suggestion or in the rails console type
1.9.3p194 :001 > SQLite3::VERSION
=> "1.3.6"
the Gemfile list all the dependencies of your Rails application, you can add constrains about the version to use for each gem. In your example you specified that your application depends on sqlite3 1.3.6 so it will use the version 1.3.6.
In general the exact version of the gems required by your application are in the Gemfile.lock.
There are several syntaxes you can you to specify the versions:
gem 'gemname', '1.2.3' - Requires gemname version 1.2.3
gem 'gemname', '>= 1.2.3' - Requires gemname version 1.2.3 or any higher version. Can break things
gem 'gemname', '~> 1.2' - Require gemname 1.2 or a minor update like 1.2.9. You get updates but not mayor one which could break compatibility
The interesting thing to note is that once the Gemfile.lock is written and checked in your version control, it's shared between all the members of the team that all will use the same, exact, versions of the required gems.
If you need to update a required gem you can do that in 2 steps:
Update the Gemfile if needed
Run bundle update gemname
The step 2 will download the new gem version (if there is a new version respecting the constrain in the Gemfile), install it and update the Gemfile.lock accordingly.

Resources