Why Using require ‘rubygems’ Is Wrong
Ryan Tomayko, currently known as one of the lead developers of Sinatra, was definitely not mincing his words yesterday when he posted Why "require 'rubygems'" In Your Library/App/Tests Is Wrong:
You should never do this in a source file included with your library, app, or tests:
The system I use to manage my $LOAD_PATH is not your library/app/tests concern. Whether rubygems is used or not is an environment issue. Your library or app should have no say in the matter. Explicitly requiring rubygems is either not necessary or misguided.
When I use your library, deploy your app, or run your tests I may not want to use rubygems. When you "require 'rubygems'" in your code, you remove my ability to make that decision. I cannot unrequire rubygems, but you can not require it in the first place.
I hadn't thought of it this way before, but Ryan makes a lot of sense. The choice of library management system should be down to the environment and not enforced by a library or a single portion of a larger application.
Check out Ryan's post for the deeper rationale and see what you think. He's definitely got me thinking twice about using require 'rubygems' in my own scripts, although as the slow migration over to Ruby 1.9 begins, perhaps it will become a non-issue, since RubyGems is included with Ruby 1.9 and loaded by default. Thoughts?
January 30, 2009 at 3:51 am
If you go back in the ruby-talk archives, I suspect you'll find this position expressed early on in the development of RubyGems. The library was always intended to become part of the core, though, and "require 'rubygems'", a bit of a hack we're using in the meantime.
January 30, 2009 at 3:59 am
If anyone can find references on this, do post - much appreciated :)
January 30, 2009 at 4:30 am
There's also Minigems, so RubyGems is not the only show in town.
Although, RubyGems is integrating more code which helps packaging plugins as RubyGems even easier. Gem.find_files and Gem.searcher are both handy methods for finding files within other gems.
January 30, 2009 at 4:54 am
Unavoidable if you want to use one of the libraries that's part of the standard library but maintained separately, e.g. test-unit, rdoc and whatever else might show up.
I imagine this goes away in 2.x, though, with gems as part of the standard lib.
January 30, 2009 at 5:17 am
Is it unavoidable? I use RUBYOPT to automatically include rubygems so I don't have to use require 'rubygems' anywhere. I might be interpreting it wrong, but I think doing that is in the spirit of what Ryan's talking about.. that is, it's an environment thing, rather than forcing it into code other people might use.
January 30, 2009 at 6:12 am
Peter, what? If I want to use Test::Unit 2.x, I must call "gem 'test-unit'" to force it to use the gem instead of the stdlib, which means I must require rubygems. I mean, I suppose I could leave out an explicit require from the test file, but it's not going to work.
January 30, 2009 at 6:38 am
Tests are probably a bad example. I'm not entirely sure why Ryan included those in his list. Tests tend to be very much strapped to a specific implementation (as opposed to a library, which might need to fit in anywhere).
That said, if RUBYOPT is set to rubygems, though, then it should still work, since that makes the Ruby interpreter load RubyGems.. though it seems pointless to me since you're forcing the use of the gem with "gem" anyway ;-)
January 30, 2009 at 7:08 am
You can also require "rubygems" only on LoadError: http://github.com/henrik/slugalizer/blob/1e23d71cf1fdb0be269341137ced7fbf8ac73909/slugalizer.rb#L6-12
I'm sure Ryan does not like that either, but it's something of a compromise. If you use something other than rubygems, you have a chance to load it without. If not, it works without needing e.g. RUBYOPT.
January 30, 2009 at 7:34 am
I personally find that quite agreeable, Henrik. Now you mention it, I've seen that pattern in code before :)
January 30, 2009 at 8:20 am
I can see Ryan's point (kind of), but for 99.9% of people this is a complete non-issue. Rubygems may not be perfect, but for better or worse it's the system we have.
If Ryan wants to run in a non-rubygems environment, it seems to me the onus is on him to make it happen. Forcing everyone else to either explicitly require it on the command line every time they run anything at all, or set environment variables on their local machine, is just not going to happen.
Perhaps a more fruitful approach would be to lobby Ruby Core for a more customisable/overrideable load system. It is a bit hard-wired at the moment. However, it also works extremely well in the vast majority of cases, and I don't find Ryan's reasons to upset the status quo all that compelling.
January 30, 2009 at 9:25 am
I don't think Ryan's point is around whether RubyGems is any good or not - it's just that if you use require 'rubygems' you are forcing RubyGems' usage on someone who might not want it to be loaded up in certain situations (different versions of libraries between gems and local installs, etc).
January 30, 2009 at 10:55 am
Yeah. The reason I wrote the piece in the first place was because people seemed to misunderstand my original tweet and assumed I meant, "rubygems is always bad." It's not. I use rubygems all over the place. I just think it's unnecessary or inappropriate to explicitly "require 'rubygems'" in 99% of the cases.
I'm working under the theory that people require it explicitly because they see everyone else require it explicitly and also as a sort of general thing to try when things don't work ("throw require 'rubygems' in there - maybe that'll fix it!"). I'm trying to put forward the idea that, when your environment is not setup correctly, it's best to try to fix it in the environment instead of ramming something into the code and potentially breaking other people's environments.
Also, because I want to run my code under rubygems in one place doesn't mean I won't want to run that same code without rubygems in another. For webapps, I sometimes develop with gems but manage the $LOAD_PATH manually in production. I don't like the extra weight gems add to my RSS and find managing a vendor directory and RUBYOPT variable to be a quite simple way of getting around it.
Henrik's suggestion of rescuing LoadError and requiring rubygems there is a good compromise, IMO. I use that sometimes in tests. Adding "warn 'foolib not found - requiring rubygems'" to the rescue clause makes it even better.
And finally, I'm surprised no one has pointed out the irony/hypocrisy in all of this yet. Follow the "Sinatra" link in the first sentence of the original post. There's five lines of code on that page and one of them is ... that's actually what led to my initial tweet that "require 'rubygems' is a code smell". I find it funny that suggesting people add use that idiom is perfectly acceptable but that suggesting people use "ruby -rubygems" is somehow ugly. It should be the other way around, IMO.
January 30, 2009 at 11:04 am
Sorry Peter but I see RUBYOPT for rubygems as something bad.
We with One-Click Installer project had several bug reports and really weird errors coming from users with that option defined.
This also made really impossible to trace under some situation what was wrong with the user code.
There is also the issue with specific dependencies.
How I'm supposed to keep my libs that depends on public or semi-public API of other libraries when they decide to change it between minor and teeny version bumps?
That happened to me with win32-service and mongrel_service gem.
For certain packages there is no standard setup.rb distribution.
In latest One-Click Installer we decided not to turn RUBYOPT=rubygems on.
January 30, 2009 at 11:23 am
I can sort of understand the reasoning for this objection. But I have several cases in my project where I try to load some optional libraries and I define some code integrating them if they exist. If this dependencies were not "soft", I could simply error out of course.
begin; require 'rubygems'; rescue LoadError; end
begin; require 'Qt4'; rescue LoadError; end
begin; require 'Qt4'; rescue LoadError; end
begin; require 'narray'; rescue LoadError; end
January 30, 2009 at 11:56 am
The fact that Rubygems installs each gem under a different directory violates the Linux FHS (http://www.pathname.com/fhs/), as well as being source-intrusive. The Debian project describes these and many other issues in their Ruby packaging page:
Of course having Rubygems in the Ruby ecosystem has allowed for a lot of third party contributed code, but when we look at the Python and Perl equivalents, which do not share many of Rubygems design choices, I cannot avoid questioning whether we could have done a better job at it.
January 30, 2009 at 1:30 pm
A classic example of this is on debian/ubuntu where many of the most commonly used libraries are available via the standard s/w installation mechanism. It is easy to get fed up with having to patch the source of a library because it didn't cross the author's mind that the library their library depends on might be installed when rubygems isn't.
January 30, 2009 at 4:00 pm
rubygems is the worst thing that ever happened to Ruby. Period. Whoever invented this library has no idea regarding managing software packages on multiple servers.
January 31, 2009 at 1:41 pm
Cool, then provide an alternative mechanism (ala: stop ranting).
Go beta test and pay for debgem then, noone forces you to use rubygems.
You can download source packages and pretty much do whatever you want with them.
I heard noone complains about Perl CPAN (oh, except from Debian/Ubuntu guys), neither of Python Eggs (again except for the same folks).
Don't want rubygems installed? fake it.
February 2, 2009 at 8:17 pm
Requiring users of your library to set RUBYOPT is very onerous. Things should work out of the box. Period.
Rubygems is *in* Ruby now. The debate is over. There's no reason to change your codebase to pacify people who haven't accepted this.
February 8, 2009 at 6:48 pm
I find using the gem method to be a very common and sane way to do dependency management. I do not see a commonly used alternative in the ruby world.