Class: Versionomy::Value

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/versionomy/value.rb

Overview

Version number value

A version number value is an ordered list of values, corresponding to an ordered list of fields defined by a schema. For example, if the schema is a simple one of the form “major.minor.tiny”, then the the version number “1.4.2” would have the values [1, 4, 2] in that order, corresponding to the fields [:major, :minor, :tiny].

Version number values are comparable with other values that have an equivalent schema.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(values_, format_, unparse_params_ = nil) ⇒ Value

Create a value, given a hash or array of values, and a format. Both these parameters are required.

The values should either be a hash of field names and values, or an array of values that will be interpreted in field order.

You can also optionally provide default unparsing parameters for the value.


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/versionomy/value.rb', line 70

def initialize(values_, format_, unparse_params_=nil)
  unless values_.kind_of?(::Hash) || values_.kind_of?(::Array)
    raise ::ArgumentError, "Expected hash or array but got #{values_.class}"
  end
  @_format = format_
  @_unparse_params = unparse_params_
  @_field_path = []
  @_values = {}
  values_ = _canonicalize_values_hash(values_) if values_.kind_of?(::Hash)
  field_ = @_format.schema.root_field
  while field_
    value_ = values_.kind_of?(::Hash) ? values_[field_.name] : values_.shift
    value_ = value_ ? field_.canonicalize_value(value_) : field_.default_value
    @_field_path << field_
    @_values[field_.name] = value_
    field_ = field_.child(value_)
  end
  modules_ = @_format.schema.modules
  extend(*modules_) if modules_.size > 0
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(symbol_) ⇒ Object

Field values may be retrieved by calling them as methods.


413
414
415
# File 'lib/versionomy/value.rb', line 413

def method_missing(symbol_)
  self[symbol_] || super
end

Class Method Details

.yaml_new(klass_, tag_, data_) ⇒ Object


499
500
501
502
503
504
505
506
507
508
509
510
# File 'lib/versionomy/value.rb', line 499

def self.yaml_new(klass_, tag_, data_)  # :nodoc:
  unless data_.kind_of?(::Hash)
    raise ::YAML::TypeError, "Invalid version format: #{data_.inspect}"
  end
  format_ = Format.get(data_['format'], true)
  value_ = data_['value']
  if value_
    format_.parse(value_, data_['parse_params'])
  else
    Value.new(data_['fields'], format_, data_['unparse_params'])
  end
end

Instance Method Details

#<(obj_) ⇒ Object

Compare this version number with the given version number. The comparison may succeed even if the two have different schemas, if the RHS can be converted to the LHS's format.


386
387
388
389
390
391
392
# File 'lib/versionomy/value.rb', line 386

def <(obj_)
  val_ = (self <=> obj_)
  unless val_
    raise Errors::SchemaMismatchError
  end
  val_ < 0
end

#<=>(obj_) ⇒ Object

Compare this version number with the given version number, returning 0 if the two are value-equal, a negative number if the RHS is greater, or a positive number if the LHS is greater. The comparison may succeed even if the two have different schemas, if the RHS can be converted to the LHS's format.


362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/versionomy/value.rb', line 362

def <=>(obj_)
  if obj_.kind_of?(::String)
    obj_ = @_format.parse(obj_)
  end
  return nil unless obj_.kind_of?(Value)
  if obj_.schema != @_format.schema
    begin
      obj_ = obj_.convert(@_format)
    rescue
      return nil
    end
  end
  obj_.each_field_object do |field_, value_|
    val_ = field_.compare_values(@_values[field_.name], value_)
    return val_ if val_ != 0
  end
  0
end

#==(obj_) ⇒ Object

Returns true if this version number is value-equal to the given number. This type of equality means that they are equivalent, or that it is possible to convert the RHS to the LHS's format, and that they would be equivalent after such a conversion has taken place. Note that this is different from the definition of eql?.


351
352
353
# File 'lib/versionomy/value.rb', line 351

def ==(obj_)
  (self <=> obj_) == 0
end

#>(obj_) ⇒ Object

Compare this version number with the given version number. The comparison may succeed even if the two have different schemas, if the RHS can be converted to the LHS's format.


399
400
401
402
403
404
405
# File 'lib/versionomy/value.rb', line 399

def >(obj_)
  val_ = (self <=> obj_)
  unless val_
    raise Errors::SchemaMismatchError
  end
  val_ > 0
end

#[](field_) ⇒ Object

Returns the value of the given field, or nil if the field is not recognized. The field may be specified as a field object, field name, or field index.


202
203
204
# File 'lib/versionomy/value.rb', line 202

def [](field_)
  @_values[_interpret_field(field_)]
end

#_inspectObject

:nodoc:


101
102
103
104
# File 'lib/versionomy/value.rb', line 101

def _inspect  # :nodoc:
  "#<#{self.class}:0x#{object_id.to_s(16)} " +
    @_field_path.map{ |field_| "#{field_.name}=#{@_values[field_.name].inspect}" }.join(' ')
end

#bump(field_) ⇒ Object

