vmlib 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,55 @@
1
+ ###############################################################################
2
+ # VMLib exceptions
3
+ ###############################################################################
4
+ # Copyright (C) 2013 Nirenjan Krishnan
5
+ # All rights reserved.
6
+ ###############################################################################
7
+
8
+ ;
9
+
10
+ module VMLib
11
+
12
+
13
+ # This is a namespace for errors that can be thrown by VMLib
14
+ module Errors
15
+
16
+ # Base class for all VMLib exceptions
17
+ class VMLibError < ::RuntimeError
18
+ end
19
+
20
+ # Thrown if release type doesn't match with requested parameter
21
+ class NoVersionError < VMLibError
22
+ end
23
+
24
+ # Thrown if bump operation cannot be performed
25
+ class BumpError < VMLibError
26
+ end
27
+
28
+ # Thrown if initialization fails
29
+ class InitError < VMLibError
30
+ end
31
+
32
+ # Thrown if parsing fails
33
+ class ParseError < VMLibError
34
+ end
35
+
36
+ # Thrown if assigning an invalid value
37
+ class AssignError < VMLibError
38
+ end
39
+
40
+ # Thrown if the API receives invalid parameters
41
+ class ParameterError < VMLibError
42
+ end
43
+
44
+ # Thrown if there's an issue with the path for the version file
45
+ class PathError < VMLibError
46
+ end
47
+
48
+ # Thrown if there's an issue with the version file itself
49
+ class VersionFileError < VMLibError
50
+ end
51
+ end
52
+
53
+
54
+ end
55
+
data/lib/vmlib/file.rb ADDED
@@ -0,0 +1,106 @@
1
+ ###############################################################################
2
+ # VMLib file manager
3
+ ###############################################################################
4
+ # Copyright (C) 2013 Nirenjan Krishnan
5
+ # All rights reserved.
6
+ ###############################################################################
7
+
8
+ ;
9
+
10
+ module VMLib
11
+
12
+ # This is the file class for handling VMLib version files
13
+ class File
14
+
15
+ # Default file name of 'Version'
16
+ FILE_NAME = 'Version'
17
+
18
+ # Create a new file object with the specified filename. If the filename
19
+ # is not specified, it reverts to the default
20
+ def initialize(filename = nil)
21
+ @filename ||= FILE_NAME
22
+ @dir = nil
23
+ end
24
+
25
+ # Search the current and all parent folders for a version file
26
+ # Raises an error if it cannot find any up to the root.
27
+ def find_file(dir = nil)
28
+ dir ||= ::Dir.pwd
29
+ raise Errors::PathError unless ::File.directory?(dir)
30
+ path = ::File.join(dir, @filename)
31
+
32
+ ::Dir.chdir(dir) do
33
+ while !::File.exists?(path) do
34
+ if (::File.dirname(path).match(/^(\w:\/|\/)$/i))
35
+ raise Errors::NoVersionError, "#{dir} is not versioned"
36
+ end
37
+
38
+ path = ::File.join(::File.dirname(path), '..')
39
+ path = ::File.expand_path(path)
40
+ #puts "vmlib: looking at path #{path}"
41
+ path = ::File.join(path, @filename)
42
+ end
43
+ @dir = path
44
+ return path
45
+ end
46
+ end
47
+
48
+ # Read the version file and return the parsed version data
49
+ def read(dir = nil)
50
+ path = find_file(dir)
51
+
52
+ if ::File.readable?(path)
53
+ # Read
54
+ verdata = Version.new
55
+ verdata.parse ::File.read(path)
56
+ else
57
+ raise Errors::VersionFileError, "unable to read #{path}"
58
+ end
59
+
60
+ return verdata
61
+ end
62
+
63
+ # Write the specfied version into the version file
64
+ def write(version, dir = nil)
65
+ path = find_file(dir)
66
+
67
+ unless version.is_a? Version
68
+ raise Errors::ParameterError, "invalid version #{version}"
69
+ end
70
+
71
+ if ::File.writable?(path)
72
+ # Write
73
+ ::File.write(path, version.to_s + "\n")
74
+ else
75
+ raise Errors::VersionFileError, "unable to write #{path}"
76
+ end
77
+ end
78
+
79
+ # Create a new version file for the current (or specified) folder.
80
+ # If it already exists, then throw an error
81
+ def create(name = '', dir = nil)
82
+ # Make sure that the directory isn't versioned already
83
+ begin
84
+ path = find_file(dir)
85
+ rescue Errors::NoVersionError
86
+ # We are good to go
87
+ else
88
+ # Raise error that it's already versioned
89
+ raise Errors::VersionFileError, "version already exists at #{path}"
90
+ end
91
+
92
+ dir ||= ::Dir.pwd
93
+ raise Errors::PathError unless ::File.directory?(dir)
94
+ path = ::File.join(dir, @filename)
95
+
96
+ ::File.open(path, "w") do |file|
97
+ version = Version.new(name)
98
+ file.write version.to_s + "\n"
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+
105
+
106
+ end
@@ -0,0 +1,94 @@
1
+ ###############################################################################
2
+ # VMLib formatter
3
+ ###############################################################################
4
+ # Copyright (C) 2013 Nirenjan Krishnan
5
+ # All rights reserved.
6
+ ###############################################################################
7
+
8
+ ;
9
+
10
+ module VMLib
11
+
12
+ class Version
13
+
14
+ # Default tag format for version control systems
15
+ TAG_FORMAT = 'v%M.%m.%p%r%b'
16
+
17
+ # Display the version in the specified format
18
+ # Input: Format string
19
+ # %n - name
20
+ # %M - major version number
21
+ # %m - minor version number
22
+ # %p - patch number
23
+ # %r - prerelease
24
+ # %b - build number
25
+ def format(fstr)
26
+ if @name.empty?
27
+ fstr = fstr.gsub('%n', '')
28
+ else
29
+ fstr = fstr.gsub('%n', @name.to_s + ' ')
30
+ end
31
+
32
+ # Match the major version
33
+ match = /%(\d*)M/.match(fstr)
34
+ fstr = fstr.gsub(/%(\d*)M/, "%0#{$1}d" % @major)
35
+
36
+ # Match the minor version
37
+ match = /%(\d*)m/.match(fstr)
38
+ fstr = fstr.gsub(/%(\d*)m/, "%0#{$1}d" % @minor)
39
+
40
+ # Match the patch version
41
+ match = /%(\d*)p/.match(fstr)
42
+ fstr = fstr.gsub(/%(\d*)p/, "%0#{$1}d" % @patch)
43
+
44
+ if (@reltype == :rel_type_final) or
45
+ (@reltype == :rel_type_custom and @relcustom.length == 0)
46
+ fstr = fstr.gsub('%r', '')
47
+ else
48
+ fstr = case @reltype
49
+ when :rel_type_dev
50
+ fstr.gsub('%r', "-#{@devnum}")
51
+ when :rel_type_alpha
52
+ fstr.gsub('%r', "-a.#{@alphanum}")
53
+ when :rel_type_beta
54
+ fstr.gsub('%r', "-b.#{@betanum}")
55
+ when :rel_type_rc
56
+ fstr.gsub('%r', "-rc.#{@rcnum}")
57
+ when :rel_type_custom
58
+ fstr.gsub('%r', '-' + @relcustom.join('.'))
59
+ else
60
+ fstr.gsub('%r', '')
61
+ end
62
+ end
63
+
64
+ if (@buildtype == :bld_type_final) or
65
+ (@buildtype == :bld_type_custom and @buildcustom.length == 0)
66
+ fstr = fstr.gsub('%b', '')
67
+ else
68
+ fstr = case @buildtype
69
+ when :bld_type_custom
70
+ fstr.gsub('%b', '+' + @buildcustom.join('.'))
71
+ else
72
+ fstr.gsub('%b', '')
73
+ end
74
+ end
75
+
76
+ fstr
77
+ end
78
+
79
+ # Display a version tag suitable for use in tagging releases
80
+ # in the user's version control system
81
+ def tag
82
+ format TAG_FORMAT
83
+ end
84
+
85
+ # Display the version information as a string
86
+ def to_s
87
+ format "%n%M.%m.%p%r%b"
88
+ end
89
+
90
+
91
+ end
92
+
93
+
94
+ end
@@ -0,0 +1,13 @@
1
+ In paragraphs 10 and 11, the following text is present.
2
+
3
+ > Identifiers MUST be comprised of only ASCII alphanumerics and dash [0-9A-Za-z-].
4
+
5
+ However, it is unclear if an identifier can begin or end with a dash, or have multiple dashes in sequence. If that is allowed, then in theory, we could have identifiers like the following.
6
+
7
+ 1.0.0--+-
8
+ 1.0.0-----+----
9
+ 1.0.0--alpha--.-1-
10
+ 1.0.0--beta-.-2-+--build-256--
11
+ 1.0.0+-256
12
+
13
+ My understanding of the text indicates that this is not disallowed, but in my opinion, we should have the text
@@ -0,0 +1,216 @@
1
+ ###############################################################################
2
+ # VMLib parser
3
+ ###############################################################################
4
+ # Copyright (C) 2013 Nirenjan Krishnan
5
+ # All rights reserved.
6
+ ###############################################################################
7
+
8
+ ;
9
+
10
+ module VMLib
11
+
12
+ class Version
13
+
14
+ # The parse functions are all marked as private
15
+ private
16
+
17
+ # Regular expression format to understand the release and build formats.
18
+ #
19
+ # Acceptable formats:
20
+ # alpha.1.4
21
+ # beta.1.4
22
+ # 6.2.8
23
+ # 1
24
+ # build-256.2013-01-04T16-40Z
25
+ #
26
+ # These fields consist of dot-separated identifiers, and these identifiers
27
+ # may contain only alphanumeric characters and hyphens.
28
+ #
29
+ # Fields consisting of only digits will be interpreted as numeric and
30
+ # leading zeroes will be stripped.
31
+ SPECIAL_REGEX = /^[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*/
32
+
33
+ # Regular expression to understand the version format.
34
+ #
35
+ # Acceptable formats:
36
+ # 1.078.2
37
+ # v1.30.4908
38
+ # version 2.4.875
39
+ #
40
+ # Leading zeroes will be stripped in any numeric field, therefore, the
41
+ # version 1.078.2 would be treated the same as 1.78.2
42
+ VER_REGEX = /^(?:v|version )?(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)/
43
+
44
+ # Regular expression format to retrieve the name. Names may consist of
45
+ # any combination of any alphanumeric character, underscores and hyphens.
46
+ NAME_REGEX = /^(?<name>[0-9A-Za-z_-]+)\s+/
47
+
48
+ def convert_to_integer(array)
49
+ array.kind_of? Array or
50
+ raise Errors::VMLibError, "not an array: #{array}"
51
+
52
+ for i in (0...array.length)
53
+ array[i].to_s.match(/^\d+$/) and array[i] = array[i].to_i
54
+ end
55
+ end
56
+
57
+ def parse_release(str)
58
+ match = SPECIAL_REGEX.match(str)
59
+ if match
60
+ # OK, we have a prerelease match, now parse it to determine
61
+ # the release type
62
+ @relcustom = match[0].split '.'
63
+ rel = @relcustom[0]
64
+ case rel
65
+
66
+ # Development version 1.0.0-0, 1.0.0-1, 1.0.0-2, etc.
67
+ when /^\d+$/
68
+ if @relcustom.length == 1
69
+ @reltype = :rel_type_dev
70
+ @devnum = rel.to_i
71
+ else
72
+ @reltype = :rel_type_custom
73
+ end
74
+
75
+ # Alpha version 1.0.0-a.1, 1.0.0-alpha.2, etc.
76
+ when /^(a|alpha)$/
77
+ if @relcustom.length == 2 && @relcustom[1].match(/^\d+$/)
78
+ @reltype = :rel_type_alpha
79
+ @alphanum = @relcustom[1].to_i
80
+ else
81
+ @reltype = :rel_type_custom
82
+ end
83
+
84
+ # Beta version 1.0.0-b.1, 1.0.0-beta.2, etc.
85
+ when /^(b|beta)$/
86
+ if @relcustom.length == 2 && @relcustom[1].match(/^\d+$/)
87
+ @reltype = :rel_type_beta
88
+ @betanum = @relcustom[1].to_i
89
+ else
90
+ @reltype = :rel_type_custom
91
+ end
92
+
93
+ # Release Candidate version 1.0.0-rc.1, 1.0.0-rc.2, etc.
94
+ when /^rc$/
95
+ if @relcustom.length == 2 && @relcustom[1].match(/^\d+$/)
96
+ @reltype = :rel_type_rc
97
+ @rcnum = @relcustom[1].to_i
98
+ else
99
+ @reltype = :rel_type_custom
100
+ end
101
+
102
+ else
103
+ @reltype = :rel_type_custom
104
+ end
105
+
106
+ # Done parsing, clear the relcustom array if it's not a custom type
107
+ @relcustom = [] unless @reltype == :rel_type_custom
108
+ convert_to_integer(@relcustom)
109
+ else # if !match
110
+ # It may be an empty string, so set the reltype to final in that case
111
+ if str.empty?
112
+ match = nil
113
+ @reltype = :rel_type_final
114
+ else
115
+ raise Errors::ParseError, "unrecognized prerelease '#{str}'"
116
+ end
117
+ end
118
+
119
+ return match
120
+ end
121
+
122
+ def parse_build(str)
123
+ match = SPECIAL_REGEX.match(str)
124
+ if match
125
+ # OK, we have a build match, now parse it to determine
126
+ # the release type
127
+ # Currently, only supports :bld_type_custom
128
+ @buildcustom = match[0].split '.'
129
+ @buildtype = :bld_type_custom
130
+
131
+ # Done parsing, clear the array if it's not a custom type
132
+ @buildcustom = [] unless @buildtype == :bld_type_custom
133
+ convert_to_integer(@buildcustom)
134
+ else # if !match
135
+ # It may be an empty string, so set the buildtype to final in that case
136
+ if str.empty?
137
+ match = nil
138
+ @buildtype = :bld_type_final
139
+ else
140
+ raise Errors::ParseError, "unrecognized build '#{str}'"
141
+ end
142
+ end
143
+
144
+ return match
145
+ end
146
+
147
+ # With the exception of the root parse function
148
+ public
149
+
150
+ def parse(ver)
151
+ unless ver.kind_of? String
152
+ raise Errors::ParameterError, "expected a string to be parsed"
153
+ end
154
+
155
+ # Chop off any trailing newlines
156
+ ver.chomp!
157
+
158
+ # Match the name
159
+ match = NAME_REGEX.match(ver)
160
+ if match
161
+ @name = match[:name]
162
+ ver = ver.sub(NAME_REGEX, '')
163
+ #else
164
+ # Sometimes we may not get a name to be parsed. If that's the case
165
+ # then simply ignore it.
166
+ end
167
+
168
+ # Match the major, minor and patch versions
169
+ match = VER_REGEX.match(ver)
170
+ if match
171
+ @major = match[:major].to_i
172
+ @minor = match[:minor].to_i
173
+ @patch = match[:patch].to_i
174
+ ver = ver.sub(VER_REGEX, '')
175
+ else
176
+ raise Errors::ParseError, "unrecognized version format '#{ver}'"
177
+ end
178
+
179
+ # See if we have a prerelease version (begins with a -)
180
+ if ver =~ /^-/
181
+ ver = ver.sub(/^-/, '')
182
+
183
+ match = parse_release(ver)
184
+ if match
185
+ # Delete the matched data
186
+ ver = ver.sub(SPECIAL_REGEX, '')
187
+ end
188
+ else # if ver !~ /^-/
189
+ @reltype = :rel_type_final
190
+ end
191
+
192
+ # See if we have a build version (begins with a +)
193
+ if ver =~ /^\+/
194
+ ver = ver.sub(/^\+/, '')
195
+
196
+ match = parse_build(ver)
197
+ if match
198
+ # Delete the matched data
199
+ ver = ver.sub(SPECIAL_REGEX, '')
200
+ end
201
+ else # if ver !~ /^\+/
202
+ @buildtype = :bld_type_final
203
+ end
204
+
205
+ # By now, ver should be empty. Raise an error if this is not the case
206
+ unless ver.empty?
207
+ raise Errors::ParseError, "unrecognized version format '#{ver}'"
208
+ end
209
+
210
+ true
211
+ end
212
+
213
+ end
214
+
215
+
216
+ end
@@ -0,0 +1,65 @@
1
+ ###############################################################################
2
+ # VMLib source editor
3
+ ###############################################################################
4
+ # Copyright (C) 2013 Nirenjan Krishnan
5
+ # All rights reserved.
6
+ ###############################################################################
7
+
8
+ ;
9
+
10
+ module VMLib
11
+
12
+ # This is the class for updating source files with the new version
13
+ class Source
14
+
15
+ # Set up the tree to find the root of the source repository
16
+ def initialize(dir = nil)
17
+ # Find the primary version file and get the root path
18
+ version = File.new.find_file(dir)
19
+ root = ::File.dirname(version)
20
+
21
+ # Find all files below the root which have the filename
22
+ # beginning with 'version'
23
+ @files = ::Dir.glob("#{root}/**/version*")
24
+ @files.delete_if {|f| ! ::File.file? f }
25
+
26
+ # Read the primary version file and get the version string from it
27
+ verdata = ::File.read(version)
28
+ v = Version.new
29
+ v.parse verdata
30
+ @verstring = v.format '"%M.%m.%p%r%b"'
31
+ end
32
+
33
+ # Update the specified file
34
+ def update_file (f)
35
+ fdata = ::File.read(f).split("\n")
36
+
37
+ for i in (0...fdata.length)
38
+ # Check for a version string
39
+ # VERSION ... "<version string>"
40
+ line = fdata[i]
41
+
42
+ if (line =~ /VERSION.*"\d+\.\d+\.\d+[^"]*"/)
43
+ puts "updating line #{i} in #{f}..."
44
+ puts " old: #{line}"
45
+ fdata[i] = line.gsub(/"\d+\.\d+\.\d+[^"]*"/, @verstring)
46
+ puts " new: #{fdata[i]}"
47
+ end
48
+ end
49
+
50
+ # For whatever reason, the split deletes the last entry and leaves
51
+ # us without a newline at the end of the file. Make sure that we
52
+ # do insert a newline at the end of the file.
53
+ ::File.write(f, fdata.join("\n") + "\n")
54
+ end
55
+ private :update_file
56
+
57
+ # Update all the source files containing the version
58
+ def update
59
+ @files.each {|f| update_file(f) }
60
+ end
61
+
62
+ end
63
+
64
+
65
+ end
@@ -0,0 +1,12 @@
1
+ ###############################################################################
2
+ # VMLib version information
3
+ ###############################################################################
4
+ # Copyright (C) 2013 Nirenjan Krishnan
5
+ # All rights reserved.
6
+ ###############################################################################
7
+
8
+ module VMLib
9
+
10
+ VERSION = "1.0.0" #:nodoc:
11
+
12
+ end
data/lib/vmlib.rb ADDED
@@ -0,0 +1,23 @@
1
+ ###############################################################################
2
+ # VMLib library file
3
+ ###############################################################################
4
+ # Copyright (C) 2013 Nirenjan Krishnan
5
+ # All rights reserved.
6
+ ###############################################################################
7
+
8
+ # Version processing
9
+ require 'vmlib/base'
10
+ require 'vmlib/bump'
11
+ require 'vmlib/errors'
12
+ require 'vmlib/compare'
13
+ require 'vmlib/parse'
14
+ require 'vmlib/format'
15
+
16
+ # File processing
17
+ require 'vmlib/file'
18
+
19
+ # Gem version
20
+ require 'vmlib/version'
21
+
22
+ # Source file processing
23
+ require 'vmlib/source'
@@ -0,0 +1,113 @@
1
+ ###############################################################################
2
+ # VMLib basic version test cases
3
+ ###############################################################################
4
+ # Copyright (C) 2013 Nirenjan Krishnan
5
+ # All rights reserved.
6
+ ###############################################################################
7
+
8
+ require 'test/unit'
9
+ require 'vmlib'
10
+
11
+ module VMLib
12
+
13
+ module Tests #:nodoc:
14
+
15
+ class TestBasicVersion < ::Test::Unit::TestCase #:nodoc:
16
+
17
+ # Test the default value when a new version is created
18
+ def test_default_value
19
+ version = VMLib::Version.new
20
+
21
+ assert_equal '0.0.0-0', version.to_s
22
+ end
23
+
24
+
25
+ # Test the version with a different version number
26
+ def test_custom_version
27
+ version = VMLib::Version.new('SomeProject', 1, 1, 0, 'a.1', 'b.20')
28
+ assert_equal 'SomeProject 1.1.0-a.1+b.20', version.to_s
29
+ end
30
+
31
+
32
+ # Test setting the version record fields individually
33
+ def test_version_fields
34
+ version = VMLib::Version.new
35
+ assert_equal '0.0.0-0', version.to_s
36
+
37
+ version.major = 1
38
+ assert_equal '1.0.0-0', version.to_s
39
+
40
+ version.minor = 2
41
+ assert_equal '1.2.0-0', version.to_s
42
+
43
+ version.patch = 3
44
+ assert_equal '1.2.3-0', version.to_s
45
+
46
+ version.name = 'FooBar'
47
+ assert_equal 'FooBar 1.2.3-0', version.to_s
48
+
49
+ version.prerelease = ''
50
+ assert_equal 'FooBar 1.2.3', version.to_s
51
+
52
+ version.build = 'build.20'
53
+ assert_equal 'FooBar 1.2.3+build.20', version.to_s
54
+
55
+ version.prerelease = 'alpha.1'
56
+ assert_equal 'FooBar 1.2.3-a.1+build.20', version.to_s
57
+
58
+ end
59
+
60
+
61
+ # Test the prerelease parser and formatter
62
+ def test_prerelease
63
+ version = VMLib::Version.new
64
+ version.prerelease = '1'
65
+ assert_equal '0.0.0-1', version.to_s
66
+
67
+ version.prerelease = 'a.2'
68
+ assert_equal '0.0.0-a.2', version.to_s
69
+
70
+ # Test that the string alpha gets interpreted as 'a'
71
+ version.prerelease = 'alpha.3'
72
+ assert_equal '0.0.0-a.3', version.to_s
73
+
74
+ # Test that a non-standard alpha gets treated as a custom field
75
+ version.prerelease = 'alpha.1.2'
76
+ assert_equal '0.0.0-alpha.1.2', version.to_s
77
+
78
+ version.prerelease = 'alpha.a1'
79
+ assert_equal '0.0.0-alpha.a1', version.to_s
80
+
81
+ version.prerelease = 'b.4'
82
+ assert_equal '0.0.0-b.4', version.to_s
83
+
84
+ # Test that the string beta gets interpreted as 'b'
85
+ version.prerelease = 'beta.3'
86
+ assert_equal '0.0.0-b.3', version.to_s
87
+
88
+ # Test that a non-standard beta gets treated as a custom field
89
+ version.prerelease = 'beta.a2'
90
+ assert_equal '0.0.0-beta.a2', version.to_s
91
+
92
+ version.prerelease = 'beta.1.2'
93
+ assert_equal '0.0.0-beta.1.2', version.to_s
94
+
95
+ version.prerelease = 'rc.1'
96
+ assert_equal '0.0.0-rc.1', version.to_s
97
+
98
+ # Test that an unrecognized format still gets stored
99
+ version.prerelease = 'foo.bar.baz'
100
+ assert_equal '0.0.0-foo.bar.baz', version.to_s
101
+
102
+ # Verify that an empty pre-release gets treated as a final release
103
+ version.prerelease = ''
104
+ assert_equal '0.0.0', version.to_s
105
+ end
106
+
107
+ end
108
+
109
+
110
+ end
111
+
112
+
113
+ end