class Mixlib::Versioning::Format

@author Seth Chisamore (<schisamo@chef.io>)

@!attribute [r] major

@return [Integer] major identifier

@!attribute [r] minor

@return [Integer] minor identifier

@!attribute [r] patch

@return [Integer, nil] patch identifier

@!attribute [r] prerelease

@return [String, nil] pre-release identifier

@!attribute [r] build

@return [String, nil] build identifier

@!attribute [r] iteration

@return [String, nil] build interation

@!attribute [r] input

@return [String, nil] original input version string that was parsed

Attributes

build[R]
input[R]
iteration[R]
major[R]
minor[R]
patch[R]
prerelease[R]

Public Class Methods

for(format_type) click to toggle source

Returns the {Mixlib::Versioning::Format} class that maps to the given format type.

@example

Mixlib::Versioning::Format.for(:semver)
Mixlib::Versioning::Format.for('semver')
Mixlib::Versioning::Format.for(Mixlib::Versioning::Format::SemVer)

@param format_type [String, Symbol, Mixlib::Versioning::Format] Name of

a valid +Mixlib::Versioning::Format+ in Class or snake-case form.

@raise [Mixlib::Versioning::UnknownFormatError] if the given format

type doesn't exist

@return [Class] the {Mixlib::Versioning::Format} class

# File lib/mixlib/versioning/format.rb, line 62
def self.for(format_type)
  if format_type.is_a?(Class) &&
      format_type.ancestors.include?(Mixlib::Versioning::Format)
    format_type
  else
    case format_type.to_s
    when "semver" then Mixlib::Versioning::Format::SemVer
    when "opscode_semver" then Mixlib::Versioning::Format::OpscodeSemVer
    when "git_describe" then Mixlib::Versioning::Format::GitDescribe
    when "rubygems" then Mixlib::Versioning::Format::Rubygems
    when "partial_semver" then Mixlib::Versioning::Format::PartialSemVer
    else
      msg = "'#{format_type}' is not a supported Mixlib::Versioning format"
      raise Mixlib::Versioning::UnknownFormatError, msg
    end
  end
end
new(version_string) click to toggle source

@param version_string [String] string representation of the version

# File lib/mixlib/versioning/format.rb, line 83
def initialize(version_string)
  parse(version_string)
  @input = version_string
end

Public Instance Methods

<=>(other) click to toggle source

Compare this version number with the given version number, following Semantic Versioning 2.0.0-rc.1 semantics.

@param other [Mixlib::Versioning::Format] @return [Integer] -1, 0, or 1 depending on whether the this version is

less than, equal to, or greater than the other version
# File lib/mixlib/versioning/format.rb, line 200
def <=>(other)
  # Check whether the `other' is a String and if so, then get an
  # instance of *this* class (e.g., GitDescribe, OpscodeSemVer,
  # SemVer, Rubygems, etc.), so we can compare against it.
  other = self.class.new(other) if other.is_a?(String)

  # First, perform comparisons based on major, minor, and patch
  # versions.  These are always presnt and always non-nil
  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

  # Next compare pre-release specifiers.  A pre-release sorts
  # before a release (e.g. 1.0.0-alpha.1 comes before 1.0.0), so
  # we need to take nil into account in our comparison.
  #
  # If both have pre-release specifiers, we need to compare both
  # on the basis of each component of the specifiers.
  if @prerelease && other.prerelease.nil?
    return -1
  elsif @prerelease.nil? && other.prerelease
    return 1
  elsif @prerelease && other.prerelease
    pre = compare_dot_components(@prerelease, other.prerelease)
    return pre unless pre == 0
  end

  # Build specifiers are compared like pre-release specifiers,
  # except that builds sort *after* everything else
  # (e.g. 1.0.0+build.123 comes after 1.0.0, and
  # 1.0.0-alpha.1+build.123 comes after 1.0.0-alpha.1)
  if @build.nil? && other.build
    return -1
  elsif @build && other.build.nil?
    return 1
  elsif @build && other.build
    build_ver = compare_dot_components(@build, other.build)
    return build_ver unless build_ver == 0
  end

  # Some older version formats improperly include a package iteration in
  # the version string. This is different than a build specifier and
  # valid release versions may include an iteration. We'll transparently
  # handle this case and compare iterations if it was parsed by the
  # implementation class.
  if @iteration.nil? && other.iteration
    return -1
  elsif @iteration && other.iteration.nil?
    return 1
  elsif @iteration && other.iteration
    return @iteration <=> other.iteration
  end

  # If we get down here, they're both equal
  0
end
build?() click to toggle source

@return [Boolean] Whether or not this is a build version

# File lib/mixlib/versioning/format.rb, line 119
def build?
  !@build.nil?
end
eql?(other) click to toggle source

@param other [Mixlib::Versioning::Format] @return [Boolean] returns true if the versions are equal, false

otherwise.
# File lib/mixlib/versioning/format.rb, line 265
def eql?(other)
  @major == other.major &&
    @minor == other.minor &&
    @patch == other.patch &&
    @prerelease == other.prerelease &&
    @build == other.build
end
hash() click to toggle source
# File lib/mixlib/versioning/format.rb, line 273
def hash
  [@major, @minor, @patch, @prerelease, @build].compact.join(".").hash
end
in_same_prerelease_line?(other) click to toggle source

Returns `true` if `other` an share the same `major`, `minor`, and `patch` values. Pre-release and build specifiers are not taken into consideration.

@return [Boolean]

# File lib/mixlib/versioning/format.rb, line 139
def in_same_prerelease_line?(other)
  @major == other.major &&
    @minor == other.minor &&
    @patch == other.patch &&
    @prerelease == other.prerelease
end
in_same_release_line?(other) click to toggle source

Returns `true` if `other` and this {Format} share the same `major`, `minor`, and `patch` values. Pre-release and build specifiers are not taken into consideration.

@return [Boolean]

# File lib/mixlib/versioning/format.rb, line 128
def in_same_release_line?(other)
  @major == other.major &&
    @minor == other.minor &&
    @patch == other.patch
end
inspect() click to toggle source

Since the default implementation of `Object#inspect` uses `Object#to_s` under the covers (which we override) we need to also override `#inspect` to ensure useful debug information.

# File lib/mixlib/versioning/format.rb, line 154
def inspect
  vars = instance_variables.map do |n|
    "#{n}=#{instance_variable_get(n).inspect}"
  end
  format("#<%s:0x%x %s>", self.class, object_id, vars.join(", "))
end
parse(_version_string) click to toggle source

Parses the version string splitting it into it's component version identifiers for easy comparison and sorting of versions. This method *MUST* be overriden by all descendants of this class.

@param version_string [String] string representation of the version @raise [Mixlib::Versioning::ParseError] raised if parsing fails

# File lib/mixlib/versioning/format.rb, line 94
def parse(_version_string)
  raise Error, "You must override the #parse"
end
prerelease?() click to toggle source

@return [Boolean] Whether or not this is a pre-release version

# File lib/mixlib/versioning/format.rb, line 104
def prerelease?
  !@prerelease.nil? && @build.nil?
end
prerelease_build?() click to toggle source

@return [Boolean] Whether or not this is a pre-release build version

# File lib/mixlib/versioning/format.rb, line 114
def prerelease_build?
  !@prerelease.nil? && !@build.nil?
end
release?() click to toggle source

@return [Boolean] Whether or not this is a release version

# File lib/mixlib/versioning/format.rb, line 99
def release?
  @prerelease.nil? && @build.nil?
end
release_build?() click to toggle source

@return [Boolean] Whether or not this is a release build version

# File lib/mixlib/versioning/format.rb, line 109
def release_build?
  @prerelease.nil? && !@build.nil?
end
to_rubygems_string() click to toggle source

Returns Rubygems compliant string representation of this {Format} instance. The string returned will take on the form:

“`text MAJOR.MINOR.PATCH.PRERELEASE “`

@return [String] Rubygems compliant string representation of this

{Format} instance

@todo create a proper serialization abstraction

# File lib/mixlib/versioning/format.rb, line 188
def to_rubygems_string
  s = [@major, @minor, @patch].map(&:to_i).join(".")
  s += ".#{@prerelease}" if @prerelease
  s
end
to_s() click to toggle source

@return [String] String representation of this {Format} instance

# File lib/mixlib/versioning/format.rb, line 147
def to_s
  @input
end
to_semver_string() click to toggle source

Returns SemVer compliant string representation of this {Format} instance. The string returned will take on the form:

“`text MAJOR.MINOR.PATCH-PRERELEASE+BUILD “`

@return [String] SemVer compliant string representation of this

{Format} instance

@todo create a proper serialization abstraction

# File lib/mixlib/versioning/format.rb, line 171
def to_semver_string
  s = [@major, @minor, @patch].map(&:to_i).join(".")
  s += "-#{@prerelease}" if @prerelease
  s += "+#{@build}" if @build
  s
end

Private Instance Methods

compare_dot_components(a_item, b_item) click to toggle source

Compares prerelease and build version component strings according to SemVer 2.0.0-rc.1 semantics.

Returns -1, 0, or 1, just like the spaceship operator (`<=>`), and is used in the implemntation of `<=>` for this class.

Pre-release and build specifiers are dot-separated strings. Numeric components are sorted numerically; otherwise, sorting is standard ASCII order. Numerical components have a lower precedence than string components.

See www.semver.org for more.

Both `a_item` and `b_item` should be Strings; `nil` is not a valid input.

# File lib/mixlib/versioning/format.rb, line 307
def compare_dot_components(a_item, b_item)
  a_components = a_item.split(".")
  b_components = b_item.split(".")

  max_length = [a_components.length, b_components.length].max

  (0..(max_length - 1)).each do |i|
    # Convert the ith component into a number if possible
    a = maybe_int(a_components[i])
    b = maybe_int(b_components[i])

    # Since the components may be of differing lengths, the
    # shorter one will yield +nil+ at some point as we iterate.
    if a.nil? && !b.nil?
      # a_item was shorter
      return -1
    elsif !a.nil? && b.nil?
      # b_item was shorter
      return 1
    end

    # Now we need to compare appropriately based on type.
    #
    # Numbers have lower precedence than strings; therefore, if
    # the components are of different types (String vs. Integer),
    # we just return -1 for the numeric one and we're done.
    #
    # If both are the same type (Integer vs. Integer, or String
    # vs. String), we can just use the native comparison.
    #
    if a.is_a?(Integer) && b.is_a?(String)
      # a_item was "smaller"
      return -1
    elsif a.is_a?(String) && b.is_a?(Integer)
      # b_item was "smaller"
      return 1
    else
      comp = a <=> b
      return comp unless comp == 0
    end
  end # each

  # We've compared all components of both strings; if we've gotten
  # down here, they're totally the same
  0
end
maybe_int(n) click to toggle source

If a String `n` can be parsed as an Integer do so; otherwise, do nothing.

@param n [String, nil] @return [Integer] the parsed {Integer}

# File lib/mixlib/versioning/format.rb, line 286
def maybe_int(n)
  Integer(n)
rescue
  n
end