versionomy 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,14 @@
1
+ === 0.2.2 / 2009-11-18
2
+
3
+ * Standard format now supports certain kinds of prereleases without a prerelease number. e.g. "1.9.2dev" is interpreted as the same number as "1.9.2dev0".
4
+ * Added Versionomy#ruby_version.
5
+ * A field can specify a default_value for parsing, distinct from the one specified by the schema.
6
+ * A field can specify requires_next_field to control whether the following field is required or optional.
7
+
1
8
  === 0.2.1 / 2009-11-08
2
9
 
3
10
  * Added Versionomy#version_of.
4
- * Now lets Blockenspiel set its own VERSION instead of reaching into
5
- Blockenspiel's namespace.
11
+ * Now lets Blockenspiel set its own VERSION instead of reaching into Blockenspiel's namespace.
6
12
 
7
13
  === 0.2.0 / 2009-11-05
8
14
 
@@ -8,6 +8,7 @@ numbers in the wide variety of versioning schemes in use.
8
8
 
9
9
  Let's be honest. Version numbers are not easy to deal with, and very
10
10
  seldom seem to be done right.
11
+
11
12
  Imagine the common case of testing the Ruby version. Most of us, if we
12
13
  need to worry about Ruby VM compatibility, will do something like:
13
14
 
@@ -15,21 +16,23 @@ need to worry about Ruby VM compatibility, will do something like:
15
16
 
16
17
  Treating the version number as a string works well enough, until it
17
18
  doesn't. The above code will do the right thing for Ruby 1.8.6, 1.8.7,
18
- 1.8.8, and 1.9.1. But it will fail if the version is "1.8.10".
19
+ 1.8.8, and 1.9.1. But it will fail if the version is "1.8.10" or "1.10".
20
+ And properly interpreting "prerelease" version syntax such as
21
+ "1.9.2-preview1"? Forget it.
19
22
 
20
23
  There are a few version number classes out there that do better than
21
- treating version numbers as plain strings. One well-known class is
22
- Gem::Version, part of rubygems. This class separates the version into
23
- fields and lets you manipulate and compare version numbers more robustly.
24
- It provides limited support for "prerelease" versions through using
25
- string-valued fields as a bit of a hack. However, it's still a little
26
- clumsy. A prerelease version has to be represented like this: "1.9.2.b.1"
27
- or "1.9.2.preview.2". Wouldn't it be nice to be able to parse more typical
28
- version number formats such as "1.9.2b1" and "1.9.2 preview-2"? Wouldn't
24
+ treating version numbers as plain strings. One example is Gem::Version,
25
+ part of the RubyGems package. This class separates the version into fields
26
+ and lets you manipulate and compare version numbers more robustly. It even
27
+ provides limited support for "prerelease" versions through using string-
28
+ valued fields-- although it's a hack, and a bit of a clumsy one at that. A
29
+ prerelease version has to be represented like this: "1.9.2.b.1" or
30
+ "1.9.2.preview.2". Wouldn't it be nice to be able to parse more typical
31
+ version number formats such as "1.9.2b1" and "1.9.2-preview2"? Wouldn't
29
32
  it be nice for a version like "1.9.2b1" to _understand_ that it's a "beta"
30
33
  version and behave accordingly?
31
34
 
32
- With Versionomy, you can!
35
+ With Versionomy, you can do all this and more.
33
36
 
34
37
  === Some examples
35
38
 
@@ -53,7 +56,7 @@ With Versionomy, you can!
53
56
  v2 > v1 # => true
54
57
  v2.to_s # => '1.4a3'
55
58
 
56
- # Another parsing example.
59
+ # Version numbers are semantically self-adjusting.
57
60
  v3 = Versionomy.parse('1.4.0b2')
58
61
  v3.major # => 1
59
62
  v3.minor # => 4
@@ -149,8 +152,6 @@ provides a schema and formatter/parser matching Gem::Version.
149
152
  * Test coverage is still a little skimpy. It is focused on the "standard"
150
153
  version number format and schema, but doesn't fully exercise all the
151
154
  capabilities of custom formats.
152
- * The standard format parser requires that "prerelease" versions have a
153
- prerelease number. e.g. it accepts "1.9.2dev1" but not "1.9.2dev".
154
155
 
155
156
  === Development and support
156
157
 
@@ -50,7 +50,7 @@ module Versionomy
50
50
  # and you should not need to call it again. It is documented, however,
51
51
  # so that you can inspect its source code from RDoc, since the source
52
52
  # contains useful examples of how to use the conversion DSLs.
53
-
53
+
54
54
  def self.create_standard_to_rubygems
