I maintain a gem with dependencies that are stored in a Gemfile, for example:
gem 'foo', '~> 1.5'
gem 'bar', '~> 2.0.5'
Thanks to pessimistic version constraints, bundler will by default install the latest 1.x version of foo, but can compromise on a lower version if my gem is used in conjunction with another that requires (for example) foo =1.6.2
Question: Is there a simple way to get bundler to install all of the minimum versions of my dependencies (in this case, foo =1.5.0 and bar =2.0.5) so that I can test whether, after I write some new functionality, my gem will still work in combination with other environments that use those lower versions?
Or, is the only way for me to manually reinstall all of the minimum versions and then run my tests?
Because we decided to use Rubygems' Requirement class, there isn't a way to specify the lowest version. I vaguely recall an automated testing tool to help you iterate over dependency versions that you want to test against, but it's extremely hard to automate because there are an exponential number of possible version combinations. I suggest creating a second Gemfile with the oldest versions you want to test against, and using BUNDLE_GEMFILE to run against that Gemfile in an additional CI build.
I saw your question in IRC... from my understanding, there's no way to do it without changing your Gemfile. Sorry. :(
https://github.com/carlhuda/bundler/blob/master/lib/bundler/cli.rb for reference
Related
In a Ruby project, I have a dependency that is used by my gem, and also used by a gem that my gem depends on. Specifically, I use sawyer and octokit, so my-gem depends on sawyer, and octokit also depends on sawyer.
Right now, this is what I have:
spec.add_runtime_dependency 'octokit', '~> 6.0'
spec.add_runtime_dependency 'sawyer' # no version constraint
This works fine for now, but I'm wondering if this is the best way to do things. The intent is to use whatever version of sawyer that octokit uses, but I'm wondering if it is perhaps better to have a version constraint, or to just omit sawyer from the gemspec.
TL;DR
Gems are most often meant to be libraries, not applications, although there are exceptions. Libraries should only list gems in their gemspecs that are first class dependencies, and avoid building in unnecessary or overly restrictive constraints on the gem.
What to List and How Much to Constrain
When to List and Constrain Gems
As a rule of thumb, gems (as opposed to applications) shouldn't constrain gem dependencies except when necessary to avoid specific conflicts. Otherwise, you may unnecessarily constrain the use of your gem by others who have other constraints or need later or earlier versions for whatever reason.
So, assuming your gem really needs octokit >= 6.0 and <= 6.1, then go ahead and constrain it that way. Otherwise, drop or reduce the level of constraint to the greatest practical extent. For example, if a dependency follows semantic versioning then you should only constrain its major version (if you constrain it at all) unless there's a known problem with a particular minor or patch version that you need to avoid. In other words, be as liberal as possible with library constraints.
Don't Duplicate the Responsibilities of Bundler or Upstream Libraries
If you aren't calling the sawyer gem directly, then it's really just a dependency of octokit that's unrelated to your gem. In such cases, you should trust Bundler's dependency resolution for octokit to pull in the things it needs to function unless you find a specific problem. Otherwise, you will end up managing dependencies that may change upstream, which creates a needless burden on both maintenance of your library and any developers who use it.
On the other hand, if your gem makes direct calls to sawyer, then it's a first class dependency that should be listed in your gemspec. Again, if needed at all, keep your constraints as loose as you can to avoid excessive constraints or having to continuously update minor or patch versions down the road.
In short, if the only reason to list sawyer is because octokit currently uses it then Bundler should manage the dependency. Ideally, the octokit maintainers should update octokit's dependencies when needed. Only manage third-party dependencies when absolutely necessary, and only as long as it takes to get essential fixes merged into the upstream library (or into a suitable fork or library replacement if the fixes you need won't be merged).
Why should one care about specifying gem version at all if bundler detects Ruby version and manages to get the latest release to match that version. If I'm not a fond of newer version personally, I would disable incrementing with ~> 1.4.4 and in other cases I'd let bundler manage stuff with putting gem name into Gemfile without any argument
The approach you are suggesting - start with the latest version and pin if problems are experienced - works fine for projects that are 1) actively maintained and 2) tolerant of breakage.
Now imagine you have to deliver this project to a customer who then will run it for a year or longer and you won't be there to support it. In this case simply getting the latest release of all dependencies is not necessarily the best strategy. Maybe you would proactively specify major versions of all of your important dependencies instead. Potentially even lock to minor versions which does give more stability at the cost of missing security updates/bug fixes.
Isn't the Gemfile.lock a hack used to perpetuate bad practices in dependency version control?
I.e. Shouldn't developers set the dependency version ranges strictly in the Gemfile?
For example if my Gemfile says that I depend on gem A version 1.0.1 or versions [1.0-2.0), why would I need the .lock?
No, Gemfile.lock makes a lot of sense and is crucial to the concept of automatically picking gem versions. As a developer, you do not need to bother about exact version numbers. You can say "give me whatever version of gem X fits all other versions of all other gems" (by just saying gem 'xyz' without any further information). Or you can tell it to stay within the bugfixing line of an older version of a gem (gem 'xyz', '~> 2.3.0') or whatever.
By adding the exact version in Gemfile.lock you then make sure that the versions stay consistent for all developers (and environments). You make the act of upgrading to a newer version of a gem a conscious (and well-documented) choice instead of a random part of your build/deploy process.
why would I need the .lock?
to install exactly the same versions as all the other guys in the team. Or install in production the same versions that you use in development.
It might happen that a new version of some gem is released while you were collecting sign-offs for your release. You better be sure you install/load exactly the versions that you developed/tested with.
I would like to publish a Gem I wrote so it can be installed by users via gem install my-gem. The RubyGems guide on how to Make Your Own Gem has a section on using Test::Unit and rake to test my code.
Writing tests is going to add significant development time to a Gem that (in this case) is somewhat trivial in nature. I'd like to avoid it if possible.
Is it necessary to write tests in order to publish a Gem or just a suggestion?
It is a (good) suggestion, and is in no way required to publish a gem.
I am new in rails 3 and need to know how rails manage gem dependencies in rails 3 also i need to know how it is differ from older rails version?
I don't think there have been any changes to the basic workflow. It is enabled by default in Rails 3 whereas you had to set it up manually in 2.3.x.
Basically, bundler takes the requirements set out in your Gemfile (gem name, minimum version, exact version etc), and it works out a set of gems that meet those requirements. It records the exact versions in Gemfile.lock, and takes responsiblity for 'requiring' any gems you need in your application.
It's worth reading the manual, especially with respect to the various deployment options.
See gembundler.com for a summary of how to use it in Rails 3.