semverly 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/semverly.rb +1 -0
- data/lib/semverly/sem_ver.rb +115 -0
- data/spec/sem_ver_spec.rb +253 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea5a5be7cba05dc9e3324dbddfbd6335fba33eb2
|
4
|
+
data.tar.gz: ba3de76ced529808388e3fab10cc9822a569aa6d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4251076bd8a77b7d1f7bdc05183522e2421d0a5c69fa0331fc1239325d8a2c038041d201c77c0564d88d9095efdfc32eab527fb6a9e33c2a8fcf128d39f71579
|
7
|
+
data.tar.gz: 850412ea9b74f49c75301a03055a272bb533d9a021ea72ff6892a1f75f6a513122a4d952ec34c8b01b60d0aac2f5ec86eb53f1b0b161811d4fa86e9cabdfd98e
|
data/lib/semverly.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'semverly/sem_ver'
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# The SemVer class provides parsing and sorting for version strings that
|
2
|
+
# comply with the {Semantic Versioning 2.0.0}[http://semver.org/] specification.
|
3
|
+
class SemVer
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
# The major version (_x_ in +x.y.z+).
|
7
|
+
attr_accessor :major
|
8
|
+
|
9
|
+
# The minor version (_y_ in +x.y.z+).
|
10
|
+
attr_accessor :minor
|
11
|
+
|
12
|
+
# The patch version (_z_ in +x.y.z+).
|
13
|
+
attr_accessor :patch
|
14
|
+
|
15
|
+
# Pre-release version tag (optional).
|
16
|
+
attr_accessor :prerelease
|
17
|
+
|
18
|
+
# Version metadata (optional).
|
19
|
+
attr_accessor :metadata
|
20
|
+
|
21
|
+
# Creates a new SemVer instance.
|
22
|
+
#
|
23
|
+
# Initialization requires at least one argument: the major version number.
|
24
|
+
# The minor and patch versions are optional and are +0+ if not supplied. The
|
25
|
+
# prerelease and metadata parts are also optional and blank if not supplied.
|
26
|
+
def initialize(major, minor = 0, patch = 0, prerelease = nil, metadata = nil)
|
27
|
+
self.major = major
|
28
|
+
self.minor = minor
|
29
|
+
self.patch = patch
|
30
|
+
self.prerelease = prerelease
|
31
|
+
self.metadata = metadata
|
32
|
+
end
|
33
|
+
|
34
|
+
# Parses a SemVer-compliant string into its component parts.
|
35
|
+
#
|
36
|
+
# @param string [String] the version identifier to parse
|
37
|
+
# @return [SemVer] A +SemVer+ instance or +nil+ if the string is not SemVer compliant.
|
38
|
+
def self.parse(string)
|
39
|
+
re = Regexp.new('\Av?(?<major>\d+)
|
40
|
+
(\.(?<minor>\d+))?
|
41
|
+
(\.(?<patch>\d+))?
|
42
|
+
(-(?<prerelease>[0-9A-Za-z.-]+))?
|
43
|
+
(\+(?<metadata>[0-9A-Za-z.-]+))?\z', Regexp::EXTENDED)
|
44
|
+
matches = re.match(string)
|
45
|
+
return nil if matches.nil?
|
46
|
+
|
47
|
+
major = matches['major'].to_i
|
48
|
+
minor = matches['minor'].to_i
|
49
|
+
patch = matches['patch'].to_i
|
50
|
+
|
51
|
+
if prerelease = matches['prerelease']
|
52
|
+
return nil if prerelease.split('.').any? { |part| part.nil? || part == '' }
|
53
|
+
end
|
54
|
+
|
55
|
+
if metadata = matches['metadata']
|
56
|
+
return nil if metadata.split('.').any? { |part| part.nil? || part == '' }
|
57
|
+
end
|
58
|
+
|
59
|
+
new(major, minor, patch, prerelease, metadata)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Generates a version string from SemVer parts.
|
63
|
+
#
|
64
|
+
# @return [String] The version identifier.
|
65
|
+
def to_s
|
66
|
+
s = "#{major}.#{minor}.#{patch}"
|
67
|
+
s << "-#{prerelease}" unless prerelease.nil?
|
68
|
+
s << "+#{metadata}" unless metadata.nil?
|
69
|
+
s
|
70
|
+
end
|
71
|
+
|
72
|
+
# Compares two SemVer versions.
|
73
|
+
#
|
74
|
+
# @param other [SemVer] another +SemVer+ instance to compare with the receiver
|
75
|
+
# @return [Fixnum] -1, 0 or +1 depending on if this instance is less than, equal to or greater than +other+.
|
76
|
+
def <=>(other)
|
77
|
+
result = [self.major, self.minor, self.patch] <=> [other.major, other.minor, other.patch]
|
78
|
+
result = compare_prerelease(other) if result == 0
|
79
|
+
result
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def compare_prerelease(other)
|
85
|
+
return 0 if prerelease.nil? && other.prerelease.nil?
|
86
|
+
return -1 if other.prerelease.nil?
|
87
|
+
return 1 if prerelease.nil?
|
88
|
+
|
89
|
+
my_parts = prerelease.split('.')
|
90
|
+
other_parts = other.prerelease.split('.')
|
91
|
+
|
92
|
+
loop do
|
93
|
+
my_part = my_parts.shift
|
94
|
+
other_part = other_parts.shift
|
95
|
+
|
96
|
+
return 0 if my_part.nil? && other_part.nil?
|
97
|
+
return -1 if my_part.nil?
|
98
|
+
return 1 if other_part.nil?
|
99
|
+
|
100
|
+
mine_numeric = my_part =~ /\A\d+\z/
|
101
|
+
other_numeric = other_part =~ /\A\d+\z/
|
102
|
+
if mine_numeric && other_numeric
|
103
|
+
result = my_part.to_i <=> other_part.to_i
|
104
|
+
return result unless result == 0
|
105
|
+
elsif mine_numeric
|
106
|
+
return -1
|
107
|
+
elsif other_numeric
|
108
|
+
return 1
|
109
|
+
else
|
110
|
+
result = my_part <=> other_part
|
111
|
+
return result unless result == 0
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,253 @@
|
|
1
|
+
shared_examples 'parsing' do
|
2
|
+
context 'without pre-release or metadata' do
|
3
|
+
context 'with major, minor and patch' do
|
4
|
+
subject { SemVer.parse("#{prefix}1.2.3") }
|
5
|
+
|
6
|
+
it 'has major version 1' do
|
7
|
+
expect(subject.major).to eq(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'has minor version 2' do
|
11
|
+
expect(subject.minor).to eq(2)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has patch version 3' do
|
15
|
+
expect(subject.patch).to eq(3)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'without patch' do
|
20
|
+
subject { SemVer.parse("#{prefix}1.2") }
|
21
|
+
|
22
|
+
it 'has major version 1' do
|
23
|
+
expect(subject.major).to eq(1)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'has minor version 2' do
|
27
|
+
expect(subject.minor).to eq(2)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'has patch version 0' do
|
31
|
+
expect(subject.patch).to eq(0)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'without minor or patch' do
|
36
|
+
subject { SemVer.parse("#{prefix}1") }
|
37
|
+
|
38
|
+
it 'has major version 1' do
|
39
|
+
expect(subject.major).to eq(1)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'has minor version 0' do
|
43
|
+
expect(subject.minor).to eq(0)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'has patch version 0' do
|
47
|
+
expect(subject.patch).to eq(0)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'with pre-release' do
|
53
|
+
subject { SemVer.parse("#{prefix}1.2.3-beta.1")}
|
54
|
+
|
55
|
+
it 'has major version 1' do
|
56
|
+
expect(subject.major).to eq(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'has minor version 2' do
|
60
|
+
expect(subject.minor).to eq(2)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'has patch version 3' do
|
64
|
+
expect(subject.patch).to eq(3)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'has pre-release beta.1' do
|
68
|
+
expect(subject.prerelease).to eq('beta.1')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with metadata' do
|
73
|
+
subject { SemVer.parse("#{prefix}1.2.3+abc123")}
|
74
|
+
|
75
|
+
it 'has major version 1' do
|
76
|
+
expect(subject.major).to eq(1)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'has minor version 2' do
|
80
|
+
expect(subject.minor).to eq(2)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'has patch version 3' do
|
84
|
+
expect(subject.patch).to eq(3)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'has metadata abc123' do
|
88
|
+
expect(subject.metadata).to eq('abc123')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'with pre-release and metadata' do
|
93
|
+
subject { SemVer.parse("#{prefix}1.2.3-beta.1+abc123")}
|
94
|
+
|
95
|
+
it 'has major version 1' do
|
96
|
+
expect(subject.major).to eq(1)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'has minor version 2' do
|
100
|
+
expect(subject.minor).to eq(2)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'has patch version 3' do
|
104
|
+
expect(subject.patch).to eq(3)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'has pre-release beta.1' do
|
108
|
+
expect(subject.prerelease).to eq('beta.1')
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'has metadata abc123' do
|
112
|
+
expect(subject.metadata).to eq('abc123')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe SemVer do
|
118
|
+
describe '.new' do
|
119
|
+
subject { SemVer.new(1) }
|
120
|
+
|
121
|
+
it 'has major version 1' do
|
122
|
+
expect(subject.major).to eq(1)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'has minor version 0' do
|
126
|
+
expect(subject.minor).to eq(0)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'has minor version 0' do
|
130
|
+
expect(subject.patch).to eq(0)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'is not a pre-release' do
|
134
|
+
expect(subject.prerelease).to be_nil
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'has no metadata' do
|
138
|
+
expect(subject.metadata).to be_nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe '.parse' do
|
143
|
+
context 'with a version prefix' do
|
144
|
+
let(:prefix) { 'v' }
|
145
|
+
include_examples 'parsing'
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'without a version prefix' do
|
149
|
+
let(:prefix) { '' }
|
150
|
+
include_examples 'parsing'
|
151
|
+
end
|
152
|
+
|
153
|
+
context 'with a non-SemVer compliant string' do
|
154
|
+
{
|
155
|
+
'non-numeric major, minor and patch parts' => 'a.b.c',
|
156
|
+
'invalid characters in pre-release' => '1.2.3-this/that',
|
157
|
+
'blank identifier in pre-release' => '1.2.3-this..that',
|
158
|
+
'invalid characters in metadata' => '1.2.3+this/that',
|
159
|
+
'blank identifier in metadata' => '1.2.3+this..that'
|
160
|
+
}.each do |description, string|
|
161
|
+
it "returns nil with #{description}" do
|
162
|
+
expect(SemVer.parse(string)).to be_nil
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '#to_s' do
|
169
|
+
context 'without pre-release or metadata' do
|
170
|
+
it 'returns a properly formatted string' do
|
171
|
+
semver = SemVer.new(1, 2, 3)
|
172
|
+
expect(semver.to_s).to eq('1.2.3')
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'with pre-release' do
|
177
|
+
it 'returns a properly formatted string' do
|
178
|
+
semver = SemVer.new(1, 2, 3, 'beta')
|
179
|
+
expect(semver.to_s).to eq('1.2.3-beta')
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'with metadata' do
|
184
|
+
it 'returns a properly formatted string' do
|
185
|
+
semver = SemVer.new(1, 2, 3, nil, 'abc123')
|
186
|
+
expect(semver.to_s).to eq('1.2.3+abc123')
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'with pre-release and metadata' do
|
191
|
+
it 'returns a properly formatted string' do
|
192
|
+
semver = SemVer.new(1, 2, 3, 'beta', 'abc123')
|
193
|
+
expect(semver.to_s).to eq('1.2.3-beta+abc123')
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe 'sorting' do
|
199
|
+
it 'sorts the major version numerically' do
|
200
|
+
first = SemVer.new(1)
|
201
|
+
second = SemVer.new(2)
|
202
|
+
expect(first <=> second).to eq(-1)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'sorts the minor version numerically' do
|
206
|
+
first = SemVer.new(1, 1)
|
207
|
+
second = SemVer.new(1, 2)
|
208
|
+
expect(first <=> second).to eq(-1)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'sorts the patch version numerically' do
|
212
|
+
first = SemVer.new(1, 1, 1)
|
213
|
+
second = SemVer.new(1, 1, 2)
|
214
|
+
expect(first <=> second).to eq(-1)
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'sorts pre-release versions before release' do
|
218
|
+
first = SemVer.new(1, 0, 0, 'beta')
|
219
|
+
second = SemVer.new(1, 0, 0)
|
220
|
+
expect(first <=> second).to eq(-1)
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'sorts pre-release parts with only digits numerically' do
|
224
|
+
first = SemVer.new(1, 0, 0, 'beta.2')
|
225
|
+
second = SemVer.new(1, 0, 0, 'beta.10')
|
226
|
+
expect(first <=> second).to eq(-1)
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'sorts pre-release parts with letters lexically' do
|
230
|
+
first = SemVer.new(1, 0, 0, 'alpha')
|
231
|
+
second = SemVer.new(1, 0, 0, 'beta')
|
232
|
+
expect(first <=> second).to eq(-1)
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'sorts numeric pre-release parts before non-numeric' do
|
236
|
+
first = SemVer.new(1, 0, 0, 'alpha.1')
|
237
|
+
second = SemVer.new(1, 0, 0, 'alpha.beta')
|
238
|
+
expect(first <=> second).to eq(-1)
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'sorts fewer pre-release parts before more' do
|
242
|
+
first = SemVer.new(1, 0, 0, 'alpha')
|
243
|
+
second = SemVer.new(1, 0, 0, 'alpha.1')
|
244
|
+
expect(first <=> second).to eq(-1)
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'does not consider metadata when sorting' do
|
248
|
+
first = SemVer.new(1, 0, 0, nil, 'foo')
|
249
|
+
second = SemVer.new(1, 0, 0, nil, 'bar')
|
250
|
+
expect(first <=> second).to eq(0)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: semverly
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Steve Madsen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-08-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.14'
|
27
|
+
description: Parses and compares version strings that comply with Semantic Versioning
|
28
|
+
2.0 (http://semver.org/)
|
29
|
+
email: steve@lightyearsoftware.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/semverly/sem_ver.rb
|
35
|
+
- lib/semverly.rb
|
36
|
+
- spec/sem_ver_spec.rb
|
37
|
+
homepage: http://github.com/lightyear/semverly
|
38
|
+
licenses:
|
39
|
+
- MIT
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 2.0.3
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: Semantic Versioning parsing and comparison
|
61
|
+
test_files:
|
62
|
+
- spec/sem_ver_spec.rb
|
63
|
+
has_rdoc:
|