versionomy 0.0.4 → 0.1.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.
@@ -3,7 +3,7 @@
3
3
  # Versionomy value
4
4
  #
5
5
  # -----------------------------------------------------------------------------
6
- # Copyright 2008 Daniel Azuma
6
+ # Copyright 2008-2009 Daniel Azuma
7
7
  #
8
8
  # All rights reserved.
9
9
  #
@@ -45,32 +45,36 @@ module Versionomy
45
45
  # number "1.4.2" would have the values <tt>[1, 4, 2]</tt> in that order,
46
46
  # corresponding to the fields <tt>[:major, :minor, :tiny]</tt>.
47
47
  #
48
- # Version number values are comparable with other values that have the same
49
- # form.
48
+ # Version number values are comparable with other values that have an
49
+ # equivalent schema.
50
50
 
51
51
  class Value
52
52
 
53
53
 
54
- def initialize(schema_, values_=nil, parse_params_=nil, subvalue_=nil) # :nodoc:
55
- @schema = schema_
56
- val_ = nil
57
- case values_
58
- when Hash
59
- val_ = values_[@schema.name]
60
- when Array
61
- val_ = values_.first
62
- values_ = values_[1..-1]
63
- else
64
- val_ = values_
65
- values_ = nil
54
+ # Create a value, given a hash or array of values, and a format. Both
55
+ # these parameters are required.
56
+ #
57
+ # The values should either be a hash of field names and values, or an
58
+ # array of values that will be interpreted in field order.
59
+ #
60
+ # You can also optionally provide default unparsing parameters for the
61
+ # value.
62
+
63
+ def initialize(values_, format_, unparse_params_=nil)
64
+ unless values_.kind_of?(::Hash) || values_.kind_of?(::Array)
65
+ raise ::ArgumentError, "Expected hash or array but got #{values_.class}"
66
66
  end
67
- @value = val_ ? @schema.canonicalize_value(val_) : @schema.initial_value
68
- @parse_params = parse_params_ || Hash.new
69
- if subvalue_
70
- @subvalue = subvalue_
71
- else
72
- subschema_ = @schema._subschema(@value)
73
- @subvalue = subschema_ ? Versionomy::Value._new(subschema_, values_) : nil
67
+ @format = format_
68
+ @unparse_params = unparse_params_
69
+ @field_path = []
70
+ @values = {}
71
+ field_ = @format.schema.root_field
72
+ while field_
73
+ value_ = values_.kind_of?(Hash) ? values_[field_.name] : values_.shift
74
+ value_ = value_ ? field_.canonicalize_value(value_) : field_.default_value
75
+ @field_path << field_
76
+ @values[field_.name] = value_
77
+ field_ = field_.child(value_)
74
78
  end
75
79
  end
76
80
 
@@ -79,42 +83,25 @@ module Versionomy
79
83
  begin
80
84
  str_ = unparse
81
85
  "#<#{self.class}:0x#{object_id.to_s(16)} #{str_.inspect}>"
82
- rescue Versionomy::Errors::ParseError
86
+ rescue Errors::UnparseError
83
87
  _inspect
84
88
  end
85
89
  end
86
90
 
87
91
  def _inspect # :nodoc:
88
- "#<#{self.class}:0x#{object_id.to_s(16)}#{_inspect2}>"
89
- end
90
-
91
- def _inspect2 # :nodoc:
92
- " #{@schema.name}=#{@value.inspect}#{@subvalue ? @subvalue._inspect2 : ''}"
93
- end
94
-
95
-
96
- # Get the value of the most significant field
97
-
98
- def _toplevel_value # :nodoc:
99
- @value
100
- end
101
-
102
-
103
- # Get a value representing all fields except the most significant field
104
-
105
- def _subvalue # :nodoc:
106
- @subvalue
92
+ "#<#{self.class}:0x#{object_id.to_s(16)} " +
93
+ @field_path.map{ |field_| "#{field_.name}=#{@values[field_.name].inspect}" }.join(' ')
107
94
  end
108
95
 
109
96
 
110
97
  # Returns a string representation generated by unparsing.
