semverly 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: