versionomy 0.0.1
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/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
|