versionomy 0.2.1 → 0.2.2

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.
@@ -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