55
55
 
56
56
  # We'll use a parsing conversion.
@@ -65,10 +65,14 @@ module Versionomy
65
65
 
66
66
  # If the standard format version has a prerelease notation,
67
67
  # make sure it is set off using a delimiter that the rubygems
68
- # format can recognized. So instead of "1.0b2", we force the
68
+ # format can recognize. So instead of "1.0b2", we force the
69
69
  # unparsing to generate "1.0.b.2".
70
70
  params_[:release_type_delim] = '.'
71
- params_[:release_type_postdelim] = '.'
71
+ params_[:development_version_delim] = '.'
72
+ params_[:alpha_version_delim] = '.'
73
+ params_[:beta_version_delim] = '.'
74
+ params_[:release_candidate_version_delim] = '.'
75
+ params_[:preview_version_delim] = '.'
72
76
 
73
77
  # If the standard format version has a patchlevel notation,
74
78
  # force it to use the default number rather than letter style.
@@ -137,30 +137,69 @@ module Versionomy
137
137
  # uninterpreted characters.
138
138
 
139
139
  def parse(string_, params_=nil)
140
- values_ = {}
141
140
  parse_params_ = default_parse_params
142
141
  parse_params_.merge!(params_) if params_
143
- parse_params_[:string] = string_
144
- parse_params_[:previous_field_missing] = false
145
- unparse_params_ = {}
146
- field_ = @schema.root_field
147
- while field_
142
+ parse_state_ = {
143
+ :backtrack => nil,
144
+ :string => string_,
145
+ :values => {},
146
+ :unparse_params => {},
147
+ :field => @schema.root_field,
148
+ :recognizer_index => 0,
149
+ :previous_field_missing => false
150
+ }
151
+ while (field_ = parse_state_[:field])
148
152
  handler_ = @field_handlers[field_.name]
149
- v_ = handler_.parse(parse_params_, unparse_params_)
150
- values_[field_.name] = v_
151
- field_ = field_.child(v_)
153
+ recognizer_ = handler_.get_recognizer(parse_state_[:recognizer_index])
154
+ parse_data_ = nil
155
+ if recognizer_
156
+ parse_state_[:recognizer_index] += 1
157
+ parse_data_ = recognizer_.parse(parse_state_, parse_params_)
158
+ if parse_data_
159
+ parse_state_[:previous_field_missing] = false
160
+ if recognizer_.requires_next_field
161
+ parse_state_[:next_field_required] = true
162
+ parse_state_ = {
163
+ :backtrack => parse_state_,
164
+ :string => parse_state_[:string],
165
+ :values => parse_state_[:values].dup,
166
+ :unparse_params => parse_state_[:unparse_params].dup,
167
+ :field => parse_state_[:field],
168
+ :recognizer_index => 0,
169
+ :previous_field_missing => false,
170
+ }
171
+ else
172
+ parse_state_[:next_field_required] = false
173
+ end
174
+ end
175
+ elsif parse_state_[:next_field_required]
176
+ parse_state_ = parse_state_[:backtrack]
177
+ else
178
+ parse_data_ = [handler_.default_value, nil, nil, nil]
179
+ parse_state_[:previous_field_missing] = true
180
+ parse_state_[:next_field_required] = false
181
+ end
182
+ if parse_data_
183
+ parse_state_[:values][field_.name] = parse_data_[0]
184
+ parse_state_[:string] = parse_data_[2] if parse_data_[2]
185
+ parse_state_[:unparse_params].merge!(parse_data_[3]) if parse_data_[3]
186
+ parse_state_[:field] = field_.child(parse_data_[0])
187
+ parse_state_[:recognizer_index] = 0
188
+ handler_.set_style_unparse_param(parse_data_[1], parse_state_[:unparse_params])
189
+ end
152
190
  end
153
- if parse_params_[:string].length > 0
191
+ unparse_params_ = parse_state_[:unparse_params]
192
+ if parse_state_[:string].length > 0
154
193
  case parse_params_[:extra_characters]
155
194
  when :ignore
156
195
  # do nothing
157
196
  when :suffix
158
- unparse_params_[:suffix] = parse_params_[:string]
197
+ unparse_params_[:suffix] = parse_state_[:string]
159
198
  else
160
- raise Errors::ParseError, "Extra characters: #{parse_params_[:string].inspect}"
199
+ raise Errors::ParseError, "Extra characters: #{parse_state_[:string].inspect}"
161
200
  end
162
201
  end
163
- Value.new(values_, self, unparse_params_)
202
+ Value.new(parse_state_[:values], self, unparse_params_)
164
203
  end
