versionomy 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/Manifest.txt +16 -0
- data/README.txt +134 -0
- data/Rakefile +49 -0
- data/lib/versionomy/errors.rb +107 -0
- data/lib/versionomy/format.rb +132 -0
- data/lib/versionomy/schema.rb +537 -0
- data/lib/versionomy/standard.rb +385 -0
- data/lib/versionomy/value.rb +291 -0
- data/lib/versionomy/version.rb +49 -0
- data/lib/versionomy.rb +46 -0
- data/tests/tc_standard_basic.rb +146 -0
- data/tests/tc_standard_bump.rb +170 -0
- data/tests/tc_standard_change.rb +97 -0
- data/tests/tc_standard_comparison.rb +77 -0
- data/tests/tc_standard_parse.rb +162 -0
- metadata +95 -0
@@ -0,0 +1,385 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Versionomy standard schema and formats
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2008 Daniel Azuma
|
7
|
+
#
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# Redistribution and use in source and binary forms, with or without
|
11
|
+
# modification, are permitted provided that the following conditions are met:
|
12
|
+
#
|
13
|
+
# * Redistributions of source code must retain the above copyright notice,
|
14
|
+
# this list of conditions and the following disclaimer.
|
15
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
16
|
+
# this list of conditions and the following disclaimer in the documentation
|
17
|
+
# and/or other materials provided with the distribution.
|
18
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
19
|
+
# contributors to this software, may be used to endorse or promote products
|
20
|
+
# derived from this software without specific prior written permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
# -----------------------------------------------------------------------------
|
34
|
+
;
|
35
|
+
|
36
|
+
|
37
|
+
module Versionomy
|
38
|
+
|
39
|
+
|
40
|
+
# === The standard schema
|
41
|
+
#
|
42
|
+
# The standard schema is designed to handle most commonly-used version
|
43
|
+
# number forms, and allow parsing and comparison between them.
|
44
|
+
#
|
45
|
+
# It begins with four numeric fields: "major.minor.tiny.tiny2".
|
46
|
+
#
|
47
|
+
# The next field, "release_type", defines the remaining structure.
|
48
|
+
# The release type can be one of these symbolic values: <tt>:prerelease</tt>,
|
49
|
+
# <tt>:development</tt>, <tt>:alpha</tt>, <tt>:beta</tt>,
|
50
|
+
# <tt>:release_candidate</tt>, or <tt>:release</tt>.
|
51
|
+
#
|
52
|
+
# Depending on that value, additional fields become available. For example,
|
53
|
+
# the <tt>:alpha</tt> value enables the fields "alpha_version"
|
54
|
+
# and "alpha_minor", which represent version numbers after the "a" alpha
|
55
|
+
# specifier. i.e. "2.1a30" has an alpha_version of 30. "2.1a30.2" also
|
56
|
+
# has an alpha_minor of 2. Similarly, the <tt>:beta</tt> release_type
|
57
|
+
# value enables the fields "beta_version" and "beta_minor". A release_type
|
58
|
+
# of <tt>:release</tt> enables "patchlevel" and "patchlevel_minor", to
|
59
|
+
# support versions like "1.8.7p72".
|
60
|
+
#
|
61
|
+
# The full definition of the standard schema is as follows:
|
62
|
+
#
|
63
|
+
# Schema.new(:major, :initial => 1) do
|
64
|
+
# schema(:minor) do
|
65
|
+
# schema(:tiny) do
|
66
|
+
# schema(:tiny2) do
|
67
|
+
# schema(:release_type, :type => :symbol) do
|
68
|
+
# symbol(:prerelease, :bump => :release)
|
69
|
+
# symbol(:development, :bump => :alpha)
|
70
|
+
# symbol(:alpha, :bump => :beta)
|
71
|
+
# symbol(:beta, :bump => :release_candidate)
|
72
|
+
# symbol(:release_candidate, :bump => :release)
|
73
|
+
# symbol(:release, :bump => :release)
|
74
|
+
# initial_value(:release)
|
75
|
+
# schema(:prerelease_version, :only => :prerelease, :initial => 1) do
|
76
|
+
# schema(:prerelease_minor)
|
77
|
+
# end
|
78
|
+
# schema(:development_version, :only => :development, :initial => 1) do
|
79
|
+
# schema(:development_minor)
|
80
|
+
# end
|
81
|
+
# schema(:alpha_version, :only => :alpha, :initial => 1) do
|
82
|
+
# schema(:alpha_minor)
|
83
|
+
# end
|
84
|
+
# schema(:beta_version, :only => :beta, :initial => 1) do
|
85
|
+
# schema(:beta_minor)
|
86
|
+
# end
|
87
|
+
# schema(:release_candidate_version, :only => :release_candidate, :initial => 1) do
|
88
|
+
# schema(:release_candidate_minor)
|
89
|
+
# end
|
90
|
+
# schema(:patchlevel, :only => :release) do
|
91
|
+
# schema(:patchlevel_minor)
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
|
99
|
+
module Standard
|
100
|
+
|
101
|
+
|
102
|
+
# A formatter for the standard schema
|
103
|
+
|
104
|
+
class StandardFormat
|
105
|
+
|
106
|
+
|
107
|
+
# Create a new formatter
|
108
|
+
|
109
|
+
def initialize(opts_={})
|
110
|
+
@patchlevel_separator = opts_[:patchlevel_separators] || ['-', 'p']
|
111
|
+
@prerelease_symbol = opts_[:prerelease_symbols] || 'pre'
|
112
|
+
@development_symbol = opts_[:development_symbols] || 'd'
|
113
|
+
@alpha_symbol = opts_[:alpha_symbols] || 'a'
|
114
|
+
@beta_symbol = opts_[:beta_symbols] || 'b'
|
115
|
+
@release_candidate_symbol = opts_[:release_candidate_symbols] || 'rc'
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
def _create_regex(given_, default_) # :nodoc:
|
120
|
+
if given_
|
121
|
+
if given_.respond_to?(:join)
|
122
|
+
given_.join('|')
|
123
|
+
else
|
124
|
+
given_.to_s
|
125
|
+
end
|
126
|
+
else
|
127
|
+
if default_.respond_to?(:join)
|
128
|
+
default_.join('|')
|
129
|
+
else
|
130
|
+
default_.to_s
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
private :_create_regex
|
135
|
+
|
136
|
+
def _create_separator(given_, default_) # :nodoc:
|
137
|
+
if given_
|
138
|
+
given_.to_s
|
139
|
+
else
|
140
|
+
if default_.respond_to?(:join)
|
141
|
+
default_[0].to_s
|
142
|
+
else
|
143
|
+
default_.to_s
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
private :_create_separator
|
148
|
+
|
149
|
+
|
150
|
+
# Parse a string for the standard schema.
|
151
|
+
|
152
|
+
def parse(schema_, str_, params_)
|
153
|
+
params_ = Hash.new.merge(params_)
|
154
|
+
hash_ = Hash.new
|
155
|
+
if str_ =~ /^(\d+)(.*)$/
|
156
|
+
hash_[:major] = $1.to_i
|
157
|
+
str_ = $2
|
158
|
+
else
|
159
|
+
hash_[:major] = 0
|
160
|
+
end
|
161
|
+
if str_ =~ /^\.(\d+)(.*)$/
|
162
|
+
hash_[:minor] = $1.to_i
|
163
|
+
str_ = $2
|
164
|
+
if str_ =~ /^\.(\d+)(.*)$/
|
165
|
+
hash_[:tiny] = $1.to_i
|
166
|
+
str_ = $2
|
167
|
+
if str_ =~ /^\.(\d+)(.*)$/
|
168
|
+
hash_[:tiny2] = $1.to_i
|
169
|
+
str_ = $2
|
170
|
+
params_[:required_fields] = 4
|
171
|
+
else
|
172
|
+
hash_[:tiny2] = 0
|
173
|
+
params_[:required_fields] = 3
|
174
|
+
end
|
175
|
+
else
|
176
|
+
hash_[:tiny] = 0
|
177
|
+
params_[:required_fields] = 2
|
178
|
+
end
|
179
|
+
else
|
180
|
+
hash_[:minor] = 0
|
181
|
+
params_[:required_fields] = 1
|
182
|
+
end
|
183
|
+
if str_ =~ /^(#{_create_regex(params_[:prerelease_symbol], @prerelease_symbol)})(\d+)(.*)$/
|
184
|
+
params_[:prerelease_symbol] = $1
|
185
|
+
hash_[:release_type] = :prerelease
|
186
|
+
hash_[:prerelease_version] = $2.to_i
|
187
|
+
str_ = $3
|
188
|
+
if str_ =~ /^\.(\d+)/
|
189
|
+
hash_[:prerelease_minor] = $1.to_i
|
190
|
+
params_[:prerelease_required_fields] = 2
|
191
|
+
else
|
192
|
+
params_[:prerelease_required_fields] = 1
|
193
|
+
end
|
194
|
+
elsif str_ =~ /^(#{_create_regex(params_[:development_symbol], @development_symbol)})(\d+)(.*)$/
|
195
|
+
params_[:development_symbol] = $1
|
196
|
+
hash_[:release_type] = :development
|
197
|
+
hash_[:development_version] = $2.to_i
|
198
|
+
str_ = $3
|
199
|
+
if str_ =~ /^\.(\d+)/
|
200
|
+
hash_[:development_minor] = $1.to_i
|
201
|
+
params_[:development_required_fields] = 2
|
202
|
+
else
|
203
|
+
params_[:development_required_fields] = 1
|
204
|
+
end
|
205
|
+
elsif str_ =~ /^(#{_create_regex(params_[:alpha_symbol], @alpha_symbol)})(\d+)(.*)$/
|
206
|
+
params_[:alpha_symbol] = $1
|
207
|
+
hash_[:release_type] = :alpha
|
208
|
+
hash_[:alpha_version] = $2.to_i
|
209
|
+
str_ = $3
|
210
|
+
if str_ =~ /^\.(\d+)/
|
211
|
+
hash_[:alpha_minor] = $1.to_i
|
212
|
+
params_[:alpha_required_fields] = 2
|
213
|
+
else
|
214
|
+
params_[:alpha_required_fields] = 1
|
215
|
+
end
|
216
|
+
elsif str_ =~ /^(#{_create_regex(params_[:beta_symbol], @beta_symbol)})(\d+)(.*)$/
|
217
|
+
params_[:beta_symbol] = $1
|
218
|
+
hash_[:release_type] = :beta
|
219
|
+
hash_[:beta_version] = $2.to_i
|
220
|
+
str_ = $3
|
221
|
+
if str_ =~ /^\.(\d+)/
|
222
|
+
hash_[:beta_minor] = $1.to_i
|
223
|
+
params_[:beta_required_fields] = 2
|
224
|
+
else
|
225
|
+
params_[:beta_required_fields] = 1
|
226
|
+
end
|
227
|
+
elsif str_ =~ /^(#{_create_regex(params_[:release_candidate_symbol], @release_candidate_symbol)})(\d+)(.*)$/
|
228
|
+
params_[:release_candidate_symbol] = $1
|
229
|
+
hash_[:release_candidate_version] = $2.to_i
|
230
|
+
hash_[:release_type] = :release_candidate
|
231
|
+
str_ = $3
|
232
|
+
if str_ =~ /^\.(\d+)/
|
233
|
+
hash_[:release_candidate_minor] = $1.to_i
|
234
|
+
params_[:release_candidate_required_fields] = 2
|
235
|
+
else
|
236
|
+
params_[:release_candidate_required_fields] = 1
|
237
|
+
end
|
238
|
+
else
|
239
|
+
hash_[:release_type] = :release
|
240
|
+
if str_ =~ /^(#{_create_regex(params_[:patchlevel_separator], @patchlevel_separator)})(\d+)(.*)$/
|
241
|
+
params_[:patchlevel_separator] = $1
|
242
|
+
params_[:patchlevel_format] = :digit
|
243
|
+
hash_[:patchlevel] = $2.to_i
|
244
|
+
str_ = $3
|
245
|
+
if str_ =~ /^\.(\d+)/
|
246
|
+
hash_[:patchlevel_minor] = $1.to_i
|
247
|
+
params_[:patchlevel_required_fields] = 2
|
248
|
+
else
|
249
|
+
params_[:patchlevel_required_fields] = 1
|
250
|
+
end
|
251
|
+
elsif str_ =~ /^([a-z])/
|
252
|
+
char_ = $1
|
253
|
+
params_[:patchlevel_format] = :alpha_lower
|
254
|
+
params_[:patchlevel_required_fields] = 1
|
255
|
+
hash[:patchlevel] = (char_.bytes.next rescue char_[0]) - 96
|
256
|
+
elsif str_ =~ /^([A-Z])/
|
257
|
+
char_ = $1
|
258
|
+
params_[:patchlevel_format] = :alpha_upper
|
259
|
+
params_[:patchlevel_required_fields] = 1
|
260
|
+
hash[:patchlevel] = (char_.bytes.next rescue char_[0]) - 64
|
261
|
+
end
|
262
|
+
end
|
263
|
+
Versionomy::Value._new(schema_, hash_, params_)
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
# Unparse a value for the standard schema.
|
268
|
+
|
269
|
+
def unparse(schema_, value_, params_)
|
270
|
+
params_ = value_.parse_params.merge(params_)
|
271
|
+
str_ = "#{value_.major}.#{value_.minor}.#{value_.tiny}.#{value_.tiny2}"
|
272
|
+
(4 - (params_[:required_fields] || 2)).times{ str_.sub!(/\.0$/, '') }
|
273
|
+
case value_.release_type
|
274
|
+
when :prerelease
|
275
|
+
prerelease_required_fields_ = params_[:prerelease_required_fields] || 1
|
276
|
+
str_ << _create_separator(params_[:prerelease_symbol], @prerelease_symbol)
|
277
|
+
str_ << value_.prerelease_version.to_s
|
278
|
+
if value_.prerelease_minor > 0 || prerelease_required_fields_ > 1
|
279
|
+
str_ << ".#{value_.prerelease_minor}"
|
280
|
+
end
|
281
|
+
when :development
|
282
|
+
development_required_fields_ = params_[:development_required_fields] || 1
|
283
|
+
str_ << _create_separator(params_[:development_symbol], @development_symbol)
|
284
|
+
str_ << value_.development_version.to_s
|
285
|
+
if value_.development_minor > 0 || development_required_fields_ > 1
|
286
|
+
str_ << ".#{value_.development_minor}"
|
287
|
+
end
|
288
|
+
when :alpha
|
289
|
+
alpha_required_fields_ = params_[:alpha_required_fields] || 1
|
290
|
+
str_ << _create_separator(params_[:alpha_symbol], @alpha_symbol)
|
291
|
+
str_ << value_.alpha_version.to_s
|
292
|
+
if value_.alpha_minor > 0 || alpha_required_fields_ > 1
|
293
|
+
str_ << ".#{value_.alpha_minor}"
|
294
|
+
end
|
295
|
+
when :beta
|
296
|
+
beta_required_fields_ = params_[:beta_required_fields] || 1
|
297
|
+
str_ << _create_separator(params_[:beta_symbol], @beta_symbol)
|
298
|
+
str_ << value_.beta_version.to_s
|
299
|
+
if value_.beta_minor > 0 || beta_required_fields_ > 1
|
300
|
+
str_ << ".#{value_.beta_minor}"
|
301
|
+
end
|
302
|
+
when :release_candidate
|
303
|
+
release_candidate_required_fields_ = params_[:release_candidate_required_fields] || 1
|
304
|
+
str_ << _create_separator(params_[:release_candidate_symbol], @release_candidate_symbol)
|
305
|
+
str_ << value_.release_candidate_version.to_s
|
306
|
+
if value_.release_candidate_minor > 0 || release_candidate_required_fields_ > 1
|
307
|
+
str_ << ".#{value_.release_candidate_minor}"
|
308
|
+
end
|
309
|
+
else
|
310
|
+
patchlevel_required_fields_ = params_[:patchlevel_required_fields] || 0
|
311
|
+
if value_.patchlevel > 0 || patchlevel_required_fields_ > 0
|
312
|
+
if params_[:patchlevel_format] == :alpha_lower
|
313
|
+
str_.concat(96 + value_.patchlevel)
|
314
|
+
elsif params_[:patchlevel_format] == :alpha_upper
|
315
|
+
str_.concat(64 + value_.patchlevel)
|
316
|
+
else
|
317
|
+
str_ << _create_separator(params_[:patchlevel_separator], @patchlevel_separator)
|
318
|
+
str_ << value_.patchlevel.to_s
|
319
|
+
if value_.patchlevel_minor > 0 || patchlevel_required_fields_ > 1
|
320
|
+
str_ << ".#{value_.patchlevel_minor}"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
str_
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
|
331
|
+
# Get the standard schema
|
332
|
+
|
333
|
+
def self.schema
|
334
|
+
@standard_schema ||= SchemaCreator._create_schema
|
335
|
+
end
|
336
|
+
|
337
|
+
|
338
|
+
module SchemaCreator # :nodoc:
|
339
|
+
|
340
|
+
def self._create_schema
|
341
|
+
Schema.new(:major, :initial => 1) do
|
342
|
+
schema(:minor) do
|
343
|
+
schema(:tiny) do
|
344
|
+
schema(:tiny2) do
|
345
|
+
schema(:release_type, :type => :symbol) do
|
346
|
+
symbol(:prerelease, :bump => :release)
|
347
|
+
symbol(:development, :bump => :alpha)
|
348
|
+
symbol(:alpha, :bump => :beta)
|
349
|
+
symbol(:beta, :bump => :release_candidate)
|
350
|
+
symbol(:release_candidate, :bump => :release)
|
351
|
+
symbol(:release, :bump => :release)
|
352
|
+
initial_value(:release)
|
353
|
+
schema(:prerelease_version, :only => :prerelease, :initial => 1) do
|
354
|
+
schema(:prerelease_minor)
|
355
|
+
end
|
356
|
+
schema(:development_version, :only => :development, :initial => 1) do
|
357
|
+
schema(:development_minor)
|
358
|
+
end
|
359
|
+
schema(:alpha_version, :only => :alpha, :initial => 1) do
|
360
|
+
schema(:alpha_minor)
|
361
|
+
end
|
362
|
+
schema(:beta_version, :only => :beta, :initial => 1) do
|
363
|
+
schema(:beta_minor)
|
364
|
+
end
|
365
|
+
schema(:release_candidate_version, :only => :release_candidate, :initial => 1) do
|
366
|
+
schema(:release_candidate_minor)
|
367
|
+
end
|
368
|
+
schema(:patchlevel, :only => :release) do
|
369
|
+
schema(:patchlevel_minor)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
define_format(:default, StandardFormat.new)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
|
380
|
+
end
|
381
|
+
|
382
|
+
end
|
383
|
+
|
384
|
+
|
385
|
+
end
|
@@ -0,0 +1,291 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Versionomy value
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2008 Daniel Azuma
|
7
|
+
#
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# Redistribution and use in source and binary forms, with or without
|
11
|
+
# modification, are permitted provided that the following conditions are met:
|
12
|
+
#
|
13
|
+
# * Redistributions of source code must retain the above copyright notice,
|
14
|
+
# this list of conditions and the following disclaimer.
|
15
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
16
|
+
# this list of conditions and the following disclaimer in the documentation
|
17
|
+
# and/or other materials provided with the distribution.
|
18
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
19
|
+
# contributors to this software, may be used to endorse or promote products
|
20
|
+
# derived from this software without specific prior written permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
# -----------------------------------------------------------------------------
|
34
|
+
;
|
35
|
+
|
36
|
+
|
37
|
+
module Versionomy
|
38
|
+
|
39
|
+
|
40
|
+
# === Version number value
|
41
|
+
#
|
42
|
+
# A version number value is an ordered list of values, corresponding to an
|
43
|
+
# ordered list of fields defined by a schema. For example, if the schema
|
44
|
+
# is a simple one of the form "major.minor.tiny", then the the version
|
45
|
+
# number "1.4.2" would have the values <tt>[1, 4, 2]</tt> in that order,
|
46
|
+
# corresponding to the fields <tt>[:major, :minor, :tiny]</tt>.
|
47
|
+
#
|
48
|
+
# Version number values are comparable with other values that have the same
|
49
|
+
# form.
|
50
|
+
|
51
|
+
class Value
|
52
|
+
|
53
|
+
|
54
|
+
def initialize(schema_, values_=nil, parse_params_=nil, subvalue_=nil) # :nodoc:
|
55
|
+
@schema = schema_
|
56
|
+
val_ = nil
|
57
|
+
case values_
|
58
|
+
when Hash
|
59
|
+
val_ = values_[@schema.name]
|
60
|
+
when Array
|
61
|
+
val_ = values_.first
|
62
|
+
values_ = values_[1..-1]
|
63
|
+
else
|
64
|
+
val_ = values_
|
65
|
+
values_ = nil
|
66
|
+
end
|
67
|
+
@value = val_ ? @schema.canonicalize_value(val_) : @schema.initial_value
|
68
|
+
@parse_params = parse_params_ || Hash.new
|
69
|
+
if subvalue_
|
70
|
+
@subvalue = subvalue_
|
71
|
+
else
|
72
|
+
subschema_ = @schema._subschema(@value)
|
73
|
+
@subvalue = subschema_ ? Versionomy::Value._new(subschema_, values_) : nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
# Returns a string representation generated by unparsing.
|
79
|
+
# If unparsing fails, does not raise Versionomy::Errors::ParseError,
|
80
|
+
# but instead returns the string generated by +inspect+.
|
81
|
+
#
|
82
|
+
|
83
|
+
def to_s
|
84
|
+
begin
|
85
|
+
unparse
|
86
|
+
rescue Versionomy::Errors::ParseError
|
87
|
+
inspect
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# Unparse this version number.
|
93
|
+
#
|
94
|
+
# Raises Versionomy::Errors::ParseError if unparsing failed.
|
95
|
+
|
96
|
+
def unparse(params_={})
|
97
|
+
format_ = @schema.get_format(params_[:format])
|
98
|
+
if format_.nil?
|
99
|
+
raise Versionomy::Errors::UnknownFormatError
|
100
|
+
end
|
101
|
+
format_.unparse(@schema, self, params_)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# Return the schema defining the form of this version number
|
106
|
+
|
107
|
+
def schema
|
108
|
+
@schema
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# Return the parsing parameters for this value.
|
113
|
+
|
114
|
+
def parse_params
|
115
|
+
@parse_params
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
# Returns an array of recognized field names for this value, in field order.
|
120
|
+
def fields
|
121
|
+
@subvalue ? @subvalue.fields.unshift(@schema.name) : [@schema.name]
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# Returns true if this value contains the given field.
|
126
|
+
|
127
|
+
def has_field?(symbol_)
|
128
|
+
symbol_ = symbol_.to_sym
|
129
|
+
if symbol_ == @schema.name
|
130
|
+
true
|
131
|
+
elsif @subvalue
|
132
|
+
@subvalue.has_field?(symbol_)
|
133
|
+
else
|
134
|
+
false
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
# Returns the value of the given field, or nil if the field is not recognized.
|
140
|
+
|
141
|
+
def [](symbol_)
|
142
|
+
symbol_ = symbol_ ? symbol_.to_sym : @schema.name
|
143
|
+
if symbol_ == @schema.name
|
144
|
+
@value
|
145
|
+
elsif @subvalue
|
146
|
+
@subvalue[symbol_]
|
147
|
+
else
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
# Returns the value as an array of field values, in field order.
|
154
|
+
|
155
|
+
def value_array
|
156
|
+
@subvalue ? @subvalue.value_array.unshift(@value) : [@value]
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
# Returns the value as a hash of values keyed by field name.
|
161
|
+
|
162
|
+
def value_hash
|
163
|
+
hash_ = {@schema.name => @value}
|
164
|
+
@subvalue ? @subvalue.value_hash.merge(hash_) : hash_
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
# Returns a new version number created by bumping the given field.
|
169
|
+
|
170
|
+
def bump(symbol_)
|
171
|
+
if (symbol_ == @schema.name)
|
172
|
+
bumped_ = @schema.bump_value(@value)
|
173
|
+
if bumped_ == @value
|
174
|
+
self
|
175
|
+
else
|
176
|
+
Versionomy::Value._new(@schema, bumped_, @parse_params)
|
177
|
+
end
|
178
|
+
else
|
179
|
+
if @subvalue
|
180
|
+
bumped_ = @subvalue.bump(symbol_)
|
181
|
+
if @subvalue.equal?(bumped_)
|
182
|
+
self
|
183
|
+
else
|
184
|
+
Versionomy::Value._new(@schema, @value, @parse_params, bumped_)
|
185
|
+
end
|
186
|
+
else
|
187
|
+
self
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
# Returns a new version number created by changing the given field values.
|
194
|
+
|
195
|
+
def change(values_={})
|
196
|
+
Versionomy::Value._new(@schema, value_hash.merge(values_), @parse_params)
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
# Returns true if this version number is equal to the given verison number.
|
201
|
+
# Equality means the values and field names are the same, although the
|
202
|
+
# schemas may actually be different.
|
203
|
+
|
204
|
+
def eql?(obj_)
|
205
|
+
return false unless obj_.kind_of?(Versionomy::Value)
|
206
|
+
return false if @schema.name != obj_.schema.name || @value != obj_.toplevel_value
|
207
|
+
@subvalue.eql?(obj_.subvalue)
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
# Returns true if this version number is equal to the given verison number.
|
212
|
+
# Equality means the values and field names are the same, even if the schemas
|
213
|
+
# are different.
|
214
|
+
|
215
|
+
def ==(obj_)
|
216
|
+
eql?(obj_)
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
# Compare this version number with the given version number.
|
221
|
+
# Version numbers with the same field names and types are comparable,
|
222
|
+
# even if the schemas are different.
|
223
|
+
|
224
|
+
def <=>(obj_)
|
225
|
+
return nil unless obj_.kind_of?(Versionomy::Value)
|
226
|
+
return nil if @schema.name != obj_.schema.name
|
227
|
+
val_ = @schema.compare_values(@value, obj_.toplevel_value)
|
228
|
+
if val_ == 0
|
229
|
+
if @subvalue.nil?
|
230
|
+
obj_.subvalue.nil? ? 0 : nil
|
231
|
+
else
|
232
|
+
@subvalue <=> obj_.subvalue
|
233
|
+
end
|
234
|
+
else
|
235
|
+
val_
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
# Compare this version number with the given version number.
|
241
|
+
# Version numbers with the same field names and types are comparable,
|
242
|
+
# even if the schemas are different.
|
243
|
+
|
244
|
+
def <(obj_)
|
245
|
+
(self <=> obj_) < 0
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
# Compare this version number with the given version number.
|
250
|
+
# Version numbers with the same field names and types are comparable,
|
251
|
+
# even if the schemas are different.
|
252
|
+
|
253
|
+
def >(obj_)
|
254
|
+
(self <=> obj_) > 0
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
# Get the value of the most significant field
|
259
|
+
|
260
|
+
def toplevel_value
|
261
|
+
@value
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
# Get a value representing all fields except the most significant field
|
266
|
+
|
267
|
+
def subvalue
|
268
|
+
@subvalue
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
# Field values may be retrieved by calling them as methods.
|
273
|
+
|
274
|
+
def method_missing(symbol_)
|
275
|
+
self[symbol_] || super
|
276
|
+
end
|
277
|
+
|
278
|
+
|
279
|
+
class << self
|
280
|
+
|
281
|
+
# :stopdoc:
|
282
|
+
alias_method :_new, :new
|
283
|
+
private :new
|
284
|
+
# :startdoc:
|
285
|
+
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
|
291
|
+
end
|