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.
@@ -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
@@ -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: