Class: SemVer
Overview
We need to subclass Numeric to force range comparisons not to try to iterate over SemVer and instead use numeric comparisons (eg >, <, >=, <=)
Constant Summary collapse
- VERSION =
/^v?(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z-]*|)$/
- SIMPLE_RANGE =
/^v?(\d+|[xX])(?:\.(\d+|[xX])(?:\.(\d+|[xX]))?)?$/
- MIN =
SemVer.new('0.0.0-')
- MAX =
SemVer.new('8.0.0')
Instance Attribute Summary collapse
-
#major ⇒ Object
readonly
Returns the value of attribute major.
-
#minor ⇒ Object
readonly
Returns the value of attribute minor.
-
#special ⇒ Object
readonly
Returns the value of attribute special.
-
#tiny ⇒ Object
readonly
Returns the value of attribute tiny.
Class Method Summary collapse
- .[](range) ⇒ Object
- .find_matching(pattern, versions) ⇒ Object
- .pre(vstring) ⇒ Object
- .valid?(ver) ⇒ Boolean
Instance Method Summary collapse
- #<=>(other) ⇒ Object
-
#initialize(ver) ⇒ SemVer
constructor
A new instance of SemVer.
- #inspect ⇒ Object (also: #to_s)
- #matched_by?(pattern) ⇒ Boolean
Constructor Details
#initialize(ver) ⇒ SemVer
Returns a new instance of SemVer.
68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/semver.rb', line 68 def initialize(ver) unless SemVer.valid?(ver) raise ArgumentError.new("Invalid version string '#{ver}'!") end @major, @minor, @tiny, @special = VERSION.match(ver).captures.map do |x| # Because Kernel#Integer tries to interpret hex and octal strings, which # we specifically do not want, and which cannot be overridden in 1.8.7. Float(x).to_i rescue x end end |
Instance Attribute Details
#major ⇒ Object (readonly)
Returns the value of attribute major.
66 67 68 |
# File 'lib/semver.rb', line 66 def major @major end |
#minor ⇒ Object (readonly)
Returns the value of attribute minor.
66 67 68 |
# File 'lib/semver.rb', line 66 def minor @minor end |
#special ⇒ Object (readonly)
Returns the value of attribute special.
66 67 68 |
# File 'lib/semver.rb', line 66 def special @special end |
#tiny ⇒ Object (readonly)
Returns the value of attribute tiny.
66 67 68 |
# File 'lib/semver.rb', line 66 def tiny @tiny end |
Class Method Details
.[](range) ⇒ Object
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/semver.rb', line 23 def self.[](range) range.gsub(/([><=])\s+/, '\1').split(/\b\s+(?!-)/).map do |r| case r when SemVer::VERSION SemVer.new(pre(r)) .. SemVer.new(r) when SemVer::SIMPLE_RANGE r += ".0" unless SemVer.valid?(r.gsub(/x/i, '0')) SemVer.new(r.gsub(/x/i, '0'))...SemVer.new(r.gsub(/(\d+)\.x/i) { "#{$1.to_i + 1}.0" } + '-') when /\s+-\s+/ a, b = r.split(/\s+-\s+/) SemVer.new(pre(a)) .. SemVer.new(b) when /^~/ ver = r.sub(/~/, '').split('.').map(&:to_i) start = (ver + [0] * (3 - ver.length)).join('.') ver.pop unless ver.length == 1 ver[-1] = ver.last + 1 finish = (ver + [0] * (3 - ver.length)).join('.') SemVer.new(pre(start)) ... SemVer.new(pre(finish)) when /^>=/ ver = r.sub(/^>=/, '') SemVer.new(pre(ver)) .. SemVer::MAX when /^<=/ ver = r.sub(/^<=/, '') SemVer::MIN .. SemVer.new(ver) when /^>/ if r =~ /-/ ver = [r[1..-1]] else ver = r.sub(/^>/, '').split('.').map(&:to_i) ver[2] = ver.last + 1 end SemVer.new(ver.join('.') + '-') .. SemVer::MAX when /^</ ver = r.sub(/^</, '') SemVer::MIN ... SemVer.new(pre(ver)) else (1..1) end end.inject { |a,e| a & e } end |
.find_matching(pattern, versions) ⇒ Object
15 16 17 |
# File 'lib/semver.rb', line 15 def self.find_matching(pattern, versions) versions.select { |v| v.matched_by?("#{pattern}") }.sort.last end |
.pre(vstring) ⇒ Object
19 20 21 |
# File 'lib/semver.rb', line 19 def self.pre(vstring) vstring =~ /-/ ? vstring : vstring + '-' end |
.valid?(ver) ⇒ Boolean
11 12 13 |
# File 'lib/semver.rb', line 11 def self.valid?(ver) VERSION =~ ver end |
Instance Method Details
#<=>(other) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/semver.rb', line 80 def <=>(other) # Note that prior to ruby 2.3.0, if a <=> method threw an exception, ruby # would silently rescue the exception and return nil from <=> (which causes # the derived == comparison to return false). Starting in ruby 2.3.0, this # behavior changed and the exception is actually thrown. Some comments at: # https://bugs.ruby-lang.org/issues/7688 # # SemVer#initialize above throws an ArgumentError given an invalid # version string. So, to preserve the ability to use the == operator # between a SemVer object and an invalid version string, we take care here # to do the valid? check before constructing the SemVer object (i.e. # so that a == comparison doesn't throw an exception, but just returns # false.) unless other.is_a? SemVer return nil unless SemVer.valid?(other) other = SemVer.new("#{other}") end return self.major <=> other.major unless self.major == other.major return self.minor <=> other.minor unless self.minor == other.minor return self.tiny <=> other.tiny unless self.tiny == other.tiny return 0 if self.special == other.special return 1 if self.special == '' return -1 if other.special == '' return self.special <=> other.special end |
#inspect ⇒ Object Also known as: to_s
128 129 130 |
# File 'lib/semver.rb', line 128 def inspect @vstring || "v#{@major}.#{@minor}.#{@tiny}#{@special}" end |
#matched_by?(pattern) ⇒ Boolean
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/semver.rb', line 109 def matched_by?(pattern) # For the time being, this is restricted to exact version matches and # simple range patterns. In the future, we should implement some or all of # the comparison operators here: # https://github.com/isaacs/node-semver/blob/d474801/semver.js#L340 case pattern when SIMPLE_RANGE pattern = SIMPLE_RANGE.match(pattern).captures pattern[1] = @minor unless pattern[1] && pattern[1] !~ /x/i pattern[2] = @tiny unless pattern[2] && pattern[2] !~ /x/i [@major, @minor, @tiny] == pattern.map { |x| x.to_i } when VERSION self == SemVer.new(pattern) else false end end |