111
- # If unparsing fails, does not raise Versionomy::Errors::ParseError,
98
+ # If unparsing fails, does not raise Versionomy::Errors::UnparseError,
112
99
  # but instead returns the string generated by +inspect+.
113
100
 
114
101
  def to_s
115
102
  begin
116
103
  unparse
117
- rescue Versionomy::Errors::ParseError
104
+ rescue Errors::UnparseError
118
105
  _inspect
119
106
  end
120
107
  end
@@ -122,151 +109,152 @@ module Versionomy
122
109
 
123
110
  # Unparse this version number.
124
111
  #
125
- # Raises Versionomy::Errors::ParseError if unparsing failed.
112
+ # Raises Versionomy::Errors::UnparseError if unparsing failed.
126
113
 
127
- def unparse(params_={})
128
- format_ = @schema.get_format(params_[:format])
129
- if format_.nil?
130
- raise Versionomy::Errors::UnknownFormatError
131
- end
132
- format_.unparse(@schema, self, params_)
114
+ def unparse(params_=nil)
115
+ @format.unparse(self, params_)
133
116
  end
134
117
 
135
118
 
136
- # Parse another string using the same schema, and same parse parameters
137
- # as this value, subject to the given modifications.
119
+ # Return the schema defining the form of this version number
138
120
 
139
- def parse(str_, params_={})
140
- @schema.parse(str_, @parse_params.merge(params_))
121
+ def schema
122
+ @format.schema
141
123
  end
142
124
 
143
125
 
144
- # Return the schema defining the form of this version number
126
+ # Return the format defining the form of this version number
145
127
 
146
- def schema
147
- @schema
128
+ def format
129
+ @format
148
130
  end
149
131
 
150
132
 
151
- # Return the parsing parameters for this value.
133
+ # Return the unparsing parameters for this value.
134
+ # Returns nil if this value was not created using a parser.
152
135
 
153
- def parse_params
154
- @parse_params
136
+ def unparse_params
137
+ @unparse_params ? @unparse_params.dup : nil
138
+ end
139
+
140
+
141
+ # Iterates over each field, in field order, yielding the field name and value.
142
+
143
+ def each_field
144
+ @field_path.each do |field_|
145
+ yield(field_, @values[field_.name])
146
+ end
147
+ end
148
+
149
+
150
+ # Iterates over each field, in field order, yielding the
151
+ # Versionomy::Schema::Field object and value.
152
+
153
+ def each_field_object # :nodoc:
154
+ @field_path.each do |field_|
155
+ yield(field_, @values[field_.name])
156
+ end
155
157
  end
156
158
 
157
159
 
158
160
  # Returns an array of recognized field names for this value, in field order.
159
161
 
160
- def fields
161
- @subvalue ? @subvalue.fields.unshift(@schema.name) : [@schema.name]
162
+ def field_names
163
+ @field_path.map{ |field_| field_.name }
162
164
  end
163
165
 
164
166
 
165
- # Returns true if this value contains the given field.
167
+ # Returns true if this value contains the given field, which may be specified
168
+ # as a field object or name.
166
169
 
167
- def has_field?(symbol_)
168
- symbol_ = symbol_.to_sym
169
- if symbol_ == @schema.name
170
- true
171
- elsif @subvalue
172
- @subvalue.has_field?(symbol_)
170
+ def has_field?(field_)
171
+ case field_
172
+ when Schema::Field
173
+ @field_path.include?(field_)
174
+ when ::String, ::Symbol
175
+ @values.has_key?(field_.to_sym)
173
176
  else
174
- false
177
+ raise ::ArgumentError
175
178
  end
176
179
  end
177
180
 
178
181
 
179
182
  # Returns the value of the given field, or nil if the field is not recognized.
183
+ # The field may be specified as a field object or field name.
180
184
 
181
- def [](symbol_)
182
- symbol_ = symbol_ ? symbol_.to_sym : @schema.name
183
- if symbol_ == @schema.name
184
- @value
185
- elsif @subvalue
186
- @subvalue[symbol_]
187
- else
188
- nil
189
- end
185
+ def [](field_)
186
+ field_ = field_.name if field_.kind_of?(Schema::Field)
187
+ @values[field_.to_sym]
190
188
  end