165
204
 
166
205
 
@@ -210,32 +249,32 @@ module Versionomy
210
249
  unparse_params_.merge!(params_)
211
250
  _interpret_field_lists(unparse_params_)
212
251
  end
213
- unparse_params_.delete(:skipped_handler_list)
214
- unparse_params_.delete(:required_for_later)
252
+ skipped_handler_list_ = nil
253
+ requires_next_field_ = false
215
254
  string_ = ''
216
255
  value_.each_field_object do |field_, val_|
217
256
  handler_ = @field_handlers[field_.name]
218
- fragment_ = handler_.unparse(val_, unparse_params_)
219
- if fragment_
220
- list_ = unparse_params_.delete(:skipped_handler_list)
221
- if list_ && handler_.requires_previous_field && !unparse_params_[:required_for_later]
222
- unparse_params_[:required_for_later] = true
223
- list_.each do |pair_|
224
- frag_ = pair_[0].unparse(pair_[1], unparse_params_)
257
+ unparse_data_ = handler_.unparse(val_, unparse_params_, requires_next_field_)
258
+ if unparse_data_
259
+ if skipped_handler_list_ && handler_.requires_previous_field
260
+ skipped_handler_list_.each do |pair_|
261
+ frag_ = pair_[0].unparse(pair_[1], unparse_params_, true)
225
262
  unless frag_
226
263
  raise Errors::UnparseError, "Field #{field_.name} empty although a prerequisite for a later field"
227
264
  end
228
- string_ << frag_
265
+ string_ << frag_[0]
229
266
  end
230
- unparse_params_[:required_for_later] = false
231
267
  end
232
- string_ << fragment_
268
+ skipped_handler_list_ = nil
269
+ string_ << unparse_data_[0]
270
+ requires_next_field_ = unparse_data_[1]
233
271
  else
234
272
  if handler_.requires_previous_field
235
- (unparse_params_[:skipped_handler_list] ||= []) << [handler_, val_]
273
+ (skipped_handler_list_ ||= []) << [handler_, val_]
236
274
  else
237
- unparse_params_[:skipped_handler_list] = [[handler_, val_]]
275
+ skipped_handler_list_ = [[handler_, val_]]
238
276
  end
277
+ requires_next_field_ = false
239
278
  end
240
279
  end
241
280
  string_ << (unparse_params_[:suffix] || '')
@@ -280,14 +319,14 @@ module Versionomy
280
319
  def _interpret_field_lists(unparse_params_) # :nodoc:
281
320
  fields_ = unparse_params_.delete(:required_fields)
282
321
  if fields_
283
- fields_ = [fields_] unless fields_.kind_of?(Array)
322
+ fields_ = [fields_] unless fields_.kind_of?(::Array)
284
323
  fields_.each do |f_|
285
324
  unparse_params_["#{f_}_required".to_sym] = true
286
325
  end
287
326
  end
288
327
  fields_ = unparse_params_.delete(:optional_fields)
289
328
  if fields_
290
- fields_ = [fields_] unless fields_.kind_of?(Array)
329
+ fields_ = [fields_] unless fields_.kind_of?(::Array)
291
330
  fields_.each do |f_|
292
331
  unparse_params_["#{f_}_required".to_sym] = false
293
332
  end
@@ -353,6 +392,10 @@ module Versionomy
353
392
  # field is optional, but the first and second are required, so it
354
393
  # will often be unparsed as "2.0".
355
394
  # Default is false.
395
+ # <tt>:default_value</tt>::
396
+ # The actual value set for this field if it is omitted from the
397
+ # version string. Defaults to the field's schema default value,
398
+ # but that can be overridden here.
356
399
  # <tt>:case_sensitive</tt>::
357
400
  # If set to true, the regexps are case-sensitive. Default is false.
358
401
  # <tt>:delimiter_regexp</tt>::
@@ -391,6 +434,15 @@ module Versionomy
391
434
  # parameter set to true. The default is true, so you must specify
392
435
  # <tt>:requires_previous_field => false</tt> explicitly if you want
393
436
  # a field not to require the previous field.
437
+ # <tt>:requires_next_field</tt>::
438
+ # If set to true, this field's presence in a formatted version
439
+ # string requires the presence of the next field. For example, in
440
+ # the version "1.0a5", the release_type field requires the presence
441
+ # of the alpha_version field, because if the "5" was missing, the
442
+ # string "1.0a" looks like a patchlevel indicator. Often it is
443
+ # easier to set default_value_optional in the next field, but this
444
+ # option is also available if the behavior is dependent on the
445
+ # value of this previous field.
394
446
  # <tt>:default_style</tt>::
395
447
  # The default style for this field. This is the style used for
396
448
  # unparsing if the value was not constructed by a parser or is
@@ -607,6 +659,7 @@ module Versionomy
607
659
  @field = field_
608
660
  @recognizers = []
609
661
  @requires_previous_field = default_opts_.fetch(:requires_previous_field, true)
662
+ @default_value = default_opts_[:default_value] || field_.default_value
610
663
  @default_style = default_opts_.fetch(:default_style, nil)
611
664
  @style_unparse_param_key = "#{field_.name}_style".to_sym
612
665
  if block_
@@ -631,37 +684,44 @@ module Versionomy
631
684
  end
632
685
 
633
686
 
634
- # Parse this field from the string.
635
- # This must either return a parsed value, or raise an error.
636
- # It should also set the style in the unparse_params, if the style
637
- # is determined not to be the default.
687
+ # Returns the default value set when this field is missing from a
688
+ # version string.
638
689
 
639
- def parse(parse_params_, unparse_params_)
640
- pair_ = nil
641
- @recognizers.each do |recog_|
642
- pair_ = recog_.parse(parse_params_, unparse_params_)
643
- break if pair_
644
- end
645
- parse_params_[:previous_field_missing] = pair_.nil?
646
- pair_ ||= [@field.default_value, @default_style]
647
- if pair_[1] && pair_[1] != @default_style
648
- unparse_params_[@style_unparse_param_key] = pair_[1]
690
+ def default_value
691
+ @default_value
692
+ end
693
+
694
+
695
+ # Gets the given indexed recognizer. Returns nil if the index is out
696
+ # of range.
697
+
698
+ def get_recognizer(index_)
699
+ @recognizers[index_]
700
+ end
701
+
702
+
703
+ # Finishes up parsing by setting the appropriate style field in the
704
+ # unparse_params, if needed.
705
+
706
+ def set_style_unparse_param(style_, unparse_params_)
707
+ if style_ && style_ != @default_style
708
+ unparse_params_[@style_unparse_param_key] = style_
649
709
  end
650
- pair_[0]
651
710
  end
652
711
 
653
712
 
654
713
  # Unparse a string from this field value.
655
714
  # This may return nil if this field is not required.
656
715
 
657
- def unparse(value_, unparse_params_)
716
+ def unparse(value_, unparse_params_, required_for_later_)
658
717
  style_ = unparse_params_[@style_unparse_param_key] || @default_style
659
718
  @recognizers.each do |recog_|
660
719
  if recog_.should_unparse?(value_, style_)
661
- return recog_.unparse(value_, style_, unparse_params_)
720
+ fragment_ = recog_.unparse(value_, style_, unparse_params_, required_for_later_)
721
+ return fragment_ ? [fragment_, recog_.requires_next_field] : nil
662
722
  end
663
723
  end
664
- unparse_params_[:required_for_later] ? '' : nil
724
+ required_for_later_ ? ['', false] : nil
665
725
  end
666
726
 
667
727
  end
@@ -690,6 +750,7 @@ module Versionomy
690
750
  def setup(field_, value_regexp_, opts_)
691
751
  @style = opts_[:style]
692
752
  @default_value_optional = opts_[:default_value_optional]
753
+ @default_value = opts_[:default_value] || field_.default_value
693
754
  @regexp_options = opts_[:case_sensitive] ? nil : ::Regexp::IGNORECASE
694
755
  @value_regexp = ::Regexp.new("\\A(#{value_regexp_})", @regexp_options)
695
756
  regexp_ = opts_[:delimiter_regexp] || '\.'
@@ -703,8 +764,8 @@ module Versionomy
703
764
  @default_delimiter = opts_[:default_delimiter] || '.'
704
765
  @default_post_delimiter = opts_[:default_post_delimiter] || ''
705
766
  @requires_previous_field = opts_.fetch(:requires_previous_field, true)
767
+ @requires_next_field = opts_.fetch(:requires_next_field, false)
706
768
  name_ = field_.name
707
- @default_field_value = field_.default_value
708
769
  @delim_unparse_param_key = "#{name_}_delim".to_sym
709
770
  @post_delim_unparse_param_key = "#{name_}_postdelim".to_sym
710
771
  @required_unparse_param_key = "#{name_}_required".to_sym
@@ -716,9 +777,9 @@ module Versionomy
716
777
  # Returns either nil, indicating that this recognizer doesn't match
717
778
  # the given syntax, or a two element array of the value and style.
718
779
 
