RubyGems Deprecations

drbrain | Thu, 19 May 2011 01:03:47 GMT

Posted in

I've been maintaining RubyGems a long, long time now. I believe my first release was 0.9.5 back at the end of 2007, four and a half years! In that time I've looked at pretty much every line of RubyGems. I know the history and intent behind most of the classes that exist in RubyGems and what purposes they served. (For more on that history watch my RubyConf 2010 presentation, History of RDoc and RubyGems.)

One thing I haven't done enough of in RubyGems is cleanup of the little things that make RubyGems easy to use as a library. While I've done some work to make it easier to use as a library I've ignored too many of the little things that make a polished library. Over the past several releases Ryan Davis has been driving these small cleanups to make RubyGems easier to use and maintain.

There are many things that are clumsy to do in RubyGems 1.7 and earlier due to poor encapsulation that are now clean, simple and intuitive in RubyGems 1.8.

Given a Gem::Specification, how do you activate it?

# RubyGems 1.8

spec.activate

# RubyGems 1.7 and older

Gem.activate spec.name, spec.version

Given a Gem::Specification, how do you determine various directories for files in the gem?

# RubyGems 1.8

spec.ri_dir
spec.cache_dir
spec.bin_dir
# etc.

# RubyGems 1.7 and older

File.join spec.installation_path, 'doc', 'ri'
File.join spec.installation_path, 'cache'
File.join spec.installation_path, 'gems', spec.full_name, 'bin'

How do you list the name of every gem you have installed?

# RubyGems 1.8

Gem::Specification.all_names

# RubyGems 1.7 and older

Gem.source_index.map { |_, s| s.full_name }

... and so on. While these look like trivial examples, the work underlying these changes is large. Also, note that for each of these examples the Ruby 1.7 and older code still works, but it will complain.

Deprecations

Gem::SourceIndex has been deprecated because it has no real purpose anymore. Back in the olden days before RubyGems 1.2 Gem::SourceIndex was used to determine which gems you could install (I cover this my RubyConf 2010 talk above). It got pressed into service as a local repository of gems as well, but since RubyGems 1.2 it's really become just a wrapper around a Hash.

Instead of keeping around the baggage of an ancient, mostly-useless Hash wrapper we've moved the important functionality into Gem::Specification as class methods. Now Gem::Specification behaves as an Enumerable container.

Gem::GemPathSearcher was used to figure out which files belonged to which gems so require 'rake' would activate the rake gem. Unfortunately its implementation was so complicated I could barely understand it let alone improve it.

Instead of keep around a complex, incomprehensible class we moved the functionality into Gem::Specification as well.

# RubyGems 1.8

spec = Gem::Specification.find_by_path 'rake'

# RubyGems 1.7 and older

spec = Gem.searcher.find 'rake'

Gem::Specification#default_executable has been deprecated as it was intended to be used for a feature that never was implemented. The idea behind it was a command like gem exec rake that would invoke the proper version of rake through RubyGems. Since this was never implemented we've deprecated the attribute.

In all, RubyGems releases since 1.4 have focused on cleaning up much of the historic cruft left behind after years of releases. We've properly encapsulated much of the functionality so it's implemented in a sensible place. Most of the deprecations we have made can be fixed by a single line change or running a single command.

Deprecation Warnings

I learned from the removal of #version_requirements that people will ignore deprecation warnings if you only output them once. When RubyGems 1.5 was released with the removal #version_requirements people were confused about why their Rails applications broke.

A single warning that prints every time you start your rails app and says Gem::Dependency#version_requirements is deprecated and will be removed on or after August 2010 can be swept under the rug, so for this release we decided to print warnings every time you use deprecated functionality.

Since we knew that users would need to update their gemspecs we provided the following instructions:

After installing RubyGems 1.8.0 you will see deprecations when loading your exsting gems. Run `gem pristine --all --no-extensions` to regenerate your gem specifications safely.

Currently RubyGems does not save the build arguments used to build gems with extensions. You will need to run `gem pristine gem_with_extension -- --build-arg` to regenerate a gem with an extension where it requires special build arguments.

These instructions were printed when you ran gem update --system and running the command would immediately solve the problem. They were also duplicated in my blog posts for the RubyGems 1.8.0, 1.8.1 and 1.8.2 (which also fixed a bug in gem pristine for some rvm users).

If you upgraded from RubyGems 1.7 to RubyGems 1.8.1 you may have missed the note about running gem pristine. We've changed our install process to print release notes from the version you're upgrading from to the version you're upgrading to.

The remaining deprecations should be rarely encountered as the deprecated functionality is rarely used outside RubyGems.

PS: If you write a library that interacts with RubyGems beyond require you should be on the RubyGems-developers mailing list. comments

Comments RSS FEED

In any case, it is still unacceptable that a Rubygems release completely breaks Rails, one of the most used Ruby projects. And with no semantic versioning at all. Besides, the series of releases, like 1.7.0, .1 and .2 shows that there are no real tests before releasing a new rubygems version. Are there .RC releases?

Rubygems should not be only about the require, and if you need to be in a special group to go beyond that, something is really messed up.

We know that there is a lot of work there and we appreciate it, but for a tool that is inside Ruby core, we would expect more care and attention.

Paul Kool said about 14 hours later

Breaking freezing gems is not completely breaking Rails which is the only thing that Rails forgot to patch in the latest 2.3 release.

The latest RubyGems is not obligated to work with every version of Rails released ever in the same way that the latest version of Rails is not obligated to work with every Ruby version ever released.

If you’re still running Rails 2.3.5 (six versions behind the latest Rails 2.3 release) how can you reasonably claim that the latest version of RubyGems must work with it?

To claim that there is no testing is completely false. Ryan and I both install RubyGems from trunk and use it in our daily work. Unfortunately there are 25,000 unique gems (and over 120,000 releases) so exhaustive testing with every possible combinations of gems is computationally infeasible. (Well, due to EC2, merely financially infeasible.)

I’ve created release candidates before, but they rarely remove bugs (for any project) as people rarely install them and even more rarely use a release candidate enough to provide feedback that would discover bugs.

RubyGems is not only about require, but in the case of bundler (and others) people sometimes use RubyGems incorrectly. The mailing list is the way you can learn in advance about things that may break your usage so we can work together to find a solution.

Finally, there’s no more than ten people supporting a tool that’s used by hundreds of thousands of people daily. None of us are paid to do this work and none of us can work full-time on it. We’re going to miss something occasionally and when we do we need help to make it better in a calm and rational manner.

If you think we’re not providing enough care and attention join the mailing list, install from trunk and tell us about what we’ve broken.

Eric Hodel said about 17 hours later

Thanks a million for documenting the why and providing solutions for the deprecated APIs, Eric.
I gotta admit, spec.activate is definitely a ton nicer.

Rob Gleeson said about 18 hours later

Thanks for the article. It was helpful to see more explanation about the positive changes in RubyGems that have brought so many loud warnings to the fore.

Mogden said about 20 hours later

“gem pristine” just made my day. Thank you!

Eric Marden said about 21 hours later

The massive amount of deprecation warnings definitely made me take notice and figure out the solution. Good move! While I can’t begin to understand the internals of RubyGems, it seems like you and Ryan are continuing to move it in the right direction. Thanks for all the hard work!

Doug Johnston said 2 days later

Comments are disabled