Returns a new version number created by bumping the given field. The field may be specified as a field object, field name, or field index. Returns self unchanged if the field was not recognized or could not be modified.


228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/versionomy/value.rb', line 228

def bump(field_)
  name_ = _interpret_field(field_)
  return self unless name_ && @_values.include?(name_)
  values_ = []
  @_field_path.each do |fld_|
    oldval_ = @_values[fld_.name]
    if fld_.name == name_
      newval_ = fld_.bump_value(oldval_)
      return self if newval_ == oldval_
      values_ << newval_
      return Value.new(values_, @_format, @_unparse_params)
    else
      values_ << oldval_
    end
  end
  self
end

#change(values_ = {}, unparse_params_ = {}) ⇒ Object

Returns a new version number created by cloning this version number and changing the given field values.

You should pass in a hash of field names to values. These are the fields to modify; any other fields will be left alone, unless they are implicitly changed by the modifications you are making. For example, changing the :release_type on a value using the standard format, may change which fields are present in the resulting value.

You may also pass a delta hash to modify the unparse params stored in the value.


281
282
283
284
285
# File 'lib/versionomy/value.rb', line 281

def change(values_={}, unparse_params_={})
  unparse_params_ = @_unparse_params.merge(unparse_params_) if @_unparse_params
  values_ = _canonicalize_values_hash(values_)
  Value.new(@_values.merge(values_), @_format, unparse_params_)
end

#convert(format_, convert_params_ = nil) ⇒ Object

Attempts to convert this value to the given format, and returns the resulting value.

Raises Versionomy::Errors::ConversionError if the value could not be converted.


294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/versionomy/value.rb', line 294

def convert(format_, convert_params_=nil)
  if format_.kind_of?(::String) || format_.kind_of?(::Symbol)
    format_ = Format.get(format_)
  end
  return self if @_format == format_
  from_schema_ = @_format.schema
  to_schema_ = format_.schema
  if from_schema_ == to_schema_
    return Value.new(@_values, format_, convert_params_)
  end
  conversion_ = Conversion.get(from_schema_, to_schema_)
  if conversion_
    conversion_.convert_value(self, format_, convert_params_)
  else
    standard_format_ = Format.get(:standard)
    conversion1_ = Conversion.get(from_schema_, standard_format_)
    conversion2_ = Conversion.get(standard_format_, to_schema_)
    if conversion1_ && conversion2_
      value_ = conversion1_.convert_value(self, standard_format_, convert_params_)
      conversion2_.convert_value(value_, format_, convert_params_)
    else
      raise Errors::UnknownConversionError
    end
  end
end

#each_fieldObject

Iterates over each field, in field order, yielding the field name and value.


155
156
157
158
159
# File 'lib/versionomy/value.rb', line 155

def each_field
  @_field_path.each do |field_|
    yield(field_, @_values[field_.name])
  end
end

#each_field_objectObject

Iterates over each field, in field order, yielding the Versionomy::Schema::Field object and value.


165
166
167
168
169
# File 'lib/versionomy/value.rb', line 165

def each_field_object  # :nodoc:
  @_field_path.each do |field_|
    yield(field_, @_values[field_.name])
  end
end

#encode_with(coder_) ⇒ Object


478
479
480
481
482
483
484
485
486
487
488
# File 'lib/versionomy/value.rb', line 478

def encode_with(coder_)  # :nodoc:
  data_ = marshal_dump
  coder_['format'] = data_[0]
  if data_[1].kind_of?(::String)
    coder_['value'] = data_[1]
    coder_['parse_params'] = data_[2] if data_[2]
  else
    coder_['fields'] = data_[1]
    coder_['unparse_params'] = data_[2] if data_[2]
  end
end

#eql?(obj_) ⇒ Boolean

Returns true if this version number is equivalent to the given number. This type of equality means their schemas are compatible and their field values are equal. Note that this is different from the definition of ==.

Returns:

  • (Boolean)

331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/versionomy/value.rb', line 331

def eql?(obj_)
  if obj_.kind_of?(::String)
    obj_ = @_format.parse(obj_) rescue nil
  end
  return false unless obj_.kind_of?(Value)
  index_ = 0
  obj_.each_field_object do |field_, value_|
    return false if field_ != @_field_path[index_] || value_ != @_values[field_.name]
    index_ += 1
  end
  true
end

#field_namesObject

Returns an array of recognized field names for this value, in field order. This is the order of the fields actually present in this value, in order from most to least significant.


176
177
178
# File 'lib/versionomy/value.rb', line 176

def field_names
  @_field_path.map{ |field_| field_.name }
end

#formatObject

Return the format defining the schema and formatting/parsing of this version number.


140
141
142
# File 'lib/versionomy/value.rb', line 140

def format
  @_format
end

#has_field?(field_) ⇒ Boolean

Returns true if this value contains the given field, which may be specified as a field object, name, or index.

Returns:

  • (Boolean)

184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/versionomy/value.rb', line 184

def has_field?(field_)
  case field_
  when Schema::Field
    @_field_path.include?(field_)
  when ::Integer
    @_field_path.size > field_ && field_ >= 0
  when ::String, ::Symbol
    @_values.has_key?(@_format.schema.canonical_name(field_))
  else
    raise ::ArgumentError
  end
