class SemVersion

Constants

PRE_METADATA_REGEX
SEMVER_REGEX

Pattern allows min and patch to be skipped. We have to do extra checking if we want them

VERSION

Attributes

major[R]
metadata[R]
minor[R]
patch[R]
pre[R]
prerelease[R]

Public Class Methods

from_loose_version(string) click to toggle source
# File lib/sem_version.rb, line 41
def self.from_loose_version(string)
  match = string.match(SEMVER_REGEX)
  raise ArgumentError, "Version string #{string} is not valid" unless match
  maj, min, pat, pre, bui = match.captures
  min = 0 if min.nil? || min.empty?
  pat = 0 if pat.nil? || pat.empty?
  new(maj.to_i, min.to_i, pat.to_i, pre, bui)
end
new(*args) click to toggle source

Format were raw bits are passed in is undocumented, and not validity checked

# File lib/sem_version.rb, line 14
def initialize(*args)
  if args.first.is_a?(String)
    @major, @minor, @patch, @pre, @metadata = self.class.parse(args.first)
    # Validation should be handled at a string level by self.parse, but validate anyway
    validate
  elsif args.first.is_a?(Hash)
    @major, @minor, @patch, @pre, @metadata = args.first.values_at(:major, :minor, :patch, :pre, :metadata)
    # Allow :prerelease as well
    @pre ||= args.first[:prerelease]
    validate
  elsif args.first.is_a?(Array)
    @major, @minor, @patch, @pre, @metadata = *args.first
    validate
  else
    @major, @minor, @patch, @pre, @metadata = *args
    validate
  end
end
open_constraint?(constraint) click to toggle source
# File lib/sem_version.rb, line 94
def self.open_constraint?(constraint)
  comparison, _ = self.split_constraint(constraint)
  comparison != '='
end
parse(string) click to toggle source
# File lib/sem_version.rb, line 33
def self.parse(string)
  match = string.match(SEMVER_REGEX)
  raise ArgumentError, "Version string #{string} is not valid" unless match
  maj, min, pat, pre, bui = match.captures
  raise ArgumentError, "Version string #{string} is not valid" if min.empty? || pat.empty?
  [maj.to_i, min.to_i, pat.to_i, pre, bui]
end
split_constraint(constraint) click to toggle source
# File lib/sem_version.rb, line 99
def self.split_constraint(constraint)
  comparison, version = constraint.strip.split(' ', 2)
  comparison, version = '=', comparison if version.nil?
  comparison = '=' if comparison == '=='
  [comparison, version]
end
valid?(string) click to toggle source
# File lib/sem_version.rb, line 50
def self.valid?(string)
  matches = string.match(SEMVER_REGEX)
  matches && !matches[2].nil? && !matches[3].nil?
end

Public Instance Methods

<=>(other) click to toggle source
# File lib/sem_version.rb, line 55
def <=>(other)
  maj = @major <=> other.major
  return maj unless maj == 0

  min = @minor <=> other.minor
  return min unless min == 0

  pat = @patch <=> other.patch
  return pat unless pat == 0

  pre = compare_sep(@pre, other.pre, true)
  return pre unless pre == 0

  return 0
end
inspect() click to toggle source
# File lib/sem_version.rb, line 152
def inspect
  "#<SemVersion: #{to_s}>"
end
major=(val) click to toggle source
# File lib/sem_version.rb, line 106
def major=(val)
  raise ArgumentError, "#{val} is not a valid major version (must be an integer >= 0)" unless val.is_a?(Integer) && val >= 0
  @major = val
end
metadata=(val) click to toggle source
# File lib/sem_version.rb, line 129
def metadata=(val)
  unless val.nil? || (val.is_a?(String) && val =~ PRE_METADATA_REGEX)
    raise ArgumentError, "#{val} is not a valid metadata string (must be nil, or a string following http://semver.org constraints)"
  end
  @metadata = val
end
minor=(val) click to toggle source
# File lib/sem_version.rb, line 111
def minor=(val)
  raise ArgumentError, "#{val} is not a valid minor version (must be an integer >= 0)" unless val.is_a?(Integer) && val >= 0
  @minor = val