719
- def parse(parse_params_, unparse_params_)
720
- return nil if @requires_previous_field && parse_params_[:previous_field_missing]
721
- string_ = parse_params_[:string]
780
+ def parse(parse_state_, parse_params_)
781
+ return nil if @requires_previous_field && parse_state_[:previous_field_missing]
782
+ string_ = parse_state_[:string]
722
783
  if @delimiter_regexp
723
784
  match_ = @delimiter_regexp.match(string_)
724
785
  return nil unless match_
@@ -743,9 +804,9 @@ module Versionomy
743
804
  match_ = @follower_regexp.match(string_)
744
805
  return nil unless match_
745
806
  end
746
- value_ = parsed_value(value_, parse_params_, unparse_params_)
747
- return nil unless value_
748
- parse_params_[:string] = string_
807
+ parse_result_ = parsed_value(value_, parse_params_)
808
+ return nil unless parse_result_
809
+ unparse_params_ = parse_result_[1] || {}
749
810
  if delim_ != @default_delimiter
750
811
  unparse_params_[@delim_unparse_param_key] = delim_
751
812
  end
@@ -753,7 +814,15 @@ module Versionomy
753
814
  unparse_params_[@post_delim_unparse_param_key] = post_delim_
754
815
  end
755
816
  unparse_params_[@required_unparse_param_key] = true if @default_value_optional
756
- [value_, @style]
817
+ [parse_result_[0], @style, string_, unparse_params_]
818
+ end
819
+
820
+
821
+ # Returns true if this field can appear in an unparsed string only
822
+ # if the next field also appears.
823
+
824
+ def requires_next_field
825
+ @requires_next_field
757
826
  end
758
827
 
759
828
 
@@ -773,11 +842,10 @@ module Versionomy
773
842
  # It is guaranteed that this will be called only if should_unparse?
774
843
  # returns true.
775
844
 
776
- def unparse(value_, style_, unparse_params_)
845
+ def unparse(value_, style_, unparse_params_, required_for_later_)
777
846
  str_ = nil
778
- if !@default_value_optional || value_ != @default_field_value ||
779
- unparse_params_[:required_for_later] ||
780
- unparse_params_[@required_unparse_param_key]
847
+ if !@default_value_optional || value_ != @default_value ||
848
+ required_for_later_ || unparse_params_[@required_unparse_param_key]
781
849
  then
782
850
  str_ = unparsed_value(value_, style_, unparse_params_)
783
851
  if str_
@@ -816,8 +884,8 @@ module Versionomy
816
884
  setup(field_, '\d+', opts_)
817
885
  end
818
886
 
819
- def parsed_value(value_, parse_params_, unparse_params_)
820
- value_.to_i
887
+ def parsed_value(value_, parse_params_)
888
+ [value_.to_i, nil]
821
889
  end
822
890
 
823
891
  def unparsed_value(value_, style_, unparse_params_)
@@ -846,16 +914,14 @@ module Versionomy
846
914
  setup(field_, value_regexp_, opts_)
847
915
  end
848
916
 
849
- def parsed_value(value_, parse_params_, unparse_params_)
917
+ def parsed_value(value_, parse_params_)
850
918
  value_ = value_.unpack('c')[0] # Compatible with both 1.8 and 1.9
851
919
  if value_ >= 97 && value_ <= 122
852
- unparse_params_[@case_unparse_param_key] = :lower
853
- value_ - 96
920
+ [value_ - 96, {@case_unparse_param_key => :lower}]
854
921
  elsif value_ >= 65 && value_ <= 90
855
- unparse_params_[@case_unparse_param_key] = :upper
856
- value_ - 64
922
+ [value_ - 64, {@case_unparse_param_key => :upper}]
857
923
  else
858
- 0
924
+ [0, nil]
859
925
  end
860
926
  end
861
927
 
@@ -883,8 +949,8 @@ module Versionomy
883
949
  setup(field_, regexp_, opts_)
884
950
  end
885
951
 
886
- def parsed_value(value_, parse_params_, unparse_params_)
887
- value_
952
+ def parsed_value(value_, parse_params_)
953
+ [value_, nil]
888
954
  end
889
955
 
890
956
  def unparsed_value(value_, style_, unparse_params_)
@@ -905,8 +971,8 @@ module Versionomy
905
971
  @canonical = canonical_
906
972
  end
907
973
 
908
- def parsed_value(value_, parse_params, unparse_params_)
909
- @value
974
+ def parsed_value(value_, parse_params_)
975
+ [@value, nil]
910
976
  end
911
977
 
912
978
  def unparsed_value(value_, style_, unparse_params_)
@@ -937,9 +1003,9 @@ module Versionomy
937
1003
  end