191
189
 
192
190
 
193
191
  # Returns the value as an array of field values, in field order.
194
192
 
195
- def values
196
- @subvalue ? @subvalue.values.unshift(@value) : [@value]
193
+ def values_array
194
+ @field_path.map{ |field_| @values[field_.name] }
197
195
  end
198
196
 
199
197
 
200
198
  # Returns the value as a hash of values keyed by field name.
201
199
 
202
- def value_hash
203
- hash_ = {@schema.name => @value}
204
- @subvalue ? @subvalue.value_hash.merge(hash_) : hash_
200
+ def values_hash
201
+ @values.dup
205
202
  end
206
203
 
207
204
 
208
205
  # Returns a new version number created by bumping the given field.
209
206
 
210
- def bump(symbol_)
211
- if (symbol_ == @schema.name)
212
- bumped_ = @schema.bump_value(@value)
213
- if bumped_ == @value
214
- self
215
- else
216
- Versionomy::Value._new(@schema, bumped_, @parse_params)
217
- end
218
- else
219
- if @subvalue
220
- bumped_ = @subvalue.bump(symbol_)
221
- if @subvalue.equal?(bumped_)
222
- self
223
- else
224
- Versionomy::Value._new(@schema, @value, @parse_params, bumped_)
225
- end
207
+ def bump(name_)
208
+ name_ = name_.name if name_.kind_of?(Schema::Field)
209
+ name_ = name_.to_sym
210
+ values_ = []
211
+ @field_path.each do |field_|
212
+ oldval_ = @values[field_.name]
213
+ if field_.name == name_
214
+ newval_ = field_.bump_value(oldval_)
215
+ return self if newval_ == oldval_
216
+ values_ << newval_
217
+ return Value.new(values_, @format, @unparse_params)
226
218
  else
227
- self
219
+ values_ << oldval_
228
220
  end
229
221
  end
222
+ self
230
223
  end
231
224
 
232
225
 
233
226
  # Returns a new version number created by changing the given field values.
234
227
 
235
- def change(values_={})
236
- Versionomy::Value._new(@schema, value_hash.merge(values_), @parse_params)
228
+ def change(values_={}, unparse_params_={})
229
+ unparse_params_ = @unparse_params.merge(unparse_params_) if @unparse_params
230
+ Value.new(@values.merge(values_), @format, unparse_params_)
237
231
  end
238
232
 
239
233
 
240
234
  def hash # :nodoc:
241
- @hash ||= @schema.name.hash ^ @value.hash ^ @subvalue.hash
235
+ @hash ||= @values.hash
242
236
  end
243
237
 
244
238
 
245
239
  # Returns true if this version number is equal to the given verison number.
246
- # Equality means the values and field names are the same, although the
247
- # schemas may actually be different.
240
+ # Equality means the schemas and values are the same.
248
241
 
249
242
  def eql?(obj_)
250
- if obj_.kind_of?(String)
251
- obj_ = parse(obj_) rescue nil
243
+ if obj_.kind_of?(::String)
244
+ obj_ = @format.parse(obj_) rescue nil
252
245
  end
253
- if obj_.kind_of?(Versionomy::Value)
254
- if @schema.name != obj_.schema.name || @value != obj_._toplevel_value
255
- false
256
- elsif @subvalue
257
- @subvalue.eql?(obj_._subvalue)
258
- else
259
- true
260
- end
261
- else
262
- false
246
+ return false unless obj_.kind_of?(Value)
247
+ index_ = 0
248
+ obj_.each_field_object do |field_, value_|
249
+ return false if field_ != @field_path[index_] || value_ != @values[field_.name]
250
+ index_ += 1
263
251
  end
252
+ true
264
253
  end
265
254
 
266
255
 
267
256
  # Returns true if this version number is equal to the given verison number.
268
- # Equality means the values and field names are the same, even if the schemas
269
- # are different.
257
+ # Equality means the schemas and values are the same.
270
258
 