end
patch=(val) click to toggle source
# File lib/sem_version.rb, line 116
def patch=(val)
  raise ArgumentError, "#{val} is not a valid patch version (must be an integer >= 0)" unless val.is_a?(Integer) && val >= 0
  @patch = val
end
pre=(val) click to toggle source
# File lib/sem_version.rb, line 121
def pre=(val)
  unless val.nil? || (val.is_a?(String) && val =~ PRE_METADATA_REGEX)
    raise ArgumentError, "#{val} is not a valid pre-release version (must be nil, or a string following http://semver.org constraints)"
  end
  @pre = val
end
Also aliased as: prerelease=
prerelease=(val)
Alias for: pre=
satisfies?(constraint) click to toggle source
# File lib/sem_version.rb, line 71
def satisfies?(constraint)
  comparison, version = self.class.split_constraint(constraint)
  # Allow pessimistic operator
  if comparison == '~>'
    match = version.match(/^(\d+)\.(\d+)\.?(\d*)$/)
    raise ArgumentError, "Pessimistic constraints must have a version in the form 'x.y' or 'x.y.z'" unless match
    maj, min, pat = match.captures
    if pat.empty?
      lower = "#{maj}.#{min}.0"
      upper = "#{maj.to_i+1}.0.0"
    else
      lower = "#{maj}.#{min}.#{pat}"
      upper = "#{maj}.#{min.to_i+1}.0"
    end

    send('>=', SemVersion.new(lower)) && send('<', SemVersion.new(upper))
  else
    comparison = '==' if comparison == '='
    semversion = self.class.from_loose_version(version)
    send(comparison, semversion)
  end
end
to_a() click to toggle source
# File lib/sem_version.rb, line 143
def to_a
  [@major, @minor, @patch, @pre, @metadata]
end
to_h() click to toggle source
# File lib/sem_version.rb, line 147
def to_h
  h = [:major, :minor, :patch, :pre, :metadata].zip(to_a)
  Hash[h.reject{ |k,v| v.nil? }]
end
to_s() click to toggle source
# File lib/sem_version.rb, line 136
def to_s
  r = "#{@major}.#{@minor}.#{@patch}"
  r << "-#{@pre}" if @pre
  r << "+#{@metadata}" if @metadata
  r
end

Private Instance Methods

compare_sep(ours, theirs, nil_wins) click to toggle source
# File lib/sem_version.rb, line 158
def compare_sep(ours, theirs, nil_wins)
  # Both nil? They're equal
  return 0 if ours.nil? && theirs.nil?
  # One's nil? The winner is determined by precidence
  return nil_wins ? -1 : 1 if theirs.nil?
  return nil_wins ? 1 : -1 if ours.nil?

  our_parts = ours.split('.')
  their_parts = theirs.split('.')

  our_parts.zip(their_parts) do |a,b|
    # b can be nil, in which case it loses
    return 1 if b.nil?
    # If they're both ints, convert to as such
    # If one's an int and the other isn't, the string version of the int gets correctly compared
    a, b = a.to_i, b.to_i if a =~ /^\d+$/ && b =~ /^\d+$/

    comp = a <=> b
    return comp unless comp == 0
  end

  # If we got this far, either they're equal (same length) or they won
  return (our_parts.length == their_parts.length) ? 0 : -1
end
validate() click to toggle source
# File lib/sem_version.rb, line 183
def validate
  # Validates the instance variables. Different approach to validating a raw string
  raise ArgumentError, "Invalid version (major is not an int >= 0)" unless @major.is_a?(Integer) && @major >= 0
  raise ArgumentError, "Invalid version (minor is not an int >= 0)" unless @minor.is_a?(Integer) && @minor >= 0
  raise ArgumentError, "Invalid version (patch is not an int >= 0)" unless @patch.is_a?(Integer) && @patch >= 0
  unless @pre.nil? || (@pre.is_a?(String) && @pre =~ PRE_METADATA_REGEX)
    raise ArgumentError, "Invalid version (pre must be nil, or a string following http://semver.org contraints)"
  end
  unless @metadata.nil? || (@metadata.is_a?(String) && @metadata =~ PRE_METADATA_REGEX)
    raise ArgumentError, "Invalid version (metadata must be nil, or a string following http://semver.org contraints)"
  end
end