versionomy 0.1.3 → 0.2.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/History.rdoc +15 -1
- data/README.rdoc +73 -22
- data/Rakefile +1 -1
- data/lib/versionomy/{formats.rb → conversion/base.rb} +36 -37
- data/lib/versionomy/conversion/parsing.rb +173 -0
- data/lib/versionomy/conversion/rubygems.rb +138 -0
- data/lib/versionomy/conversion.rb +145 -0
- data/lib/versionomy/errors.rb +22 -2
- data/lib/versionomy/format/base.rb +53 -5
- data/lib/versionomy/format/delimiter.rb +39 -27
- data/lib/versionomy/format/rubygems.rb +234 -0
- data/lib/versionomy/format/standard.rb +382 -0
- data/lib/versionomy/format.rb +73 -4
- data/lib/versionomy/interface.rb +22 -14
- data/lib/versionomy/schema/field.rb +14 -5
- data/lib/versionomy/schema/wrapper.rb +84 -10
- data/lib/versionomy/schema.rb +9 -6
- data/lib/versionomy/value.rb +191 -53
- data/lib/versionomy/version.rb +1 -1
- data/lib/versionomy.rb +6 -2
- data/tests/tc_custom_format.rb +4 -4
- data/tests/tc_readme_examples.rb +9 -9
- data/tests/tc_rubygems_basic.rb +210 -0
- data/tests/tc_rubygems_conversions.rb +178 -0
- data/tests/tc_standard_basic.rb +10 -10
- data/tests/tc_standard_bump.rb +10 -10
- data/tests/tc_standard_change.rb +5 -5
- data/tests/tc_standard_comparison.rb +29 -16
- data/tests/tc_standard_misc.rb +95 -0
- data/tests/tc_standard_parse.rb +43 -23
- metadata +15 -5
- data/lib/versionomy/formats/standard.rb +0 -346
@@ -0,0 +1,145 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Versionomy conversion interface and registry
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2008-2009 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
|
+
# === Conversion between version schemas.
|
41
|
+
#
|
42
|
+
# Conversions are algorithms for converting from one schema to another.
|
43
|
+
# This is useful for performing conversions as well as comparing version
|
44
|
+
# numbers that use different schemas.
|
45
|
+
#
|
46
|
+
# To implement a conversion algorithm, implement the API defined by
|
47
|
+
# Versionomy::Conversion::Base. Then, register your conversion by calling
|
48
|
+
# Versionomy::Conversion#register. You will need to specify which schemas
|
49
|
+
# (from and to) that your conversion should handle. From that point on,
|
50
|
+
# whenever Versionomy needs to convert a value between those two schemas,
|
51
|
+
# it will use your conversion. You can register the same conversion object
|
52
|
+
# for multiple pairs of schemas, but you can register only one conversion
|
53
|
+
# object for any pair.
|
54
|
+
#
|
55
|
+
# A common technique for doing conversions is to unparse the version to a
|
56
|
+
# string, and then parse it in the new format. Versionomy provides a tool,
|
57
|
+
# Versionomy::Conversion::Parsing, for performing such conversions. The
|
58
|
+
# conversions between the standard and rubygems formats uses this tool.
|
59
|
+
# See Versionomy::Conversion::Rubygems for annotated examples.
|
60
|
+
|
61
|
+
module Conversion
|
62
|
+
|
63
|
+
@registry = ::Hash.new
|
64
|
+
|
65
|
+
class << self
|
66
|
+
|
67
|
+
|
68
|
+
# Convert the given value to the given format. This is identical to
|
69
|
+
# calling <tt>value_.convert(format_, convert_params_)</tt>.
|
70
|
+
#
|
71
|
+
# The format may be specified as a format object or as the name of a
|
72
|
+
# format in the Format registry.
|
73
|
+
#
|
74
|
+
# Raises Versionomy::Errors::ConversionError if the value could not
|
75
|
+
# be converted.
|
76
|
+
|
77
|
+
def convert(value_, format_, convert_params_=nil)
|
78
|
+
value_.convert(format_, convert_params_)
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# Get a conversion capable of converting between the given schemas.
|
83
|
+
#
|
84
|
+
# The schemas may be specified as format names, Format objects,
|
85
|
+
# schema wrapper objects, or the root field of the schema.
|
86
|
+
#
|
87
|
+
# If strict is set to false, returns nil if no such conversion could
|
88
|
+
# be found. If strict is set to true, may raise one of these errors:
|
89
|
+
#
|
90
|
+
# Raises Versionomy::Errors::UnknownFormatError if a format was
|
91
|
+
# specified by name but the name is not known.
|
92
|
+
#
|
93
|
+
# Raises Versionomy::Errors::UnknownConversionError if the formats
|
94
|
+
# were recognized but no conversion was found to handle them.
|
95
|
+
|
96
|
+
def get(from_schema_, to_schema_, strict_=false)
|
97
|
+
key_ = _get_key(from_schema_, to_schema_)
|
98
|
+
conversion_ = @registry[key_]
|
99
|
+
if strict_ && conversion_.nil?
|
100
|
+
raise Errors::UnknownConversionError
|
101
|
+
end
|
102
|
+
conversion_
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# Register the given conversion as the handler for the given schemas.
|
107
|
+
#
|
108
|
+
# The schemas may be specified as format names, Format objects,
|
109
|
+
# schema wrapper objects, or the root field of the schema.
|
110
|
+
#
|
111
|
+
# Raises Versionomy::Errors::ConversionRedefinedError if a conversion
|
112
|
+
# has already been registered for the given schemas.
|
113
|
+
#
|
114
|
+
# Raises Versionomy::Errors::UnknownFormatError if a format was
|
115
|
+
# specified by name but the name is not known.
|
116
|
+
|
117
|
+
def register(from_schema_, to_schema_, conversion_)
|
118
|
+
key_ = _get_key(from_schema_, to_schema_)
|
119
|
+
if @registry.include?(key_)
|
120
|
+
raise Errors::ConversionRedefinedError
|
121
|
+
end
|
122
|
+
@registry[key_] = conversion_
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def _get_key(from_schema_, to_schema_) # :nodoc:
|
129
|
+
[_get_schema(from_schema_), _get_schema(to_schema_)]
|
130
|
+
end
|
131
|
+
|
132
|
+
def _get_schema(schema_) # :nodoc:
|
133
|
+
schema_ = Format.get(schema_, true) if schema_.kind_of?(::String) || schema_.kind_of?(::Symbol)
|
134
|
+
schema_ = schema_.schema if schema_.respond_to?(:schema)
|
135
|
+
schema_ = schema_.root_field if schema_.respond_to?(:root_field)
|
136
|
+
schema_
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
end
|
data/lib/versionomy/errors.rb
CHANGED
@@ -109,7 +109,7 @@ module Versionomy
|
|
109
109
|
|
110
110
|
|
111
111
|
# This exception is raised during schema creation if you try to
|
112
|
-
# create a circular
|
112
|
+
# create a circular graph.
|
113
113
|
|
114
114
|
class CircularDescendantError < SchemaCreationError
|
115
115
|
end
|
@@ -128,13 +128,33 @@ module Versionomy
|
|
128
128
|
end
|
129
129
|
|
130
130
|
|
131
|
-
# Raised by the
|
131
|
+
# Raised by the Format registry if you try to retrieve a format with
|
132
132
|
# an unrecognized name in strict mode.
|
133
133
|
|
134
134
|
class UnknownFormatError < VersionomyError
|
135
135
|
end
|
136
136
|
|
137
137
|
|
138
|
+
# Raised when a conversion fails.
|
139
|
+
|
140
|
+
class ConversionError < VersionomyError
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
# Raised when a conversion fails because no conversion implementation
|
145
|
+
# was found.
|
146
|
+
|
147
|
+
class UnknownConversionError < ConversionError
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
# Raised when you try to register a conversion when one already
|
152
|
+
# exists for its schemas.
|
153
|
+
|
154
|
+
class ConversionRedefinedError < VersionomyError
|
155
|
+
end
|
156
|
+
|
157
|
+
|
138
158
|
end
|
139
159
|
|
140
160
|
end
|
@@ -44,10 +44,14 @@ module Versionomy
|
|
44
44
|
#
|
45
45
|
# This format doesn't actually do anything useful. It causes all strings
|
46
46
|
# to parse to the schema's default value, and unparses all values to the
|
47
|
-
# empty string.
|
47
|
+
# empty string. Instead, the purpose here is to define the API for a
|
48
|
+
# format.
|
49
|
+
#
|
50
|
+
# All formats must define the methods +schema+, +parse+, and +unparse+.
|
51
|
+
# It is also recommended that formats define the <tt>===</tt> method,
|
52
|
+
# though this is not strictly required. Finally, formats may optionally
|
53
|
+
# implement <tt>uparse_for_serialize</tt>.
|
48
54
|
#
|
49
|
-
# Instead, the purpose here is to define the API for a format. All
|
50
|
-
# formats must define the methods +schema+, +parse+, and +unparse+.
|
51
55
|
# Formats need not extend this base class, as long as they duck-type
|
52
56
|
# these methods.
|
53
57
|
|
@@ -57,14 +61,23 @@ module Versionomy
|
|
57
61
|
# Create an instance of this base format, with the given schema.
|
58
62
|
|
59
63
|
def initialize(schema_)
|
60
|
-
@
|
64
|
+
@_schema = schema_
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def inspect # :nodoc:
|
69
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} schema=#{@_schema.inspect}>"
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s # :nodoc:
|
73
|
+
inspect
|
61
74
|
end
|
62
75
|
|
63
76
|
|
64
77
|
# Returns the schema understood by this format.
|
65
78
|
|
66
79
|
def schema
|
67
|
-
@
|
80
|
+
@_schema
|
68
81
|
end
|
69
82
|
|
70
83
|
|
@@ -90,6 +103,41 @@ module Versionomy
|
|
90
103
|
end
|
91
104
|
|
92
105
|
|
106
|
+
# An optional method that does unparsing especially for serialization.
|
107
|
+
# Implement this if normal unparsing is "lossy" and doesn't guarantee
|
108
|
+
# reconstruction of the version number. This method should attempt to
|
109
|
+
# unparse in such a way that the entire version value can be
|
110
|
+
# reconstructed from the unparsed string. Serialization routines will
|
111
|
+
# first attempt to call this method to unparse for serialization. If
|
112
|
+
# this method is not present, the normal unparse method will be used.
|
113
|
+
#
|
114
|
+
# Return either the unparsed string, or an array consisting of the
|
115
|
+
# unparsed string and a hash of parse params to pass to the parser
|
116
|
+
# when the string is to be reconstructed. You may also either return
|
117
|
+
# nil or raise Versionomy::Errors::UnparseError if the unparsing
|
118
|
+
# cannot be done satisfactorily for serialization. In this case,
|
119
|
+
# serialization will be done using the raw value data rather than an
|
120
|
+
# unparsed string.
|
121
|
+
#
|
122
|
+
# This default implementation just turns around and calls unparse.
|
123
|
+
# Thus it is equivalent to the method not being present at all.
|
124
|
+
|
125
|
+
def unparse_for_serialization(value_)
|
126
|
+
unparse(value_)
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
# Determine whether the given value uses this format.
|
131
|
+
|
132
|
+
def ===(obj_)
|
133
|
+
if obj_.kind_of?(Value)
|
134
|
+
obj_.format == self
|
135
|
+
else
|
136
|
+
obj_ == self
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
|
93
141
|
end
|
94
142
|
|
95
143
|
|
@@ -70,9 +70,9 @@ module Versionomy
|
|
70
70
|
# the parse and unparse methods for details.
|
71
71
|
#
|
72
72
|
# For a usage example, see the definition of the standard format in
|
73
|
-
# Versionomy::
|
73
|
+
# Versionomy::Format::Standard#create.
|
74
74
|
|
75
|
-
class Delimiter
|
75
|
+
class Delimiter < Base
|
76
76
|
|
77
77
|
|
78
78
|
# Create a format using delimiter tools.
|
@@ -128,11 +128,11 @@ module Versionomy
|
|
128
128
|
#
|
129
129
|
# <tt>:extra_characters</tt>::
|
130
130
|
# Determines what to do if the entire string cannot be consumed by
|
131
|
-
# the parsing process. If set to <tt>:ignore</tt
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
135
|
-
#
|
131
|
+
# the parsing process. If set to <tt>:ignore</tt>, any extra
|
132
|
+
# characters are ignored. If set to <tt>:suffix</tt>, the extra
|
133
|
+
# characters are set as the <tt>:suffix</tt> unparse parameter and
|
134
|
+
# are thus appended to the end of the string when unparsing takes
|
135
|
+
# place. If set to <tt>:error</tt> (the default), causes a
|
136
136
|
# Versionomy::Errors::ParseError to be raised if there are
|
137
137
|
# uninterpreted characters.
|
138
138
|
|
@@ -152,10 +152,12 @@ module Versionomy
|
|
152
152
|
end
|
153
153
|
if parse_params_[:string].length > 0
|
154
154
|
case parse_params_[:extra_characters]
|
155
|
-
when :
|
156
|
-
|
155
|
+
when :ignore
|
156
|
+
# do nothing
|
157
157
|
when :suffix
|
158
158
|
unparse_params_[:suffix] = parse_params_[:string]
|
159
|
+
else
|
160
|
+
raise Errors::ParseError, "Extra characters: #{parse_params_[:string].inspect}"
|
159
161
|
end
|
160
162
|
end
|
161
163
|
Value.new(values_, self, unparse_params_)
|
@@ -412,7 +414,7 @@ module Versionomy
|
|
412
414
|
#
|
413
415
|
# The standard format uses styles to preserve the different
|
414
416
|
# syntaxes for the release_type field. See the source code in
|
415
|
-
# Versionomy::
|
417
|
+
# Versionomy::Format::Standard#create for this example.
|
416
418
|
|
417
419
|
def field(name_, opts_={}, &block_)
|
418
420
|
name_ = name_.to_sym
|
@@ -689,15 +691,17 @@ module Versionomy
|
|
689
691
|
@style = opts_[:style]
|
690
692
|
@default_value_optional = opts_[:default_value_optional]
|
691
693
|
@regexp_options = opts_[:case_sensitive] ? nil : ::Regexp::IGNORECASE
|
692
|
-
@value_regexp = ::Regexp.new("
|
693
|
-
regexp_ = opts_
|
694
|
-
@delimiter_regexp = regexp_.length > 0 ? ::Regexp.new("
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
@
|
699
|
-
|
700
|
-
@
|
694
|
+
@value_regexp = ::Regexp.new("\\A(#{value_regexp_})", @regexp_options)
|
695
|
+
regexp_ = opts_[:delimiter_regexp] || '\.'
|
696
|
+
@delimiter_regexp = regexp_.length > 0 ? ::Regexp.new("\\A(#{regexp_})", @regexp_options) : nil
|
697
|
+
@full_delimiter_regexp = regexp_.length > 0 ? ::Regexp.new("\\A(#{regexp_})\\z", @regexp_options) : nil
|
698
|
+
regexp_ = opts_[:post_delimiter_regexp] || ''
|
699
|
+
@post_delimiter_regexp = regexp_.length > 0 ? ::Regexp.new("\\A(#{regexp_})", @regexp_options) : nil
|
700
|
+
@full_post_delimiter_regexp = regexp_.length > 0 ? ::Regexp.new("\\A(#{regexp_})\\z", @regexp_options) : nil
|
701
|
+
regexp_ = opts_[:expected_follower_regexp] || ''
|
702
|
+
@follower_regexp = regexp_.length > 0 ? ::Regexp.new("\\A(#{regexp_})", @regexp_options) : nil
|
703
|
+
@default_delimiter = opts_[:default_delimiter] || '.'
|
704
|
+
@default_post_delimiter = opts_[:default_post_delimiter] || ''
|
701
705
|
@requires_previous_field = opts_.fetch(:requires_previous_field, true)
|
702
706
|
name_ = field_.name
|
703
707
|
@default_field_value = field_.default_value
|
@@ -777,13 +781,21 @@ module Versionomy
|
|
777
781
|
then
|
778
782
|
str_ = unparsed_value(value_, style_, unparse_params_)
|
779
783
|
if str_
|
780
|
-
|
781
|
-
|
782
|
-
|
784
|
+
if !@full_delimiter_regexp
|
785
|
+
delim_ = ''
|
786
|
+
else
|
787
|
+
delim_ = unparse_params_[@delim_unparse_param_key] || @default_delimiter
|
788
|
+
if @full_delimiter_regexp !~ delim_
|
789
|
+
delim_ = @default_delimiter
|
790
|
+
end
|
783
791
|
end
|
784
|
-
|
785
|
-
|
786
|
-
|
792
|
+
if !@full_post_delimiter_regexp
|
793
|
+
post_delim_ = ''
|
794
|
+
else
|
795
|
+
post_delim_ = unparse_params_[@post_delim_unparse_param_key] || @default_post_delimiter
|
796
|
+
if @full_post_delimiter_regexp !~ post_delim_
|
797
|
+
post_delim_ = @default_post_delimiter
|
798
|
+
end
|
787
799
|
end
|
788
800
|
str_ = delim_ + str_ + post_delim_
|
789
801
|
end
|
@@ -876,7 +888,7 @@ module Versionomy
|
|
876
888
|
end
|
877
889
|
|
878
890
|
def unparsed_value(value_, style_, unparse_params_)
|
879
|
-
value_
|
891
|
+
value_.to_s
|
880
892
|
end
|
881
893
|
|
882
894
|
end
|
@@ -921,7 +933,7 @@ module Versionomy
|
|
921
933
|
regexps_ = @mappings_in_order.map{ |map_| "(#{map_[0]})" }
|
922
934
|
setup(field_, regexps_.join('|'), opts_)
|
923
935
|
@mappings_in_order.each do |map_|
|
924
|
-
map_[0] = ::Regexp.new("
|
936
|
+
map_[0] = ::Regexp.new("\\A(#{map_[0]})", @regexp_options)
|
925
937
|
end
|
926
938
|
end
|
927
939
|
|
@@ -0,0 +1,234 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Versionomy standard format implementation
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2008-2009 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
|
+
module Format
|
40
|
+
|
41
|
+
|
42
|
+
# Get the rubygems format.
|
43
|
+
# This is identical to calling <tt>get('rubygems')</tt>.
|
44
|
+
#
|
45
|
+
# The rubygems format is designed to be parse-compatible with the
|
46
|
+
# Gem::Version class used in rubygems. The only caveat is, whereas
|
47
|
+
# Gem::Version handles an arbitrary number of fields, this format is
|
48
|
+
# limited to a maximum of 8.
|
49
|
+
#
|
50
|
+
# For the exact annotated definition of the rubygems schema and format,
|
51
|
+
# see the source code for Versionomy::Format::Rubygems#create.
|
52
|
+
|
53
|
+
def self.rubygems
|
54
|
+
get('rubygems')
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# This is a namespace for the implementation of the Rubygems schema
|
59
|
+
# and format.
|
60
|
+
|
61
|
+
module Rubygems
|
62
|
+
|
63
|
+
|
64
|
+
# Extra methods added to version values that use the rubygems schema.
|
65
|
+
|
66
|
+
module ExtraMethods
|
67
|
+
|
68
|
+
|
69
|
+
# Returns true if the version is a prerelease version-- that is,
|
70
|
+
# if any of the fields is non-numeric.
|
71
|
+
#
|
72
|
+
# This behaves the same as the Gem::Version#prerelease? method
|
73
|
+
# in rubygems.
|
74
|
+
|
75
|
+
def prerelease?
|
76
|
+
values_array.any?{ |val_| val_.kind_of?(::String) }
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# Returns the release for this version.
|
81
|
+
# For example, converts "1.2.0.a.1" to "1.2.0".
|
82
|
+
# Non-prerelease versions return themselves.
|
83
|
+
#
|
84
|
+
# This behaves the same as the Gem::Version#release method
|
85
|
+
# in rubygems.
|
86
|
+
|
87
|
+
def release
|
88
|
+
values_ = []
|
89
|
+
self.each_field_object do |field_, val_|
|
90
|
+
break unless val_.kind_of?(::Integer)
|
91
|
+
values_ << val_
|
92
|
+
end
|
93
|
+
Value.new(values_, self.format, self.unparse_params)
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
# Returns a list of the field values, in field order, with
|
98
|
+
# trailing zeroes stripped off.
|
99
|
+
#
|
100
|
+
# This behaves the same as the Gem::Version#parts method
|
101
|
+
# in rubygems.
|
102
|
+
|
103
|
+
def parts
|
104
|
+
unless @parts
|
105
|
+
@parts = values_array
|
106
|
+
@parts.pop while @parts.size > 1 && @parts.last == 0
|
107
|
+
end
|
108
|
+
@parts
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
# Create the rubygems format.
|
116
|
+
# This method is called internally when Versionomy initializes itself,
|
117
|
+
# and you should not need to call it again. It is documented, however,
|
118
|
+
# so that you can inspect its source code from RDoc, since the source
|
119
|
+
# contains useful examples of how to use the schema and format
|
120
|
+
# definition DSLs.
|
121
|
+
|
122
|
+
def self.create
|
123
|
+
|
124
|
+
# The following is the definition of the rubygems schema
|
125
|
+
schema_ = Schema.create do
|
126
|
+
|
127
|
+
# Global comparison function
|
128
|
+
to_compare_type(:string) do |a_, b_|
|
129
|
+
if a_.kind_of?(::Integer)
|
130
|
+
if b_.kind_of?(::Integer)
|
131
|
+
a_ - b_
|
132
|
+
else
|
133
|
+
1
|
134
|
+
end
|
135
|
+
else
|
136
|
+
if b_.kind_of?(::Integer)
|
137
|
+
-1
|
138
|
+
else
|
139
|
+
a_ <=> b_
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Global canonicalization function
|
145
|
+
to_canonicalize_type(:string) do |val_|
|
146
|
+
if val_.kind_of?(::Integer)
|
147
|
+
val_
|
148
|
+
else
|
149
|
+
val_ = val_.to_s
|
150
|
+
if val_ =~ /\A\d*\z/
|
151
|
+
val_.to_i
|
152
|
+
else
|
153
|
+
val_
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# The first field has the default value of 1. All other fields
|
159
|
+
# have a default value of 0. Thus, the default version number
|
160
|
+
# overall is "1.0".
|
161
|
+
field(:field0, :type => :integer, :default_value => 1) do
|
162
|
+
field(:field1, :type => :string) do
|
163
|
+
field(:field2, :type => :string) do
|
164
|
+
field(:field3, :type => :string) do
|
165
|
+
field(:field4, :type => :string) do
|
166
|
+
field(:field5, :type => :string) do
|
167
|
+
field(:field6, :type => :string) do
|
168
|
+
field(:field7, :type => :string)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Add the methods in this module to each value
|
178
|
+
add_module(Format::Rubygems::ExtraMethods)
|
179
|
+
end
|
180
|
+
|
181
|
+
# The following is the definition of the standard format. It
|
182
|
+
# understands the standard schema defined above.
|
183
|
+
format_ = Format::Delimiter.new(schema_) do
|
184
|
+
|
185
|
+
# All version number strings must start with the major version.
|
186
|
+
# Unlike other fields, it is not preceded by any delimiter.
|
187
|
+
field(:field0) do
|
188
|
+
recognize_number(:delimiter_regexp => '', :default_delimiter => '')
|
189
|
+
end
|
190
|
+
|
191
|
+
# The remainder of the version number are represented as strings
|
192
|
+
# or integers delimited by periods by default. Each is also
|
193
|
+
# dependent on the presence of the previous field, so
|
194
|
+
# :requires_previous_field retains its default value of true.
|
195
|
+
# Finally, they can be optional in an unparsed string if they are
|
196
|
+
# set to the default value of 0.
|
197
|
+
field(:field1) do
|
198
|
+
recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
|
199
|
+
end
|
200
|
+
field(:field2) do
|
201
|
+
recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
|
202
|
+
end
|
203
|
+
field(:field3) do
|
204
|
+
recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
|
205
|
+
end
|
206
|
+
field(:field4) do
|
207
|
+
recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
|
208
|
+
end
|
209
|
+
field(:field5) do
|
210
|
+
recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
|
211
|
+
end
|
212
|
+
field(:field6) do
|
213
|
+
recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
|
214
|
+
end
|
215
|
+
field(:field7) do
|
216
|
+
recognize_regexp('[0-9a-zA-Z]+', :default_value_optional => true)
|
217
|
+
end
|
218
|
+
|
219
|
+
# By default, we require that at least the first two fields
|
220
|
+
# appear in an unparsed version string.
|
221
|
+
default_unparse_params(:required_fields => [:field1])
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
register('rubygems', Format::Rubygems.create) unless get('rubygems')
|
230
|
+
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|