Class: SemVersion

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/sem_version.rb

Constant Summary collapse

VERSION =
'2.0.1'
SEMVER_REGEX =

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

/^(\d+)(?:\.(\d+)(?:\.(\d+)(?:-([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?(?:\+([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?)?)?$/
PRE_METADATA_REGEX =
/^[\dA-Za-z\-]+(\.[\dA-Za-z\-]+)*$/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ SemVersion

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


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/sem_version.rb', line 14

def initialize(*args)
  if args.first.is_a?(String)
    @major, @minor, @patch, @pre,  = 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,  = 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,  = *args.first
    validate
  else
    @major, @minor, @patch, @pre,  = *args
    validate
  end
end

Instance Attribute Details

#majorObject

Returns the value of attribute major


10
11
12
# File 'lib/sem_version.rb', line 10

def major
  @major
end

#metadataObject

Returns the value of attribute metadata


10
11
12
# File 'lib/sem_version.rb', line 10

def 
  
end

#minorObject

Returns the value of attribute minor


10
11
12
# File 'lib/sem_version.rb', line 10

def minor
  @minor
end

#patchObject

Returns the value of attribute patch


10
11
12
# File 'lib/sem_version.rb', line 10

def patch
  @patch
end

#preObject Also known as: prerelease

Returns the value of attribute pre


10
11
12
# File 'lib/sem_version.rb', line 10

def pre
  @pre
end

Class Method Details

.from_loose_version(string) ⇒ Object

Raises:

  • (ArgumentError)

41
42
43
44
45
46
47
48
# 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

.open_constraint?(constraint) ⇒ Boolean

Returns:

  • (Boolean)

94
95
96
97
# File 'lib/sem_version.rb', line 94

def self.open_constraint?(constraint)
  comparison, _ = self.split_constraint(constraint)
  comparison != '='
end

.parse(string) ⇒ Object

Raises:

  • (ArgumentError)

33
34
35
36
37
38
39
# 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) ⇒ Object


99
100
101
102
103
104
# 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) ⇒ Boolean

Returns:

  • (Boolean)

50
51
52
53
# File 'lib/sem_version.rb', line 50

def self.valid?(string)
  matches = string.match(SEMVER_REGEX)
  matches && !matches[2].nil? && !matches[3].nil?
end

Instance Method Details

#<=>(other) ⇒ Object


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 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

#inspectObject


152
153
154
# File 'lib/sem_version.rb', line 152

def inspect
  "#<SemVersion: #{to_s}>"
end

#satisfies?(constraint) ⇒ Boolean

Returns:

  • (Boolean)

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# 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_aObject


143
144
145
# File 'lib/sem_version.rb', line 143

def to_a
  [@major, @minor, @patch, @pre, ]
end

#to_hObject


147
148
149
150
# 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_sObject


136
137
138
139
140
141
# File 'lib/sem_version.rb', line 136

def to_s
  r = "#{@major}.#{@minor}.#{@patch}"
  r << "-#{@pre}" if @pre
  r << "+#{@metadata}" if 
  r
end