938
1004
  end
939
1005
 
940
- def parsed_value(value_, parse_params, unparse_params_)
1006
+ def parsed_value(value_, parse_params_)
941
1007
  @mappings_in_order.each do |map_|
942
- return map_[2] if map_[0].match(value_)
1008
+ return [map_[2], nil] if map_[0].match(value_)
943
1009
  end
944
1010
  nil
945
1011
  end
@@ -277,73 +277,87 @@ module Versionomy
277
277
  # syntax looks like "1.0a5". Long syntax looks more like
278
278
  # "1.0 Alpha 5". The parsed value retains knowledge of which style
279
279
  # it came from so it can be reconstructed when the value is unparsed.
280
- # Finally, we turn requires_previous_field off because the release
280
+ # Note that we turn requires_previous_field off because the release
281
281
  # type syntax markers don't require any particular set of the core
282
282
  # version number fields to be present. "1.0a5" and "1.0.0.0a5" are
283
283
  # both valid version numbers.
284
284
  field(:release_type, :requires_previous_field => false,
285
285
  :default_style => :short) do
286
- # First check for "short form" syntax. Not that we support post-
287
- # delimiters; that is, we recognize "1.0 pre-2" where the hyphen
288
- # is a post-delimiter. Also notice that we expect prerelease types
289
- # to be followed by a numeric prerelease version number.
286
+ # Some markers require a prerelease version (e.g. the 5 in
287
+ # "1.0a5") while others don't (e.g. "1.9.2dev"). This is because
288
+ # the syntax "1.0a" looks like a patchlevel syntax. So some of
289
+ # the following recognizers set requires_next_field while others
290
+ # do not.
291
+ # Also note that we omit the value <tt>:final</tt>. This is
292
+ # because that value is signaled by the absence of any syntax in
293
+ # the version string, including the absence of any delimiters.
294
+ # So we just allow it to fall through to the default.
295
+
296
+ recognize_regexp_map(:style => :long, :default_delimiter => '',
297
+ :delimiter_regexp => '-|\.|\s?') do
298
+ map(:development, 'dev')
299
+ map(:alpha, 'alpha')
300
+ map(:beta, 'beta')
301
+ map(:preview, 'preview')
302
+ end
290
303
  recognize_regexp_map(:style => :short, :default_delimiter => '',
291
- :delimiter_regexp => '-|\.|\s?',
292
- :post_delimiter_regexp => '-|\.|\s?',
293
- :expected_follower_regexp => '\d') do
294
- map(:development, 'd')
295
- map(:alpha, 'a')
296
- map(:beta, 'b')
304
+ :delimiter_regexp => '-|\.|\s?') do
297
305
  map(:release_candidate, 'rc')
298
306
  map(:preview, 'pre')
299
- # Note that we omit the value <tt>:final</tt>. This is because
300
- # that value is signaled by the absence of any syntax in the
301
- # version string, including the absence of any delimiters. So we
302
- # just allow it to fall through to the default.
303
307
  end
304
- # Check for "long form" syntax. Note again that we omit :final.
305
308
  recognize_regexp_map(:style => :long, :default_delimiter => '',
306
- :delimiter_regexp => '-|\.|\s?',
307
- :post_delimiter_regexp => '-|\.|\s?',
308
- :expected_follower_regexp => '\d') do
309
- map(:development, 'dev')
310
- map(:alpha, 'alpha')
311
- map(:beta, 'beta')
309
+ :delimiter_regexp => '-|\.|\s?') do
312
310
  map(:release_candidate, 'rc')
313
- map(:preview, 'preview')
311
+ end
312
+ recognize_regexp_map(:style => :short, :default_delimiter => '',
313
+ :delimiter_regexp => '-|\.|\s?',
314
+ :requires_next_field => true) do
315
+ map(:development, 'd')
316
+ map(:alpha, 'a')
317
+ map(:beta, 'b')
314
318
  end
315
319
  end
316
320
 
317
- # The development version must appear in the string if it is present
318
- # in the value, even if the value is 0. Similar for all the other
319
- # prerelease version numbers: alpha, beta, release candidate, and
320
- # preview. However, the minor fields are optional.
321
- field(:development_version) do
322
- recognize_number(:delimiter_regexp => '', :default_delimiter => '')
321
+ # The main prerelease version may sometimes be optional, so we
322
+ # mark it as optional here. If it is required, that will be
323
+ # signalled by requires_next_field on the release_type field.
324
+ # Minor prerelease versions are always optional.
325
+ # Note that we override the default_value (normally 1) and set
326
+ # it to 0 if a main prerelease version is not present. This is
327
+ # so schema-oriented operations like bumping will set the value
328
+ # to 1, while parsing a string will yield 0 when the field is
329
+ # missing (e.g. we want "1.9.2dev" to mean "1.9.2dev0".)
330
+ field(:development_version, :default_value => 0) do
331
+ recognize_number(:delimiter_regexp => '-|\.|\s?', :default_delimiter => '',
332
+ :default_value_optional => true)
323
333
  end
