diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb index d9796c4208a0..ed5bb03bd702 100644 --- a/lib/rubygems/requirement.rb +++ b/lib/rubygems/requirement.rb @@ -18,6 +18,7 @@ class Gem::Requirement ">=" => lambda {|v, r| v >= r }, "<=" => lambda {|v, r| v <= r }, "~>" => lambda {|v, r| v >= r && v.release < r.bump }, + "^>" => lambda {|v, r| v >= r && v.release < r.bump_major }, }.freeze SOURCE_SET_REQUIREMENT = Struct.new(:for_lockfile).new "!" # :nodoc: @@ -191,7 +192,14 @@ def as_list # :nodoc: end def hash # :nodoc: - requirements.map {|r| r.first == "~>" ? [r[0], r[1].to_s] : r }.sort.hash + requirements.map do |r| + case r.first + when "~>", "^>" + [r[0], r[1].to_s] + else + r + end + end.sort.hash end def marshal_dump # :nodoc: diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb index d9cd91bffa86..e2c654269119 100644 --- a/lib/rubygems/version.rb +++ b/lib/rubygems/version.rb @@ -198,6 +198,7 @@ def self.create(input) @@all = {} @@bump = {} + @@bump_major = {} @@release = {} def self.new(version) # :nodoc: @@ -255,6 +256,21 @@ def bump end end + ## + # Return a new version object where the major version number + # is one greater and all non-major version components are zeroed out - + # e.g., 5.3.1 => 6.0.0. + + def bump_major + @@bump_major[self] ||= begin + segments = self.segments + segments.pop while segments.any? {|s| String === s } + segments[0] = segments[0].succ + segments[1..-1] = Array.new(segments.length - 1, 0) + self.class.new segments.join(".") + end + end + ## # A Version is only eql? to another version if it's specified to the # same precision. Version "1.0" is not the same as version "1". diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb index de0d11ec00b6..0c2ee302087d 100644 --- a/test/rubygems/test_gem_requirement.rb +++ b/test/rubygems/test_gem_requirement.rb @@ -30,11 +30,15 @@ def test_equals2 refute_requirement_equal "~> 1.3", "~> 1.3.0" refute_requirement_equal "~> 1.3.0", "~> 1.3" + refute_requirement_equal "~> 1.3.1", "^> 1.3" assert_requirement_equal ["> 2", "~> 1.3", "~> 1.3.1"], ["~> 1.3.1", "~> 1.3", "> 2"] + assert_requirement_equal ["> 2", "^> 1.3", "^> 1.3.1"], ["^> 1.3.1", "^> 1.3", "> 2"] + assert_requirement_equal ["> 2", "^> 1.3", "^> 1.3.1"], ["^> 1.3.1", "^> 1.3", "> 2"] assert_requirement_equal ["> 2", "~> 1.3"], ["> 2.0", "~> 1.3"] assert_requirement_equal ["> 2.0", "~> 1.3"], ["> 2", "~> 1.3"] + assert_requirement_equal ["> 2.0", "^> 1.3"], ["> 2", "^> 1.3"] refute_equal Object.new, req("= 1.2") refute_equal req("= 1.2"), Object.new @@ -70,8 +74,10 @@ def test_basic_non_none def test_for_lockfile assert_equal " (~> 1.0)", req("~> 1.0").for_lockfile + assert_equal " (^> 1.0)", req("^> 1.0").for_lockfile assert_equal " (~> 1.0, >= 1.0.1)", req(">= 1.0.1", "~> 1.0").for_lockfile + assert_equal " (^> 1.0, >= 1.0.1)", req(">= 1.0.1", "^> 1.0").for_lockfile duped = req "= 1.0" duped.requirements << ["=", v("1.0")] @@ -252,6 +258,50 @@ def test_satisfied_by_eh_tilde_gt_v0 assert_satisfied_by "0.0.1", r end + def test_satisfied_by_eh_caret_gt + r = req "^> 1.2" + + refute_satisfied_by "1.1", r + assert_satisfied_by "1.2", r + assert_satisfied_by "1.2.1", r + assert_satisfied_by "1.3", r + + assert_raise ArgumentError do + r.satisfied_by? nil + end + end + + def test_satisfied_by_eh_caret_gt_v0 + r = req "^> 0.0.1" + + assert_satisfied_by "0.1.1", r + assert_satisfied_by "0.0.2", r + assert_satisfied_by "0.0.1", r + refute_satisfied_by "1.0.1", r + end + + def test_satisfied_by_eh_caret_gt_v1 + r = req "^> 1" + + assert_satisfied_by "1", r + assert_satisfied_by "1.1", r + assert_satisfied_by "1.1.1", r + assert_satisfied_by "1.0.2", r + assert_satisfied_by "1.0.1", r + refute_satisfied_by "2.0.1", r + refute_satisfied_by "2.1.1", r + end + + def test_satisfied_by_eh_caret_gt_v10 + r = req "^> 1.0" + + assert_satisfied_by "1.1.1", r + assert_satisfied_by "1.0.2", r + assert_satisfied_by "1.0.1", r + refute_satisfied_by "2.0.0", r + refute_satisfied_by "2.0.1", r + end + def test_satisfied_by_eh_good assert_satisfied_by "0.2.33", "= 0.2.33" assert_satisfied_by "0.2.34", "> 0.2.33" @@ -287,11 +337,22 @@ def test_satisfied_by_eh_good assert_satisfied_by "3.0.rc2", "> 0" + assert_satisfied_by "5.0.0.rc2", "~> 5.a" + refute_satisfied_by "5.0.0.rc2", "~> 5.x" assert_satisfied_by "5.0.0.rc2", "~> 5.a" refute_satisfied_by "5.0.0.rc2", "~> 5.x" assert_satisfied_by "5.0.0", "~> 5.a" assert_satisfied_by "5.0.0", "~> 5.x" + + assert_satisfied_by "5.0.0.rc2", "^> 5.a" + refute_satisfied_by "5.0.0.rc2", "^> 5.x" + + assert_satisfied_by "5.0.0.rc2", "^> 5.a" + refute_satisfied_by "5.0.0.rc2", "^> 5.x" + + assert_satisfied_by "5.0.0", "^> 5.a" + assert_satisfied_by "5.0.0", "^> 5.x" end def test_illformed_requirements @@ -336,6 +397,30 @@ def test_satisfied_by_eh_boxed refute_satisfied_by "2.0", "~> 1" end + def test_satisfied_by_eh_boxed_caret + refute_satisfied_by "1.3", "^> 1.4" + assert_satisfied_by "1.4", "^> 1.4" + assert_satisfied_by "1.5", "^> 1.4" + refute_satisfied_by "2.0", "^> 1.4" + + refute_satisfied_by "1.3", "^> 1.4.4" + refute_satisfied_by "1.4", "^> 1.4.4" + assert_satisfied_by "1.4.4", "^> 1.4.4" + assert_satisfied_by "1.4.5", "^> 1.4.4" + assert_satisfied_by "1.5", "^> 1.4.4" + refute_satisfied_by "2.0", "^> 1.4.4" + + assert_satisfied_by "1.1.pre", "^> 1.0.0" + refute_satisfied_by "1.1.pre", "^> 1.1" + refute_satisfied_by "2.0.a", "^> 1.0" + refute_satisfied_by "2.0.a", "^> 2.0" + + refute_satisfied_by "0.9", "^> 1" + assert_satisfied_by "1.0", "^> 1" + assert_satisfied_by "1.1", "^> 1" + refute_satisfied_by "2.0", "^> 1" + end + def test_satisfied_by_eh_multiple req = [">= 1.4", "<= 1.6", "!= 1.5"] @@ -385,6 +470,7 @@ def test_specific assert req("<= 1").specific? assert req("= 1") .specific? assert req("~> 1").specific? + assert req("^> 1").specific? assert req("> 1", "> 2").specific? # GIGO end @@ -422,11 +508,14 @@ def test_hash_returns_equal_hashes_for_equivalent_requirements refute_requirement_hash_equal "~> 1.3", "~> 1.3.0" refute_requirement_hash_equal "~> 1.3.0", "~> 1.3" + refute_requirement_hash_equal "^> 1.3.0", "^> 1.3" assert_requirement_hash_equal ["> 2", "~> 1.3", "~> 1.3.1"], ["~> 1.3.1", "~> 1.3", "> 2"] + assert_requirement_hash_equal ["> 2", "^> 1.3", "^> 1.3.1"], ["^> 1.3.1", "^> 1.3", "> 2"] assert_requirement_hash_equal ["> 2", "~> 1.3"], ["> 2.0", "~> 1.3"] assert_requirement_hash_equal ["> 2.0", "~> 1.3"], ["> 2", "~> 1.3"] + assert_requirement_hash_equal ["> 2.0", "^> 1.3"], ["> 2", "^> 1.3"] assert_requirement_hash_equal "= 1.0", "= 1.0.0" assert_requirement_hash_equal "= 1.1", "= 1.1.0"