class SemanticPuppet::Version

@note SemanticPuppet::Version subclasses Numeric so that it has sane Range

semantics in Ruby 1.9+.

Constants

MAX

The highest precedence Version possible

MIN

The lowest precedence Version possible

REGEX_BUILD
REGEX_FULL
REGEX_NUMERIC

Version string matching regexes

REGEX_PRE

Attributes

major[R]
minor[R]
patch[R]

Public Class Methods

new(major, minor, patch, prerelease = nil, build = nil) click to toggle source
# File lib/semantic_puppet/version.rb, line 79
def initialize(major, minor, patch, prerelease = nil, build = nil)
  @major      = major
  @minor      = minor
  @patch      = patch
  @prerelease = prerelease
  @build      = build
end
parse(ver) click to toggle source

Parse a Semantic Version string.

@param ver [String] the version string to parse @return [Version] a comparable {Version} object

# File lib/semantic_puppet/version.rb, line 17
def parse(ver)
  match, major, minor, patch, prerelease, build = *ver.match(/\A#{REGEX_FULL}\Z/)

  if match.nil?
    raise "Unable to parse '#{ver}' as a semantic version identifier"
  end

  prerelease = parse_prerelease(prerelease) if prerelease
  # Build metadata is not yet supported in semantic_puppet, but we hope to.
  # The following code prevents build metadata for now.
  #build = parse_build_metadata(build) if build
  if !build.nil?
    raise "'#{ver}' MUST NOT include build identifiers"
  end

  self.new(major.to_i, minor.to_i, patch.to_i, prerelease, build)
end
valid?(ver) click to toggle source

Validate a Semantic Version string.

@param ver [String] the version string to validate @return [bool] whether or not the string represents a valid Semantic Version

# File lib/semantic_puppet/version.rb, line 39
def valid?(ver)
  !!(ver =~ /\A#{REGEX_FULL}\Z/)
end

Private Class Methods

parse_build_metadata(build) click to toggle source
# File lib/semantic_puppet/version.rb, line 59
def parse_build_metadata(build)
  subject = 'Build identifiers'
  build = build.split('.', -1)

  if build.empty? or build.any? { |x| x.empty? }
    raise "#{subject} MUST NOT be empty"
  elsif build.any? { |x| x =~ /[^0-9a-zA-Z-]/ }
    raise "#{subject} MUST use only ASCII alphanumerics and hyphens"
  end

  return build
end
parse_prerelease(prerelease) click to toggle source
# File lib/semantic_puppet/version.rb, line 44
def parse_prerelease(prerelease)
  subject = 'Prerelease identifiers'
  prerelease = prerelease.split('.', -1)

  if prerelease.empty? or prerelease.any? { |x| x.empty? }
    raise "#{subject} MUST NOT be empty"
  elsif prerelease.any? { |x| x =~ /[^0-9a-zA-Z-]/ }
    raise "#{subject} MUST use only ASCII alphanumerics and hyphens"
  elsif prerelease.any? { |x| x =~ /^0\d+$/ }
    raise "#{subject} MUST NOT contain leading zeroes"
  end

  return prerelease.map { |x| x =~ /^\d+$/ ? x.to_i : x }
end
raise(msg) click to toggle source
Calls superclass method
# File lib/semantic_puppet/version.rb, line 72
def raise(msg)
  super ValidationFailure, msg, caller.drop_while { |x| x !~ /\bparse\b/ }
end

Public Instance Methods

<=>(other) click to toggle source
# File lib/semantic_puppet/version.rb, line 111
def <=>(other)
  return self.major <=> other.major unless self.major == other.major
  return self.minor <=> other.minor unless self.minor == other.minor
  return self.patch <=> other.patch unless self.patch == other.patch
  return compare_prerelease(other)
end
==(other)
Alias for: eql?
build() click to toggle source
# File lib/semantic_puppet/version.rb, line 107
def build
  @build && @build.join('.')
end
eql?(other) click to toggle source
# File lib/semantic_puppet/version.rb, line 118
def eql?(other)
  other.is_a?(Version) &&
    @major.eql?(other.major) &&
    @minor.eql?(other.minor) &&
    @patch.eql?(other.patch) &&
    @prerelease.eql?(other.instance_variable_get(:@prerelease)) &&
    @build.eql?(other.instance_variable_get(:@build))
end
Also aliased as: ==
hash() click to toggle source
# File lib/semantic_puppet/version.rb, line 134
def hash
  self.to_s.hash
end
next(part) click to toggle source
# File lib/semantic_puppet/version.rb, line 87
def next(part)
  case part
  when :major
    self.class.new(@major.next, 0, 0)
  when :minor
    self.class.new(@major, @minor.next, 0)
  when :patch
    self.class.new(@major, @minor, @patch.next)
  end
end
prerelease() click to toggle source
# File lib/semantic_puppet/version.rb, line 98
def prerelease
  @prerelease && @prerelease.join('.')
end
stable?() click to toggle source

@return [Boolean] true if this is a stable release

# File lib/semantic_puppet/version.rb, line 103
def stable?
  @prerelease.nil?
end
to_s() click to toggle source
# File lib/semantic_puppet/version.rb, line 128
def to_s
  "#{major}.#{minor}.#{patch}" +
  (@prerelease.nil? || prerelease.empty? ? '' : "-" + prerelease) +
  (@build.nil?      || build.empty?      ? '' : "+" + build     )
end

Private Instance Methods

compare_prerelease(other) click to toggle source
# File lib/semantic_puppet/version.rb, line 144
def compare_prerelease(other)
  all_mine  = @prerelease                               || @@STABLE_RELEASE
  all_yours = other.instance_variable_get(:@prerelease) || @@STABLE_RELEASE

  # Precedence is determined by comparing each dot separated identifier from
  # left to right...
  size = [ all_mine.size, all_yours.size ].max
  Array.new(size).zip(all_mine, all_yours) do |_, mine, yours|

    # ...until a difference is found.
    next if mine == yours

    # Numbers are compared numerically, strings are compared ASCIIbetically.
    if mine.class == yours.class
      return mine <=> yours

    # A larger set of pre-release fields has a higher precedence.
    elsif mine.nil?
      return -1
    elsif yours.nil?
      return 1

    # Numeric identifiers always have lower precedence than non-numeric.
    elsif mine.is_a? Numeric
      return -1
    elsif yours.is_a? Numeric
      return 1
    end
  end

  return 0
end
first_prerelease() click to toggle source
# File lib/semantic_puppet/version.rb, line 177
def first_prerelease
  self.class.new(@major, @minor, @patch, [])
end