Class: SemanticVersion

Inherits:
Object
  • Object
show all
Defined in:
lib/semantic_version.rb

Constant Summary collapse

SINGLE_OPERATORS =
{
  eq:  [0],
  lt:  [-1],
  lte: [-1, 0],
  gt:  [1],
  gte: [1, 0],
  equal_to:                 [0],
  less_than:                [-1],
  less_than_or_equal_to:    [-1, 0],
  greater_than:             [1],
  greater_than_or_equal_to: [1, 0]
}.freeze
RANGE_OPERATORS =
{
  between: [],
  within:  [],
  any_of:  []
}.freeze
OPERATORS =
SINGLE_OPERATORS.merge(RANGE_OPERATORS).freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(version) ⇒ SemanticVersion

Instantiates a new SemanticVersion.

Parameters:

  • version: (String)

    A semantic version (see semver.org).



62
63
64
# File 'lib/semantic_version.rb', line 62

def initialize(version)
  @number_components, @prerelease_components, @build_metadata = self.class.parse_components(version)
end

Instance Attribute Details

#build_metadataObject (readonly)

Returns the value of attribute build_metadata.



25
26
27
# File 'lib/semantic_version.rb', line 25

def 
  @build_metadata
end

#number_componentsObject (readonly)

Returns the value of attribute number_components.



25
26
27
# File 'lib/semantic_version.rb', line 25

def number_components
  @number_components
end

#prerelease_componentsObject (readonly)

Returns the value of attribute prerelease_components.



25
26
27
# File 'lib/semantic_version.rb', line 25

def prerelease_components
  @prerelease_components
end

Class Method Details

.[](version) ⇒ Object

Instantiates a new SemanticVersion.

Parameters:

  • version: (String)

    A semantic version (see semver.org).



32
33
34
# File 'lib/semantic_version.rb', line 32

def self.[](version)
  self.new(version)
end

.parse_components(version) ⇒ Object

Parses the given value into version components.

Parameters:

Returns:

  • An array [version_number, prerelease, build_metadata], where ‘version_number` is an array of the number components [MAJOR, MINOR, PATCH], `prerelease` is an array of the prerelease components, and `build_metadata` is, like, you know, the build metadata.



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/semantic_version.rb', line 45

def self.parse_components(version)
  return version.components if version.is_a? SemanticVersion

  version_number, extensions = version.to_s.split('-', 2)
  prerelease,  = extensions.to_s.split('+', 2)

  version_number = version_number.to_s.split('.').map(&:to_i)
  prerelease = prerelease.to_s.split('.').map {|c| c == c.to_i.to_s ? c.to_i : c }

  return [version_number, prerelease, ]
end

Instance Method Details

#is(comparison) ⇒ Object

Compares this SemanticVersion with one or more other version numbers.

Parameters:

  • assertions: (Hash)

    A hash where keys are OPERATORS and values are operands in the form of semantic versions.

Returns:

  • True if all assertions are true, false otherwise.



73
74
75
76
77
78
79
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/semantic_version.rb', line 73

def is(comparison)
  comparison.each_pair do |operator, version|
    unless OPERATORS.keys.include? operator
      raise ArgumentError.new("unrecognized operator `#{operator}'")
    end

    if RANGE_OPERATORS.keys.include? operator
      unless version.is_a?(Array) && version.length >= 2
        raise ArgumentError.new("range operand must be an array containing at least two elements")
      end

      result = if operator == :between
        is gt: version[0], lt: version[1]
      elsif operator == :within
        is gte: version[0], lte: version[1]
      elsif operator == :any_of
        version.map {|v| is eq: v }.any?
      else
        false
      end

      return false unless result == true

    else
      number_components, prerelease_components, _ = self.class.parse_components(version)

      result = 0

      # Compare version number components.

      (0..[@number_components.count, number_components.count].max-1).each do |i|
        a = @number_components[i]
        b = number_components[i]

        result = if !a.nil? && b.nil?
          a == 0 ? 0 : 1
        elsif a.nil? && !b.nil?
          b == 0 ? 0 : -1
        else
          a <=> b
        end

        break unless result == 0
      end

      if result == 0
        if @prerelease_components.empty? && !prerelease_components.empty?
          result = 1
        elsif !@prerelease_components.empty? && prerelease_components.empty?
          result = -1
        end
      end

      # Compare pre-release components.

      if result == 0
        (0..[@prerelease_components.count, prerelease_components.count].max-1).each do |i|
          break unless result == 0

          a = @prerelease_components[i]
          b = prerelease_components[i]

          result = if !a.nil? && b.nil?
            1
          elsif a.nil? && !b.nil?
            -1
          elsif a.class == b.class
            a <=> b
          else
            a.to_s <=> b.to_s
          end
        end
      end

      return false unless OPERATORS[operator].include? result
    end
  end

  return true
end

#numberObject

Returns the string representation of the “MAJOR.MINOR.PATCH” version part.



157
158
159
# File 'lib/semantic_version.rb', line 157

def number
  @number_components.join('.')
end

#prereleaseObject

Returns the string representation of the pre-release version part.



164
165
166
# File 'lib/semantic_version.rb', line 164

def prerelease
  @prerelease_components.join('.')
end

#to_sObject

Returns the string representation of the version.



171
172
173
174
175
# File 'lib/semantic_version.rb', line 171

def to_s
  number +
  (@prerelease_components.empty? ? "" : "-#{prerelease}") +
  (@build_metadata.nil? ? "" : "+#{@build_metadata}")
end