semantic_versioning 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ require 'semantic_versioning/gem_version'
2
+ require 'semantic_versioning/version'
3
+ require 'semantic_versioning/segment'
4
+ require 'semantic_versioning/version_set'
5
+ require 'semantic_versioning/errors'
@@ -0,0 +1,6 @@
1
+ module SemanticVersioning
2
+
3
+ class ParsingError < ArgumentError; end
4
+ class IncrementError < ArgumentError; end
5
+
6
+ end
@@ -0,0 +1,3 @@
1
+ module SemanticVersioning
2
+ GEM_VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,44 @@
1
+ module SemanticVersioning
2
+
3
+ class Segment
4
+
5
+ include Comparable
6
+
7
+ def initialize(input)
8
+ @input = input
9
+ end
10
+
11
+ def identifiers
12
+ return [] if @input.nil?
13
+ ids = @input.split('.')
14
+ ids.map! { |id| id.match(/^[0-9]+$/) ? id.to_i : id }
15
+ end
16
+
17
+ def <=>(other)
18
+ return nil unless other.is_a?(Segment)
19
+ scores = scores_vs(other)
20
+ scores.compact.detect { |s| s.abs == 1 }
21
+ end
22
+
23
+ private
24
+
25
+ def scores_vs(other)
26
+ ids = { :self => identifiers, :other => other.identifiers }
27
+ pids = pad_ids(ids)
28
+ pids[:self].map.with_index { |id, idx| id <=> pids[:other][idx] }
29
+ end
30
+
31
+ def pad_ids(id_arrays)
32
+ sorted = id_arrays.sort { |a, b| b[1].length <=> a[1].length }
33
+ str, int = '', 0
34
+ sorted[0][1].each_with_index do |id, idx|
35
+ if idx >= sorted[1][1].length
36
+ sorted[1][1] << (id.is_a?(Integer) ? int : str)
37
+ end
38
+ end
39
+ Hash[sorted]
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,92 @@
1
+ module SemanticVersioning
2
+
3
+ class Version
4
+
5
+ include Comparable
6
+
7
+ PATTERN = /^
8
+ (?<required>
9
+ (?<major>[0-9]+)\.
10
+ (?<minor>[0-9]+)\.
11
+ (?<patch>[0-9]+)
12
+ )
13
+ (-(?<pre>[0-9A-Za-z\-\.]+))?
14
+ (\+(?<build>[0-9A-Za-z\-\.]+))?
15
+ $/x
16
+
17
+ attr_reader :major, :minor, :patch, :required, :segments
18
+ attr_accessor :pre, :build
19
+ alias_method :prerelease, :pre
20
+
21
+ def initialize(input, segment = Segment)
22
+ if (m = input.match(PATTERN))
23
+ @input = input
24
+ @major, @minor, @patch = m['major'].to_i, m['minor'].to_i, m['patch'].to_i
25
+ @required, @pre, @build = m['required'], m['pre'], m['build']
26
+ @segments = [
27
+ segment.new(@required),
28
+ segment.new(@pre),
29
+ segment.new(@build)
30
+ ]
31
+ else
32
+ raise ParsingError, 'String input not correctly formatted for Semantic Versioning'
33
+ end
34
+ end
35
+
36
+ def <=>(other)
37
+ return nil unless other.is_a?(Version)
38
+ return 0 if to_s == other.to_s
39
+ if required == other.required
40
+ return -1 if pre && other.pre.nil?
41
+ return 1 if pre.nil? && other.pre
42
+ end
43
+ scores = segments.map.with_index { |s, idx| s <=> other.segments[idx] }
44
+ scores.compact.detect { |s| s.abs == 1 }
45
+ end
46
+
47
+ def eql?(other)
48
+ self == other
49
+ end
50
+
51
+ def hash
52
+ to_s.hash
53
+ end
54
+
55
+ def to_s
56
+ version = "#{@major}.#{@minor}.#{@patch}"
57
+ version += "-#{@pre}" unless pre.nil?
58
+ version += "+#{@build}" unless build.nil?
59
+ version
60
+ end
61
+
62
+ def increment(identifier)
63
+ case identifier
64
+ when :major
65
+ @major += 1
66
+ reset(:minor, :patch)
67
+ when :minor
68
+ @minor += 1
69
+ reset(:patch)
70
+ when :patch
71
+ @patch += 1
72
+ else
73
+ raise IncrementError, 'Only :major, :minor and :patch attributes may be incremented'
74
+ end
75
+ clear_optional_identifiers
76
+ end
77
+
78
+ private
79
+
80
+ def reset(*identifiers)
81
+ identifiers.each do |id|
82
+ instance_variable_set(:"@#{id}", 0)
83
+ end
84
+ end
85
+
86
+ def clear_optional_identifiers
87
+ @pre, @build = nil, nil
88
+ end
89
+
90
+ end
91
+
92
+ end
@@ -0,0 +1,15 @@
1
+ require 'set'
2
+
3
+ module SemanticVersioning
4
+
5
+ class VersionSet < SortedSet
6
+
7
+ def where(operator, version)
8
+ version = Version.new(version) if version.is_a? String
9
+ subset = select { |v| v.send(operator, version) }
10
+ self.class.new subset
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe SemanticVersioning::VersionSet do
4
+
5
+ let(:versions) { [v('1.0.0'), v('1.0.10'), v('0.10.0')] }
6
+ let(:set) { SemanticVersioning::VersionSet.new(versions) }
7
+
8
+ it 'yields version in correct order' do
9
+ results = set.map{ |v| v }
10
+ results.should == [v('0.10.0'), v('1.0.0'), v('1.0.10')]
11
+ end
12
+
13
+ it 'discards duplicates' do
14
+ set << v('1.0.0')
15
+ set.length.should == versions.length
16
+ end
17
+
18
+ describe '#where' do
19
+ it 'returns a new VersionSet' do
20
+ set.where(:<, v('1.0.0')).should be_a SemanticVersioning::VersionSet
21
+ end
22
+ it 'allows version to be given as a string' do
23
+ set.where(:<, '1.0.0').first.should be_a SemanticVersioning::Version
24
+ end
25
+ specify { set.where(:<, v('1.0.10')).length.should == 2 }
26
+ specify { set.where(:<=, v('1.0.10')).length.should == 3 }
27
+ specify { set.where(:==, v('1.0.0')).length.should == 1 }
28
+ specify { set.where(:>=, v('1.0.0')).length.should == 2 }
29
+ specify { set.where(:>, v('0.10.0')).length.should == 2 }
30
+ end
31
+
32
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe SemanticVersioning::Version do
4
+
5
+ let(:version) { v('1.2.3-pre.1+build.2') }
6
+
7
+ describe 'accepts only properly formatted strings' do
8
+ specify do
9
+ lambda { v('1.0.0-beta.1') }.should_not raise_error
10
+ lambda { v('1.0.0.0') }.should raise_error(SemanticVersioning::ParsingError)
11
+ end
12
+ end
13
+
14
+ describe 'instance methods' do
15
+ specify { version.major.should == 1 }
16
+ specify { version.minor.should == 2 }
17
+ specify { version.patch.should == 3 }
18
+ specify { version.pre.should == 'pre.1' }
19
+ specify { version.prerelease.should == version.pre }
20
+ specify { version.build.should == 'build.2' }
21
+ end
22
+
23
+ describe 'segments' do
24
+ specify do
25
+ version.segments.all? { |s| s.is_a?(SemanticVersioning::Segment) }.should be_true
26
+ end
27
+ end
28
+
29
+ describe 'comparisons from semver.org spec 2.0.0-rc.1' do
30
+ specify { v('1.0.0-alpha').should be < v('1.0.0-alpha.1') }
31
+ specify { v('1.0.0-alpha.1').should be < v('1.0.0-beta.2') }
32
+ specify { v('1.0.0-beta.2').should be < v('1.0.0-beta.11') }
33
+ specify { v('1.0.0-beta.11').should be < v('1.0.0-rc.1') }
34
+ specify { v('1.0.0-rc.1').should be < v('1.0.0-rc.1+build.1') }
35
+ specify { v('1.0.0-rc.1+build.1').should be < v('1.0.0') }
36
+ specify { v('1.0.0').should be < v('1.0.0+0.3.7') }
37
+ specify { v('1.0.0+0.3.7').should be < v('1.3.7+build') }
38
+ specify { v('1.3.7+build').should be < v('1.3.7+build.2.b8f12d7') }
39
+ specify { v('1.3.7+build.2.b8f12d7').should be < v('1.3.7+build.11.e0f985a') }
40
+ end
41
+
42
+ describe 'more comparisons' do
43
+ specify { v('1.0.0').should be > v('1.0.0-rc.1+build.1') }
44
+ specify { v('1.0.0-alpha').should == v('1.0.0-alpha') }
45
+ end
46
+
47
+ describe '#increment' do
48
+ specify 'major' do
49
+ version.increment(:major)
50
+ version.to_s.should == '2.0.0'
51
+ end
52
+ specify 'minor' do
53
+ version.increment(:minor)
54
+ version.to_s.should == '1.3.0'
55
+ end
56
+ specify 'patch' do
57
+ version.increment(:patch)
58
+ version.to_s.should == '1.2.4'
59
+ end
60
+ end
61
+
62
+ describe 'resetting pre and build' do
63
+ specify 'pre' do
64
+ version.pre = 'pre.2'
65
+ version.to_s.should == '1.2.3-pre.2+build.2'
66
+ end
67
+ specify 'build' do
68
+ version.build = 'build.99'
69
+ version.to_s.should == '1.2.3-pre.1+build.99'
70
+ end
71
+ end
72
+
73
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.require
4
+
5
+ require_relative '../lib/semantic_versioning'
6
+
7
+ def v(i)
8
+ SemanticVersioning::Version.new(i)
9
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: semantic_versioning
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joe Corcoran
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.12.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.12.0
30
+ description: A utility for working with software version numbers as specified by Semantic
31
+ Versioning (http://semver.org/)
32
+ email:
33
+ - joecorcoran@gmail.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - lib/semantic_versioning/errors.rb
39
+ - lib/semantic_versioning/gem_version.rb
40
+ - lib/semantic_versioning/segment.rb
41
+ - lib/semantic_versioning/version.rb
42
+ - lib/semantic_versioning/version_set.rb
43
+ - lib/semantic_versioning.rb
44
+ - spec/semantic_versioning/version_set_spec.rb
45
+ - spec/semantic_versioning/version_spec.rb
46
+ - spec/spec_helper.rb
47
+ homepage: http://github.com/joecorcoran/semantic_versioning
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 1.8.25
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Ruby classes to represent versions and sorted sets of versions. No funny
71
+ business. Updated as the SemVer spec develops.
72
+ test_files:
73
+ - spec/semantic_versioning/version_set_spec.rb
74
+ - spec/semantic_versioning/version_spec.rb
75
+ - spec/spec_helper.rb