271
259
  def ==(obj_)
272
260
  eql?(obj_)
@@ -274,49 +262,42 @@ module Versionomy
274
262
 
275
263
 
276
264
  # Compare this version number with the given version number.
277
- # Version numbers with the same field names and types are comparable,
278
- # even if the schemas are different.
279
265
 
280
266
  def <=>(obj_)
281
- if obj_.kind_of?(String)
282
- obj_ = parse(obj_)
283
- end
284
- if !obj_.kind_of?(Versionomy::Value)
285
- raise ArgumentError, "comparison of Versionomy::Value with #{obj_.class} failed"
267
+ if obj_.kind_of?(::String)
268
+ obj_ = @format.parse(obj_)
286
269
  end
287
- if @schema.name != obj_.schema.name
288
- raise SchemaMismatchError
289
- end
290
- val_ = @schema.compare_values(@value, obj_._toplevel_value)
291
- if val_ == 0
292
- if @subvalue.nil? && obj_._subvalue.nil?
293
- 0
294
- elsif !@subvalue.nil? && !obj_._subvalue.nil?
295
- @subvalue <=> obj_._subvalue
296
- else
297
- raise SchemaMismatchError
298
- end
299
- else
300
- val_
270
+ return nil unless obj_.kind_of?(Value)
271
+ index_ = 0
272
+ obj_.each_field_object do |field_, value_|
273
+ return nil unless field_ == @field_path[index_]
274
+ val_ = field_.compare_values(@values[field_.name], value_)
275
+ return val_ if val_ != 0
276
+ index_ += 1
301
277
  end
278
+ 0
302
279
  end
303
280
 
304
281
 
305
282
  # Compare this version number with the given version number.
306
- # Version numbers with the same field names and types are comparable,
307
- # even if the schemas are different.
308
283
 
309
284
  def <(obj_)
310
- (self <=> obj_) < 0
285
+ val_ = (self <=> obj_)
286
+ unless val_
287
+ raise Errors::SchemaMismatchError
288
+ end
289
+ val_ < 0
311
290
  end
312
291
 
313
292
 
314
293
  # Compare this version number with the given version number.
315
- # Version numbers with the same field names and types are comparable,
316
- # even if the schemas are different.
317
294
 
318
295
  def >(obj_)
319
- (self <=> obj_) > 0
296
+ val_ = (self <=> obj_)
297
+ unless val_
298
+ raise Errors::SchemaMismatchError
299
+ end
300
+ val_ > 0
320
301
  end
321
302
 
322
303
 
@@ -327,15 +308,6 @@ module Versionomy
327
308
  end
328
309
 
329
310
 
330
- class << self
331
-
332
- # :stopdoc:
333
- alias_method :_new, :new
334
- private :new
335
- # :startdoc:
336
-
337
- end
338
-
339
311
  end
340
312
 
341
313
 
@@ -3,7 +3,7 @@
3
3
  # Versionomy version
4
4
  #
5
5
  # -----------------------------------------------------------------------------
6
- # Copyright 2008 Daniel Azuma
6
+ # Copyright 2008-2009 Daniel Azuma
7
7
  #
8
8
  # All rights reserved.
9
9
  #
@@ -36,14 +36,14 @@
36
36
 
37
37
  module Versionomy
38
38
 
39
- VERSION_STRING = '0.0.4'
40
- VERSION = parse(VERSION_STRING)
39
+ # Current gem version, as a frozen string.
40
+ VERSION_STRING = '0.1.0'.freeze
41
+
42
+ # Current gem version, as a Versionomy::Value.
43
+ VERSION = ::Versionomy.parse(VERSION_STRING, ::Versionomy::Formats.standard)
41
44
 
42
45
  end
43
46
 
44
47
 
45
- module Blockenspiel # :nodoc:
46
-
47
- VERSION = Versionomy.parse(VERSION_STRING)
48
-
49
- end
48
+ ::Blockenspiel.const_set(:VERSION,
49
+ ::Versionomy.parse(::Blockenspiel::VERSION_STRING, ::Versionomy::Formats.standard))
data/lib/versionomy.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  # Versionomy entry point
4
4
  #