324
334
  field(:development_minor) do
325
335
  recognize_number(:default_value_optional => true)
326
336
  end
327
- field(:alpha_version) do
328
- recognize_number(:delimiter_regexp => '', :default_delimiter => '')
337
+ field(:alpha_version, :default_value => 0) do
338
+ recognize_number(:delimiter_regexp => '-|\.|\s?', :default_delimiter => '',
339
+ :default_value_optional => true)
329
340
  end
330
341
  field(:alpha_minor) do
331
342
  recognize_number(:default_value_optional => true)
332
343
  end
333
- field(:beta_version) do
334
- recognize_number(:delimiter_regexp => '', :default_delimiter => '')
344
+ field(:beta_version, :default_value => 0) do
345
+ recognize_number(:delimiter_regexp => '-|\.|\s?', :default_delimiter => '',
346
+ :default_value_optional => true)
335
347
  end
336
348
  field(:beta_minor) do
337
349
  recognize_number(:default_value_optional => true)
338
350
  end
339
- field(:release_candidate_version) do
340
- recognize_number(:delimiter_regexp => '', :default_delimiter => '')
351
+ field(:release_candidate_version, :default_value => 0) do
352
+ recognize_number(:delimiter_regexp => '-|\.|\s?', :default_delimiter => '',
353
+ :default_value_optional => true)
341
354
  end
342
355
  field(:release_candidate_minor) do
343
356
  recognize_number(:default_value_optional => true)
344
357
  end
345
- field(:preview_version) do
346
- recognize_number(:delimiter_regexp => '', :default_delimiter => '')
358
+ field(:preview_version, :default_value => 0) do
359
+ recognize_number(:delimiter_regexp => '-|\.|\s?', :default_delimiter => '',
360
+ :default_value_optional => true)
347
361
  end
348
362
  field(:preview_minor) do
349
363
  recognize_number(:default_value_optional => true)
@@ -366,7 +380,8 @@ module Versionomy
366
380
 
367
381
  # By default, we require that at least the major and minor fields
368
382
  # appear in an unparsed version string.
369
- default_unparse_params(:required_fields => [:minor])
383
+ default_unparse_params(:required_fields => [:minor, :development_version, :alpha_version,
384
+ :beta_version, :release_candidate_version, :preview_version])
370
385
  end
371
386
  end
372
387
 
@@ -154,6 +154,21 @@ module Versionomy
154
154
  end
155
155
 
156
156
 
157
+ # Get the ruby version as a Versionomy::Value, using the builtin
158
+ # constants RUBY_VERSION and RUBY_PATCHLEVEL.
159
+
160
+ def ruby_version
161
+ unless @ruby_version
162
+ @ruby_version = parse(::RUBY_VERSION, :standard)
163
+ if @ruby_version.release_type == :final
164
+ @ruby_version = @ruby_version.change({:patchlevel => ::RUBY_PATCHLEVEL},
165
+ :patchlevel_required => true, :patchlevel_delim => '-p')
166
+ end
167
+ end
168
+ @ruby_version
169
+ end
170
+
171
+
157
172
  end
158
173
 
159
174
  end
@@ -37,7 +37,7 @@
37
37
  module Versionomy
38
38
 
39
39
  # Current gem version, as a frozen string.
40
- VERSION_STRING = '0.2.1'.freeze
40
+ VERSION_STRING = '0.2.2'.freeze
41
41
 
42
42
  # Current gem version, as a Versionomy::Value.
43
43
  VERSION = ::Versionomy.parse(VERSION_STRING, :standard)
@@ -93,6 +93,21 @@ module Versionomy
93
93
  end
94
94
 
95
95
 
96
+ # Test parsing major version only.
97
+
98
+ def test_parsing_major_only
99
+ value_ = ::Versionomy.parse('2')
100
+ assert_equal(2, value_.major)
101
+ assert_equal(0, value_.minor)
102
+ assert_equal(0, value_.tiny)
103
+ assert_equal(0, value_.tiny2)
104
+ assert_equal(:final, value_.release_type)
105
+ assert_equal(0, value_.patchlevel)
106
+ assert_equal(0, value_.patchlevel_minor)
107
+ assert_equal('2', value_.unparse)
108
+ end
109
+
110
+
96
111
  # Test parsing preview.
