INFRACTIONS
One of the rarely mentioned edge cases with the way in which RubyGems loads library files, and in fact the way Ruby's load system works in general, is that the lib space is a free for all. Any library can drop any file into their package's loadpath (i.e. the lib/ directory) and potentially clobber a file of the same name in some one else's library.
Here's an example irb session that demonstrate the issue. Here I already added
a matrix.rb
file, that does nothing but puts "HERE!"
, to the cuts
gem and installed it.
require 'rubygems'
=> true
gem "cuts"
=> true
require 'matrix'
HERE!
=> true
Now, a good gem citizen knows to put their scripts in a directory with the same name as their gem package, nonetheless you might be surprised to see how often this simple but important practice is violated. Consequently the order in which such gems are searched becomes of paramount importance --something that worked just fine on one machine can suddenly stop working on another for no obvious reason.
ISOLATIONISTS
It is also worth noting that the recent crop of gem isolation systems, such as Bundler and Isolate, while serving to reduce the likelihood of possible name clashes still do not fully remedy the issue. They merely reduce the number of gems that could cause the problem for any given dependent application.
SOLUTION
The Ruby and Gem wedges solves the issue entirely by allowing us to load files from a single gem and only that gem. It does so by adding a new valid syntax to Ruby's #load and #require methods. As an example, let's say we wanted to load the troff.rb script from the unroller library.
require 'troff', :from => 'unroller'
The colon is used to separate the gem name from the rest of the pathname. With this we can be 100% certain that the troff.rb file was required from the unroller gem and not a 'troff.rb' file from any other gem that might have created a script by the same name.
EXTENT OF THE ISSUE
You might be suprised to find out how many libraries violate the best practice of putting all thier scripts in a subdirectory by the same name as the package. Just on my development system alone, which really has but a small number of gems installed, there are quite a few cases.
ParseTree-2.2.0/lib/:
composite_sexp_processor.rb
parse_tree.rb
sexp.rb
sexp_processor.rb
unified_ruby.rb
unique.rb
ParseTree-3.0.3/lib/:
gauntlet_parsetree.rb
parse_tree.rb
parse_tree_extensions.rb
unified_ruby.rb
unique.rb
RedCloth-4.1.9/lib/:
case_sensitive_require
redcloth
redcloth.rb
redcloth_scan.so
RedCloth-4.2.2/lib/:
case_sensitive_require
redcloth
redcloth.rb
redcloth_scan.so
tasks
ZenTest-4.0.0/lib/:
autotest
autotest.rb
focus.rb
functional_test_matrix.rb
multiruby.rb
unit_diff.rb
zentest.rb
zentest_mapping.rb
bossman-0.4.1/lib/:
bossman
bossman.rb
extensions
builder-2.1.2/lib/:
blankslate.rb
builder
builder.rb
chardet-0.9.0/lib/:
Big5Freq.rb
Big5Prober.rb
CharDistributionAnalysis.rb
CharSetGroupProber.rb
CharSetProber.rb
CodingStateMachine.rb
ESCSM.rb
EUCJPProber.rb
EUCKRFreq.rb
EUCKRProber.rb
EUCTWFreq.rb
EUCTWProber.rb
EscCharSetProber.rb
GB2312Freq.rb
GB2312Prober.rb
HebrewProber.rb
JISFreq.rb
JapaneseContextAnalysis.rb
LangBulgarianModel.rb
LangCyrillicModel.rb
LangGreekModel.rb
LangHebrewModel.rb
LangHungarianModel.rb
LangThaiModel.rb
Latin1Prober.rb
MBCSGroupProber.rb
MBCSSM.rb
MultiByteCharSetProber.rb
SBCSGroupProber.rb
SJISProber.rb
SingleByteCharSetProber.rb
UTF8Prober.rb
UniversalDetector.rb
eventmachine-0.12.10/lib/:
em
eventmachine.rb
evma
evma.rb
fastfilereaderext.so
jeventmachine.rb
pr_eventmachine.rb
rubyeventmachine.so
hpricot-0.8.1/lib/:
fast_xs.so
hpricot
hpricot.rb
hpricot_scan.so
html5-0.10.0/lib/:
core_ext
html5
html5.rb
http_router-0.2.5/lib/:
ext
http_router
http_router.rb
httpclient-2.1.5.2/lib/:
http-access2
http-access2.rb
httpclient
httpclient.rb
tags
linecache-0.43/lib/:
linecache.rb
trace_nums.so
tracelines.rb
liquid-2.0.0/lib/:
extras
liquid
liquid.rb
mail-2.2.5/lib/:
VERSION
mail
mail.rb
mail.rbc
tasks
maruku-0.5.9/lib/:
maruku
maruku.rb
sort_prof.rb
mechanize-0.9.3/lib/:
mechanize.rb
www
memcache-client-1.7.8/lib/:
continuum_native.rb
memcache.rb
memcache_util.rb
mocha-0.9.8/lib/:
mocha
mocha.rb
mocha_standalone.rb
stubba.rb
packr-3.1.0/lib/:
packr
packr.rb
string.rb
qed-2.3.0/lib/:
qed
qed.rb
qedoc
quality_extensions-1.1.6/lib/:
Xfind_bug_test.rb
quality_extensions
quality_extensions.rb
radiant-0.8.2/lib/:
annotatable.rb
autotest
generators
inheritable_class_attributes.rb
local_time.rb
login_system.rb
method_observer.rb
plugins
radiant
radiant.rb
simpleton.rb
task_support.rb
tasks
rails-2.3.5/lib/:
code_statistics.rb
commands
commands.rb
console_app.rb
console_sandbox.rb
console_with_helpers.rb
dispatcher.rb
fcgi_handler.rb
initializer.rb
performance_test_help.rb
rails
rails_generator
rails_generator.rb
railties_path.rb
ruby_version_check.rb
rubyprof_ext.rb
source_annotation_extractor.rb
tasks
test_help.rb
webrick_server.rb
railties-3.0.0.beta/lib/:
generators
rails
rails.rb
ramaze-2010.01/lib/:
proto
ramaze
ramaze.rb
vendor
rdiscount-1.3.5/lib/:
markdown.rb
rdiscount.rb
rdiscount.so
red-3.5.0/lib/:
javascripts
red
red.rb
red-4.1.7/lib/:
red
red.rb
source
rmagick-2.12.2/lib/:
RMagick.rb
RMagick2.so
rvg
ruby-prof-0.7.3/lib/:
ruby-prof
ruby-prof.rb
ruby_prof.so
unprof.rb
ruby_parser-2.0.4/lib/:
gauntlet_rubyparser.rb
ruby_lexer.rb
ruby_parser.rb
ruby_parser.y
ruby_parser_extras.rb
rubygems-update-1.3.7/lib/:
gauntlet_rubygems.rb
rbconfig
rubygems
rubygems.rb
ubygems.rb
s3sync-1.2.5/lib/:
HTTPStreaming.rb
S3.rb
S3_s3sync_mod.rb
S3encoder.rb
s3config.rb
s3try.rb
thread_generator.rb
version.rb
sexp_processor-3.0.1/lib/:
composite_sexp_processor.rb
sexp.rb
sexp_processor.rb
sexp_processor-3.0.3/lib/:
composite_sexp_processor.rb
sexp.rb
sexp_processor.rb
unique.rb
slim_scrooge-1.0.5/lib/:
callsite_hash.so
slim_scrooge
slim_scrooge.rb
treetop-1.2.4/lib/:
metagrammar.rb
treetop
treetop.rb
unroller-1.0.0/lib/:
troff.rb
tron.rb
unroller.rb
xcb-0.0.1/lib/:
directory_monitor.rb
extreme_continuous_builder.rb
notifiers.rb
stacking_config.rb
xcb_command.rb