5
5
  # -----------------------------------------------------------------------------
6
- # Copyright 2008 Daniel Azuma
6
+ # Copyright 2008-2009 Daniel Azuma
7
7
  #
8
8
  # All rights reserved.
9
9
  #
@@ -34,13 +34,28 @@
34
34
  ;
35
35
 
36
36
 
37
- require 'rubygems'
38
- require 'blockenspiel'
37
+ begin
38
+ require 'blockenspiel'
39
+ rescue LoadError
40
+ require 'rubygems'
41
+ require 'blockenspiel'
42
+ end
39
43
 
40
- require File.expand_path("#{File.dirname(__FILE__)}/versionomy/errors.rb")
41
- require File.expand_path("#{File.dirname(__FILE__)}/versionomy/schema.rb")
42
- require File.expand_path("#{File.dirname(__FILE__)}/versionomy/value.rb")
43
- require File.expand_path("#{File.dirname(__FILE__)}/versionomy/format.rb")
44
- require File.expand_path("#{File.dirname(__FILE__)}/versionomy/standard.rb")
45
- require File.expand_path("#{File.dirname(__FILE__)}/versionomy/interface.rb")
46
- require File.expand_path("#{File.dirname(__FILE__)}/versionomy/version.rb")
44
+
45
+ dir_ = File.expand_path('versionomy', File.dirname(__FILE__))
46
+
47
+ includes_ = [
48
+ 'errors',
49
+ 'schema',
50
+ 'schema/field',
51
+ 'schema/wrapper',
52
+ 'format',
53
+ 'format/base',
54
+ 'format/delimiter',
55
+ 'formats',
56
+ 'formats/standard',
57
+ 'value',
58
+ 'interface',
59
+ 'version',
60
+ ]
61
+ includes_.each{ |file_| require "#{dir_}/#{file_}" }
@@ -0,0 +1,66 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Versionomy parsing tests on standard schema
4
+ #
5
+ # This file contains tests for parsing on the standard schema
6
+ #
7
+ # -----------------------------------------------------------------------------
8
+ # Copyright 2008-2009 Daniel Azuma
9
+ #
10
+ # All rights reserved.
11
+ #
12
+ # Redistribution and use in source and binary forms, with or without
13
+ # modification, are permitted provided that the following conditions are met:
14
+ #
15
+ # * Redistributions of source code must retain the above copyright notice,
16
+ # this list of conditions and the following disclaimer.
17
+ # * Redistributions in binary form must reproduce the above copyright notice,
18
+ # this list of conditions and the following disclaimer in the documentation
19
+ # and/or other materials provided with the distribution.
20
+ # * Neither the name of the copyright holder, nor the names of any other
21
+ # contributors to this software, may be used to endorse or promote products
22
+ # derived from this software without specific prior written permission.
23
+ #
24
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
+ # POSSIBILITY OF SUCH DAMAGE.
35
+ # -----------------------------------------------------------------------------
36
+
37
+
38
+ require 'test/unit'
39
+ require File.expand_path("#{File.dirname(__FILE__)}/../lib/versionomy.rb")
40
+
41
+
42
+ module Versionomy
43
+ module Tests # :nodoc:
44
+
45
+ class TestCustomFormat < Test::Unit::TestCase # :nodoc:
46
+
47
+
48
+ # Test parsing with custom format for patchlevel
49
+
50
+ def test_parsing_custom_patchlevel_format
51
+ format_ = Versionomy.default_format.modified_copy do
52
+ field(:patchlevel, :requires_previous_field => false) do
53
+ recognize_number(:delimiter_regexp => '\s?sp', :default_delimiter => ' SP')
54
+ end
55
+ end
56
+ value1_ = Versionomy.parse('2008 SP2', format_)
57
+ assert_equal(2, value1_.patchlevel)
58
+ value2_ = value1_.format.parse('2008 sp3')
59
+ assert_equal(3, value2_.patchlevel)
60
+ end
61
+
62
+
63
+ end
64
+
65
+ end
66
+ end