97
112
 
98
113
  def test_parsing_preview
@@ -151,6 +166,7 @@ module Versionomy
151
166
  assert_equal(::Versionomy.parse('2.52.1BETA4'), '2.52.1b4')
152
167
  assert_equal(::Versionomy.parse('2.52.1 Beta4'), '2.52.1b4')
153
168
  assert_equal(::Versionomy.parse('2.52.1 eta4', :extra_characters => :ignore), '2.52.1')
169
+ assert_equal(::Versionomy.parse('2.52.1 Beta'), '2.52.1b0')
154
170
  end
155
171
 
156
172
 
@@ -167,7 +183,53 @@ module Versionomy
167
183
  assert_equal(0, value_.release_candidate_minor)
168
184
  assert_equal('0.2rc0', value_.unparse)
169
185
  assert_equal('0.2rc0.0', value_.unparse(:required_fields => [:release_candidate_minor]))
170
- assert_equal('0.2rc0', value_.unparse(:optional_fields => [:release_candidate_version]))
186
+ assert_equal('0.2rc', value_.unparse(:optional_fields => [:release_candidate_version]))
187
+ end
188
+
189
+
190
+ # Test parsing release candidate changing to other prerelease.
191
+ # Ensures that :short style takes precedence over :long for parsing "rc".
192
+
193
+ def test_parsing_release_candidate_type_change
194
+ value_ = ::Versionomy.parse('0.2rc1')
195
+ assert_equal(:release_candidate, value_.release_type)
196
+ assert_equal(1, value_.release_candidate_version)
197
+ assert_equal('0.2rc1', value_.unparse)
198
+ value_ = value_.change(:release_type => :beta)
199
+ assert_equal(:beta, value_.release_type)
200
+ assert_equal(1, value_.beta_version)
201
+ assert_equal('0.2b1', value_.unparse)
202
+ value_ = value_.change({:release_type => :beta}, :release_type_style => :long)
203
+ assert_equal(:beta, value_.release_type)
204
+ assert_equal(1, value_.beta_version)
205
+ assert_equal('0.2beta1', value_.unparse)
206
+ end
207
+
208
+
209
+ # Test parsing forms without a prerelease version
210
+
211
+ def test_parsing_without_prerelease_version
212
+ value_ = ::Versionomy.parse('1.9.2dev')
213
+ assert_equal(value_.release_type, :development)
214
+ assert_equal(value_.development_version, 0)
215
+ assert_equal('1.9.2dev', value_.to_s)
216
+ value_ = value_.bump(:development_version)
217
+ assert_equal('1.9.2dev1', value_.to_s)
218
+ end
219
+
220
+
221
+ # Test parsing forms without a prerelease version.
222
+ # Ensures that :development_version prefers to be required.
223
+
224
+ def test_unparsing_prerelease_version_0
225
+ value_ = ::Versionomy.parse('1.9.2dev1')
226
+ assert_equal(value_.release_type, :development)
227
+ assert_equal(value_.development_version, 1)
228
+ assert_equal('1.9.2dev1', value_.to_s)
229
+ value2_ = value_.change(:development_version => 0)
230
+ assert_equal('1.9.2dev0', value2_.to_s)
231
+ value2_ = value_.change({:development_version => 0}, :optional_fields => [:development_version])
232
+ assert_equal('1.9.2dev', value2_.to_s)
171
233
  end
172
234
 
173
235
 
@@ -212,10 +274,10 @@ module Versionomy
212
274
 
213
275
  def test_unparse_with_custom_delimiters
214
276
  value_ = ::Versionomy.parse('1.2b3')
215
- assert_equal('1.2.b.3', value_.unparse(:release_type_delim => '.', :release_type_postdelim => '.'))
216
- assert_equal('1.2b3', value_.unparse(:release_type_delim => '=', :release_type_postdelim => '*'))
277
+ assert_equal('1.2.b.3', value_.unparse(:release_type_delim => '.', :beta_version_delim => '.'))
278
+ assert_equal('1.2b3', value_.unparse(:release_type_delim => '=', :beta_version_delim => '*'))
217
279
  value_ = ::Versionomy.parse('1.2-4')
218
- assert_equal('1.2-4', value_.unparse(:release_type_delim => '.', :release_type_postdelim => '.'))
280
+ assert_equal('1.2-4', value_.unparse(:release_type_delim => '.'))
219
281
  end
220
282
 
221
283
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: versionomy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-08 00:00:00 -08:00
12
+ date: 2009-11-18 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency