diff options
author | Pan Thomakos <pan.thomakos@gmail.com> | 2015-04-16 23:00:29 -0700 |
---|---|---|
committer | Pan Thomakos <pan.thomakos@gmail.com> | 2015-04-17 08:37:14 -0700 |
commit | 2c8dd13c5617e402caebc0c113abc53cf8f4df58 (patch) | |
tree | 4b66bf34349dd33aecbe4243089e402a5f98a8d5 | |
parent | c7fce7422f467407f7544062661cbbb726b9ef38 (diff) | |
download | bundler-2c8dd13c5617e402caebc0c113abc53cf8f4df58.tar.gz |
Allow fuzzy ruby version requirements.
* Use `Gem::Requirement` and `Gem::Version` to parse and check
ruby version, engine_version and patchlevel requirements.
* Added new class `RubyVersionRequirement` to handle this functionality.
* Added tests that cover the `RubyVersionRequirement` class.
-rw-r--r-- | CHANGELOG.md | 6 | ||||
-rw-r--r-- | lib/bundler.rb | 1 | ||||
-rw-r--r-- | lib/bundler/definition.rb | 8 | ||||
-rw-r--r-- | lib/bundler/ruby_dsl.rb | 2 | ||||
-rw-r--r-- | lib/bundler/ruby_version.rb | 48 | ||||
-rw-r--r-- | spec/bundler/ruby_version_spec.rb | 80 | ||||
-rw-r--r-- | spec/other/platform_spec.rb | 26 |
7 files changed, 118 insertions, 53 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 64d7542a5f..33ec3a5f55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Next Release + +Features: + + - Allow fuzzy ruby version, patchlevel and engine version requirements (#70, @panthomakos) + ## 1.9.4 (2015-04-13) Bugfixes: diff --git a/lib/bundler.rb b/lib/bundler.rb index 49d9fd6883..7fcc07d1eb 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -44,6 +44,7 @@ module Bundler autoload :SourceList, 'bundler/source_list' autoload :Specification, 'bundler/shared_helpers' autoload :SystemRubyVersion, 'bundler/ruby_version' + autoload :RubyVersionRequirement,'bundler/ruby_version' autoload :UI, 'bundler/ui' class BundlerError < StandardError diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 9643144427..d36e56a86e 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -42,7 +42,7 @@ module Bundler # @param sources [Bundler::SourceList] # @param unlock [Hash, Boolean, nil] Gems that have been requested # to be updated or true if all gems should be updated - # @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version + # @param ruby_version [Bundler::RubyVersionRequirement, nil] Requested Ruby Version # @param optional_groups [Array(String)] A list of optional groups def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = []) @unlocking = unlock == true || !unlock.empty? @@ -386,11 +386,7 @@ module Bundler when :engine_version "Your #{Bundler.ruby_version.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}" when :patchlevel - if !expected.is_a?(String) - "The Ruby patchlevel in your Gemfile must be a string" - else - "Your Ruby patchlevel is #{actual}, but your Gemfile specified #{expected}" - end + "Your Ruby patchlevel is #{actual}, but your Gemfile specified #{expected}" end raise RubyVersionMismatch, msg diff --git a/lib/bundler/ruby_dsl.rb b/lib/bundler/ruby_dsl.rb index b29fc019a7..c1f83d3ad7 100644 --- a/lib/bundler/ruby_dsl.rb +++ b/lib/bundler/ruby_dsl.rb @@ -5,7 +5,7 @@ module Bundler raise GemfileError, "Please define :engine" if options[:engine_version] && options[:engine].nil? raise GemfileError, "ruby_version must match the :engine_version for MRI" if options[:engine] == "ruby" && options[:engine_version] && ruby_version != options[:engine_version] - @ruby_version = RubyVersion.new(ruby_version, options[:patchlevel], options[:engine], options[:engine_version]) + @ruby_version = RubyVersionRequirement.new(ruby_version, options[:patchlevel], options[:engine], options[:engine_version]) end end end diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb index da6ebae080..224a5ec60e 100644 --- a/lib/bundler/ruby_version.rb +++ b/lib/bundler/ruby_version.rb @@ -38,26 +38,6 @@ module Bundler patchlevel == other.patchlevel end - # Returns a tuple of these things: - # [diff, this, other] - # The priority of attributes are - # 1. engine - # 2. ruby_version - # 3. engine_version - def diff(other) - if engine != other.engine && @input_engine - [ :engine, engine, other.engine ] - elsif version != other.version - [ :version, version, other.version ] - elsif engine_version != other.engine_version && @input_engine - [ :engine_version, engine_version, other.engine_version ] - elsif patchlevel != other.patchlevel && @patchlevel - [ :patchlevel, patchlevel, other.patchlevel ] - else - nil - end - end - def host @host ||= [ RbConfig::CONFIG["host_cpu"], @@ -114,4 +94,32 @@ module Bundler RUBY_PATCHLEVEL.to_s end end + + class RubyVersionRequirement < RubyVersion + # Returns a tuple of these things: + # [diff, this, other] + # The priority of attributes are + # 1. engine + # 2. ruby_version + # 3. engine_version + def diff(other) + if engine != other.engine && @input_engine + [ :engine, engine, other.engine ] + elsif !version || !matches?(version, other.version) + [ :version, version, other.version ] + elsif @input_engine && !matches?(engine_version, other.engine_version) + [ :engine_version, engine_version, other.engine_version ] + elsif @patchlevel && !matches?(patchlevel, other.patchlevel) + [ :patchlevel, patchlevel, other.patchlevel ] + else + nil + end + end + + private + + def matches?(requirement, version) + Gem::Requirement.create(requirement).satisfied_by?(Gem::Version.new(version)) + end + end end diff --git a/spec/bundler/ruby_version_spec.rb b/spec/bundler/ruby_version_spec.rb new file mode 100644 index 0000000000..32d52cbfe4 --- /dev/null +++ b/spec/bundler/ruby_version_spec.rb @@ -0,0 +1,80 @@ +require "spec_helper" +require "bundler/ruby_version" + +describe Bundler::RubyVersion do + def requirement(version, patchlevel=nil, engine=nil, engine_version=nil) + Bundler::RubyVersionRequirement.new( + version, patchlevel, engine, engine_version) + end + + def version(version, patchlevel=nil, engine=nil, engine_version=nil) + Bundler::RubyVersion.new(version, patchlevel, engine, engine_version) + end + + it "matches simple version requirements" do + expect(requirement("2.0.0").diff(version("2.0.0"))).to be_nil + end + + it "matches simple patchlevel requirements" do + req = requirement("2.0.0", "645") + ver = version("2.0.0", "645") + + expect(req.diff(ver)).to be_nil + end + + it "matches engine" do + req = requirement("2.0.0", "645", "ruby") + ver = version("2.0.0", "645", "ruby") + + expect(req.diff(ver)).to be_nil + end + + it "matches simple engine version requirements" do + req = requirement("2.0.0", "645", "ruby", "2.0.1") + ver = version("2.0.0", "645", "ruby", "2.0.1") + + expect(req.diff(ver)).to be_nil + end + + it "detects engine discrepancies first" do + req = requirement("2.0.0", "645", "ruby", "2.0.1") + ver = requirement("2.0.1", "643", "rbx", "2.0.0") + + expect(req.diff(ver)).to eq([:engine, "ruby", "rbx"]) + end + + it "detects version discrepancies second" do + req = requirement("2.0.0", "645", "ruby", "2.0.1") + ver = requirement("2.0.1", "643", "ruby", "2.0.0") + + expect(req.diff(ver)).to eq([:version, "2.0.0", "2.0.1"]) + end + + it "detects engine version discrepancies third" do + req = requirement("2.0.0", "645", "ruby", "2.0.1") + ver = requirement("2.0.0", "643", "ruby", "2.0.0") + + expect(req.diff(ver)).to eq([:engine_version, "2.0.1", "2.0.0"]) + end + + it "detects patchlevel discrepancies last" do + req = requirement("2.0.0", "645", "ruby", "2.0.1") + ver = requirement("2.0.0", "643", "ruby", "2.0.1") + + expect(req.diff(ver)).to eq([:patchlevel, "645", "643"]) + end + + it "successfully matches gem requirements" do + req = requirement(">= 2.0.0", "< 643", "ruby", "~> 2.0.1") + ver = version("2.0.0", "642", "ruby", "2.0.5") + + expect(req.diff(ver)).to be_nil + end + + it "successfully detects bad gem requirements" do + req = requirement(">= 2.0.0", "< 643", "ruby", "~> 2.0.1") + ver = version("2.0.0", "642", "ruby", "2.1.0") + + expect(req.diff(ver)).to eq([:engine_version, "~> 2.0.1", "2.1.0"]) + end +end diff --git a/spec/other/platform_spec.rb b/spec/other/platform_spec.rb index ad9390cfa8..2c161f9e4b 100644 --- a/spec/other/platform_spec.rb +++ b/spec/other/platform_spec.rb @@ -203,7 +203,6 @@ G let(:engine_incorrect) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{not_local_tag}\", :engine_version => \"#{RUBY_VERSION}\"" } let(:engine_version_incorrect) { "ruby \"#{RUBY_VERSION}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_engine_version}\"" } let(:patchlevel_incorrect) { "#{ruby_version_correct}, :patchlevel => '#{not_local_patchlevel}'" } - let(:patchlevel_fixnum) { "#{ruby_version_correct}, :patchlevel => #{RUBY_PATCHLEVEL}1" } def should_be_ruby_version_incorrect expect(exitstatus).to eq(18) if exitstatus @@ -225,11 +224,6 @@ G expect(out).to be_include("Your Ruby patchlevel is #{RUBY_PATCHLEVEL}, but your Gemfile specified #{not_local_patchlevel}") end - def should_be_patchlevel_fixnum - expect(exitstatus).to eq(18) if exitstatus - expect(out).to be_include("The Ruby patchlevel in your Gemfile must be a string") - end - context "bundle install" do it "installs fine when the ruby version matches" do install_gemfile <<-G @@ -1225,25 +1219,5 @@ G should_be_patchlevel_incorrect end end - - it "fails when the patchlevel is a fixnum" do - simulate_ruby_engine "jruby" do - update_repo2 do - build_gem "activesupport", "3.0" - update_git "foo", :path => lib_path("foo") - end - - gemfile <<-G - source "file://#{gem_repo2}" - gem "activesupport", "2.3.5" - gem "foo", :git => "#{lib_path('foo')}" - - #{patchlevel_fixnum} - G - - bundle "outdated" - should_be_patchlevel_fixnum - end - end end end |