end

#hashObject

:nodoc:


321
322
323
# File 'lib/versionomy/value.rb', line 321

def hash  # :nodoc:
  @_hash ||= @_values.hash
end

#init_with(coder_) ⇒ Object


466
467
468
469
470
471
472
473
474
475
# File 'lib/versionomy/value.rb', line 466

def init_with(coder_)  # :nodoc:
  format_ = Format.get(coder_['format'], true)
  value_ = coder_['value']
  if value_
    value_ = format_.parse(value_, coder_['parse_params'])
    initialize(value_.values_array, format_, value_.unparse_params)
  else
    initialize(coder_['fields'], format_, coder_['unparse_params'])
  end
end

#inspectObject

:nodoc:


92
93
94
95
96
97
98
99
# File 'lib/versionomy/value.rb', line 92

def inspect  # :nodoc:
  begin
    str_ = unparse
    "#<#{self.class}:0x#{object_id.to_s(16)} #{str_.inspect}>"
  rescue Errors::UnparseError
    _inspect
  end
end

#marshal_dumpObject

Marshal this version number


424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/versionomy/value.rb', line 424

def marshal_dump
  format_name_ = Format.canonical_name_for(@_format, true)
  unparsed_data_ = nil
  if @_format.respond_to?(:unparse_for_serialization)
    unparsed_data_ = @_format.unparse_for_serialization(self) rescue nil
  end
  unparsed_data_ ||= @_format.unparse(self) rescue nil
  data_ = [format_name_]
  case unparsed_data_
  when ::Array
    data_ << unparsed_data_[0]
    data_ << unparsed_data_[1] if unparsed_data_[1]
  when ::String
    data_ << unparsed_data_
  else
    data_ << values_array
    data_ << @_unparse_params if @_unparse_params
  end
  data_
end

#marshal_load(data_) ⇒ Object

Unmarshal this version number.


446
447
448
449
450
451
452
453
454
# File 'lib/versionomy/value.rb', line 446

def marshal_load(data_)
  format_ = Format.get(data_[0], true)
  if data_[1].kind_of?(::String)
    val_ = format_.parse(data_[1], data_[2])
    initialize(val_.values_array, format_, val_.unparse_params)
  else
    initialize(data_[1], format_, data_[2])
  end
end

#reset(field_) ⇒ Object

Returns a new version number created by resetting the given field. The field may be specified as a field object, field name, or field index. Returns self unchanged if the field was not recognized or could not be modified.


252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/versionomy/value.rb', line 252

def reset(field_)
  name_ = _interpret_field(field_)
  return self unless name_ && @_values.include?(name_)
  values_ = []
  @_field_path.each do |fld_|
    oldval_ = @_values[fld_.name]
    if fld_.name == name_
      values_ << fld_.default_value
      return Value.new(values_, @_format, @_unparse_params)
    else
      values_ << oldval_
    end
  end
  self
end

#schemaObject

Return the schema defining the structure and semantics of this version number.


132
133
134
# File 'lib/versionomy/value.rb', line 132

def schema
  @_format.schema
end

#to_sObject

Returns a string representation generated by unparsing. If unparsing fails, does not raise Versionomy::Errors::UnparseError, but instead returns the string generated by inspect.


111
112
113
114
115
116
117
# File 'lib/versionomy/value.rb', line 111

def to_s
  begin
    unparse
  rescue Errors::UnparseError
    _inspect
  end
end

#to_yaml(opts_ = {}) ⇒ Object


513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/versionomy/value.rb', line 513

def to_yaml(opts_={})
  data_ = marshal_dump
  ::YAML.quick_emit(nil, opts_) do |out_|
    out_.map(taguri, to_yaml_style) do |map_|
      map_.add('format', data_[0])
      if data_[1].kind_of?(::String)
        map_.add('value', data_[1])
        map_.add('parse_params', data_[2]) if data_[2]
      else
        map_.add('fields', data_[1])
        map_.add('unparse_params', data_[2]) if data_[2]
      end
    end
  end
end

#unparse(params_ = nil) ⇒ Object

Unparse this version number and return a string.

Raises Versionomy::Errors::UnparseError if unparsing failed.


124
125
126
# File 'lib/versionomy/value.rb', line 124

def unparse(params_=nil)
  @_format.unparse(self, params_)
end

#unparse_paramsObject

Return the unparsing parameters for this value. Returns nil if this value was not created using a parser.


148
149
150
# File 'lib/versionomy/value.rb', line 148

def unparse_params
  @_unparse_params ? @_unparse_params.dup : nil
end

#values_arrayObject

Returns the value as an array of field values, in field order. This is the order of the fields actually present in this value, in order from most to least significant.


211
212
213
# File 'lib/versionomy/value.rb', line 211

def values_array
  @_field_path.map{ |field_| @_values[field_.name] }
end

#values_hashObject

Returns the value as a hash of values keyed by field name.


218
219
220
# File 'lib/versionomy/value.rb', line 218

def values_hash
  @_values.dup
end