rubygems binstubs allow selecting which version of the gem to load by specifying the version as the first argument which begins and ends with _:
# This file was generated by RubyGems.
# The application 'rdoc' is installed as part of a gem, and
# this file is here to facilitate running it.
version = ">= 0"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
gem 'rdoc', version
load Gem.bin_path('rdoc', 'ri', version)
$ mygem _1.2.3_ --args ...
Unfortunately, this prevents executables from operating on file names that begin and end with _
$ mygem _some_file_
/usr/share/rubygems/rubygems/requirement.rb:90:in `parse': Illformed requirement ["some_file"] (Gem::Requirement::BadRequirementError)
from /usr/share/rubygems/rubygems/requirement.rb:120:in `block in initialize'
from /usr/share/rubygems/rubygems/requirement.rb:120:in `map!'
from /usr/share/rubygems/rubygems/requirement.rb:120:in `initialize'
from /usr/share/rubygems/rubygems/requirement.rb:55:in `new'
from /usr/share/rubygems/rubygems/requirement.rb:55:in `create'
from /usr/share/rubygems/rubygems/dependency.rb:58:in `initialize'
It is my humble opinion that it would be safer to specify the version via a environment variable.
$ GEM_VERSION=1.2.3 mygem _some_file_ --args ....
Env variable or doubled underscores, make your choice.
This is true and sadly a limitation of Rubygems. We can't change it due to the massive backwards compat issue.
@evanphx couldn't we start supporting environment variables, so that one day the _1.2.3_ syntax can be deprecated?
@postmodern +1 such "meta behavior" should not be silently included in the args of any executable, we should start get a rid of this behavior.
I also noticed that older versions of rubygems (1.8.23) do check if the _..._ argument is a valid version using Gem::Version.correct?. This would prevent _bar_ from being confused as a version requirement, but would still prevent gems from accepting files named _1_ or _1.2.3_. This appears to have been removed in newer versions of rubygems.
I fail to see why the following code wouldn't be backwards compatible:
version = ">= 0"
if ENV['GEM_VERSION'] then
version = ENV['GEM_VERSION']
elsif ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
version = $1
This would preserve the legacy _1.2.3_ convention, but also allow users to bypass it and use GEM_VERSION=1.2.3 myutil _somefile_.
GEM_VERSION=1.2.3 myutil _somefile_
@postmodern I'd probably add a warning to the code if both, the ENV variable and the "magic argument" are present?
I agree, we can do better.
Thank you @drbrain for re-opening. I was going to start working on a Pull Request, but was confused by the purpose of this code:
str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
Is this required for the regexp match or for passing the version argument into the gem method?
Only allow valid version overrides in bin stubs
This allows users to provide arguments like:
which cannot possibly match a version. Previously RubyGems would give
a version parsing exception for such input.
The force_encoding is required for regexp match, yes.
I know I'm over a year late here, but I just wanted to note that this change did break using anything but exact version in the version qualifiers. For example, you used to be able to do:
$ rails _~\>3.2_ new foo
The "\" there is just needed for shell escaping purposes. The point is that "~> 3.2" used to be considered a valid version identifier for picking gem versions.
That no longer works and instead an exact version has to be specified, such as:
$ rails _3.2.21_ new foo
It's not a huge problem, but is an issue I hit when updating RubyGems versions.