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