semi_semantic 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.
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ semi-semantic
2
+ =============
3
+
4
+ A Ruby library for parsing/formatting/comparing semi-semantic versions.
5
+
6
+ ### Why not just use Semantic Versioning?
7
+ The purpose of this library is to _support, but not require_ semantic versioning.
8
+
9
+ ### How is this different than Semantic Versioning?
10
+ Unlike SemVer, Semi-Semantic does not define exactly how it must be used.
11
+
12
+ Semi-Semantic...
13
+ - Allows unlimited version segment components separated by periods (accessed by array index).
14
+ - Does not have named concept of 'major', 'minor', or 'patch' versions.
15
+ - Supports underscores, to allow compatibility with ruby gem conventions and timestamps.
16
+
17
+ ### Usage
18
+
19
+ ```Ruby
20
+ require 'semi_semantic/version'
21
+ ...
22
+ version_string = '1.0.2-alpha.1+build.10'
23
+ version = SemiSemantic::Version.parse(version_string)
24
+
25
+ p version.release.to_s
26
+ # '1.0.2'
27
+
28
+ p version.pre_release.to_s
29
+ # 'alpha.1'
30
+
31
+ p version.post_release.to_s
32
+ # 'build.10'
33
+
34
+ # increment post-release number
35
+ p SemiSemantic::Version.new(version.release, version.pre_release, version.post_release.increment)
36
+ # '1.0.2-alpha.1+build.11'
37
+
38
+ # increment pre-release number
39
+ p SemiSemantic::Version.new(version.release, version.pre_release.increment)
40
+ # '1.0.2-alpha.2'
41
+
42
+ # increment release number
43
+ p SemiSemantic::Version.new(version.release.increment)
44
+ # '1.0.3'
45
+
46
+ # increment 'major' release number
47
+ p SemiSemantic::Version.new(version.release.increment(0))
48
+ # '2.0.0'
49
+
50
+ # increment 'minor' release number
51
+ p SemiSemantic::Version.new(version.release.increment(1))
52
+ # '1.1.0'
53
+
54
+ # increment second most least significant release number
55
+ p SemiSemantic::Version.new(version.release.increment(-2))
56
+ # '1.1.0'
57
+
58
+ ```
@@ -0,0 +1,3 @@
1
+ module SemiSemantic
2
+ class ParseError < StandardError; end
3
+ end
@@ -0,0 +1,73 @@
1
+ require 'semi_semantic/parse_error'
2
+ require 'semi_semantic/version_segment'
3
+
4
+ module SemiSemantic
5
+ class Version
6
+ include Comparable
7
+
8
+ PRE_RELEASE_PREFIX = '-'
9
+ POST_RELEASE_PREFIX = '+'
10
+
11
+ attr_reader :release, :pre_release, :post_release, :segments
12
+
13
+ # Parses a semi-semantic version string into up to three segments
14
+ # Raises a ParseError if the string format is invalid
15
+ # Raises an ArgumentError if version_string is nil
16
+ def self.parse(version_string)
17
+ raise ArgumentError.new 'Invalid Version String: nil' if version_string.nil?
18
+ raise ArgumentError.new "Invalid Version String: '#{version_string}'" if version_string == ''
19
+ matches = /^(?<release>[0-9A-Za-z_\.]+)(\-(?<pre_release>[0-9A-Za-z_\-\.]+))?(\+(?<post_release>[0-9A-Za-z_\-\.]+))?$/.match(version_string)
20
+ raise ParseError.new "Invalid Version Format: '#{version_string}'" if matches.nil?
21
+
22
+ release = VersionSegment.parse matches[:release]
23
+ pre_release = nil
24
+ if matches[:pre_release]
25
+ pre_release = VersionSegment.parse matches[:pre_release]
26
+ end
27
+ post_release = nil
28
+ if matches[:post_release]
29
+ post_release = VersionSegment.parse matches[:post_release]
30
+ end
31
+ self.new(release, pre_release, post_release)
32
+ end
33
+
34
+ def initialize(release, pre_release=nil, post_release=nil)
35
+ raise ArgumentError.new 'Invalid Release: nil' if release.nil?
36
+ @release = release
37
+ @pre_release = pre_release
38
+ @post_release = post_release
39
+
40
+ @segments = [release]
41
+ @segments << pre_release unless pre_release.nil?
42
+ @segments << post_release unless post_release.nil?
43
+ end
44
+
45
+ def <=>(other)
46
+ result = (@release <=> other.release)
47
+ return result unless result == 0
48
+
49
+ unless @pre_release.nil? && other.pre_release.nil?
50
+ return 1 if @pre_release.nil?
51
+ return -1 if other.pre_release.nil?
52
+ result = (@pre_release <=> other.pre_release)
53
+ return result unless result == 0
54
+ end
55
+
56
+ unless @post_release.nil? && other.post_release.nil?
57
+ return -1 if @post_release.nil?
58
+ return 1 if other.post_release.nil?
59
+ result = (@post_release <=> other.post_release)
60
+ return result unless result == 0
61
+ end
62
+
63
+ 0
64
+ end
65
+
66
+ def to_s
67
+ str = @release.to_s
68
+ str += PRE_RELEASE_PREFIX + @pre_release.to_s unless @pre_release.nil?
69
+ str += POST_RELEASE_PREFIX + @post_release.to_s unless @post_release.nil?
70
+ str
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,88 @@
1
+ require 'semi_semantic/parse_error'
2
+
3
+ module SemiSemantic
4
+ class VersionSegment
5
+ include Comparable
6
+
7
+ #TODO: immutable?
8
+ attr_reader :components
9
+
10
+ # Converts a string into a VersionCluster
11
+ # Raises a ParseError if the string format is invalid
12
+ # Raises an ArgumentError if version_string is nil
13
+ def self.parse(component_string)
14
+ raise ArgumentError.new 'Invalid Version Component String: nil' if component_string.nil?
15
+ self.new(component_string.split('.').map do |v|
16
+ if v.match(/\A[0-9]+\z/)
17
+ v.to_i
18
+ elsif v.match(/\A[0-9A-Za-z_\-]+\z/)
19
+ v
20
+ else
21
+ raise ParseError.new 'Invalid Version Component Format: Requires alphanumerics and hyphens only'
22
+ end
23
+ end)
24
+ end
25
+
26
+ # Construction can throw ArgumentError, but does no parsing or type-conversion
27
+ def initialize(components)
28
+ raise ArgumentError.new 'Invalid Version Components: nil' if components.nil?
29
+ raise ArgumentError.new "Invalid Version Components: #{components}" if components.empty? || components.include?('')
30
+ @components = components
31
+ end
32
+
33
+ def <=>(other)
34
+ a = @components
35
+ b = other.components
36
+ if a.size > b.size
37
+ comparison = a[0...b.size] <=> b
38
+ return comparison unless comparison == 0
39
+ a[b.size..-1].each {|v| return 1 unless v == 0 }
40
+ 0
41
+ elsif a.size < b.size
42
+ comparison = a <=> b[0...a.size]
43
+ return comparison unless comparison == 0
44
+ b[a.size..-1].each {|v| return -1 unless v == 0 }
45
+ 0
46
+ else
47
+ a <=> b
48
+ end
49
+ end
50
+
51
+ # Returns a copy of the VersionCluster with the integer at the provided index incremented by one.
52
+ # Raises a TypeError if the value at that index is not an integer.
53
+ def increment(index=-1)
54
+ value = @components[index]
55
+ raise TypeError.new "'#{value}' is not an integer" unless value.is_a? Integer
56
+
57
+ copy = Array.new @components
58
+ copy[index] = value + 1
59
+
60
+ while index < copy.size && index != -1
61
+ index += 1
62
+ value = copy[index]
63
+ if value.is_a? Integer
64
+ copy[index] = 0
65
+ end
66
+ end
67
+
68
+ self.class.new copy
69
+ end
70
+
71
+ # Returns a copy of the VersionCluster with the integer at the provided index decremented by one.
72
+ # Raises a TypeError if the value at that index is not an integer.
73
+ # Raises a RangeError if the value is zero or less
74
+ def decrement(index=-1)
75
+ value = @components[index]
76
+ raise TypeError.new "'#{value}' is not an integer" unless value.is_a? Integer
77
+ raise RangeError.new "'#{value}' is zero or less" unless value > 0
78
+
79
+ copy = Array.new @components
80
+ copy[index] = value - 1
81
+ self.class.new copy
82
+ end
83
+
84
+ def to_s
85
+ @components.join('.')
86
+ end
87
+ end
88
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: semi_semantic
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pivotal
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-28 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: 3.0.0.rc
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: 3.0.0.rc
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec-legacy_formatters
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - '='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.0.0.rc1
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - '='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0.rc1
46
+ description: ! 'Semi Semantic
47
+
48
+ 120471'
49
+ email: support@cloudfoundry.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - lib/semi_semantic/parse_error.rb
55
+ - lib/semi_semantic/version.rb
56
+ - lib/semi_semantic/version_segment.rb
57
+ - README.md
58
+ homepage: https://github.com/pivotal-cf-experimental/semi_semantic
59
+ licenses:
60
+ - Apache 2.0
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: 1.9.3
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 1.8.23
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: Semi Semantic
83
+ test_files: []