Class: Indexer::Version::Number

Inherits:
Object
  • Object
show all
Includes:
Comparable, Enumerable
Defined in:
lib/indexer/version/number.rb

Overview

Represents a versiou number. Developer SHOULD use three point SemVer standard, but this class is mildly flexible in it's support for variations.

See Also:

Constant Summary collapse

STATES =

Recognized build states in order of completion. This is only used when by #bump_state.

['alpha', 'beta', 'pre', 'rc']

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*points) ⇒ Number

Creates a new version.

Parameters:

  • points (Array)

    version points



24
25
26
27
28
29
30
# File 'lib/indexer/version/number.rb', line 24

def initialize(*points)
  @crush = false
  points.map! do |point|
    sane_point(point)
  end
  @tuple = points.flatten.compact
end

Class Method Details

.[](*args) ⇒ Object

Shortcut for creating a new verison number given segmented elements.

VersionNumber[1,0,0].to_s #=> "1.0.0"

VersionNumber[1,0,0,:pre,2].to_s #=> "1.0.0.pre.2"



41
42
43
# File 'lib/indexer/version/number.rb', line 41

def self.[](*args)
  new(*args)
end

.cmp(version1, version2) ⇒ Object



66
67
68
# File 'lib/indexer/version/number.rb', line 66

def self.cmp(version1, version2)
  # TODO: class level compare might be handy
end

.parse(version) ⇒ Version

Parses a version string.

Parameters:

  • string (String)

    The version string.

Returns:

  • (Version)

    The parsed version.



54
55
56
57
58
59
60
61
62
63
# File 'lib/indexer/version/number.rb', line 54

def self.parse(version)
  case version
  when String
    new(*version.split('.'))
  when Number #self.class
    new(*version.to_a)
  else
    new(*version.to_ary)  #to_a) ?
  end
end

Instance Method Details

#<=>(other) ⇒ Object

Compare versions.



251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/indexer/version/number.rb', line 251

def <=>(other)
  [@tuple.size, other.size].max.times do |i|
    p1, p2 = (@tuple[i] || 0), (other[i] || 0)
    # this is bit tricky, basically a string < integer.
    if p1.class != p2.class
      cmp = p2.to_s <=> p1.to_s
    else
      cmp = p1 <=> p2
    end
    return cmp unless cmp == 0
  end
  #(@tuple.size <=> other.size) * -1
  return 0
end

#=~(other) ⇒ Object

For pessimistic constraint (like '~>' in gems).

FIXME: Ensure it can handle trailing state.



269
270
271
272
273
# File 'lib/indexer/version/number.rb', line 269

def =~(other)
  upver = other.bump(:last)
  #@segments >= other and @segments < upver
  self >= other and self < upver
end

#[](index) ⇒ Object

Fetch a sepecific segement by index number. In no value is found at that position than zero (0) is returned instead.

v = Version::Number[1,2,0] v[0] #=> 1 v[1] #=> 2 v[3] #=> 0 v[4] #=> 0

Zero is returned instead of +nil+ to make different version numbers easier to compare.



187
188
189
# File 'lib/indexer/version/number.rb', line 187

def [](index)
  @tuple.fetch(index,0)
end

#alpha?Boolean

Returns:

  • (Boolean)


154
155
156
157
# File 'lib/indexer/version/number.rb', line 154

def alpha?
  s = status.dowcase
  s == 'alpha' or s == 'a'
end

#beta?Boolean

Returns:

  • (Boolean)


160
161
162
163
# File 'lib/indexer/version/number.rb', line 160

def beta?
  s = status.dowcase
  s == 'beta' or s == 'b'
end

#buildObject

The build.



86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/indexer/version/number.rb', line 86

def build
  if b = state_index
    str = @tuple[b..-1].join('.')
    str = crush_point(str) if crush?
    str
  elsif @tuple[3].nil?
    nil
  else
    str = @tuple[3..-1].join('.')
    str = crush_point(str) if crush?
    str
  end
end

#build=(point) ⇒ Object

Parameters:

  • build (Integer, nil)

    (nil) The build version number.



142
143
144
# File 'lib/indexer/version/number.rb', line 142

def build=(point)
  @tuple = @tuple[0...state_index] + sane_point(point)
end

#build_numberObject

Return the state revision count. This is the number that occurs after the state.

Version::Number[1,2,0,:rc,4].build_number #=> 4



114
115
116
117
118
119
120
# File 'lib/indexer/version/number.rb', line 114

def build_number #revision
  if i = state_index
    self[i+1] || 0
  else
    nil
  end
end

#bump(which = :patch) ⇒ Object

Bump the version returning a new version number object. Select +which+ segement to bump by name: +major+, +minor+, +patch+, +state+, +build+ and also +last+.

Version::Number[1,2,0].bump(:patch).to_s #=> "1.2.1"

Version::Number[1,2,1].bump(:minor).to_s #=> "1.3.0"

Version::Number[1,3,0].bump(:major).to_s #=> "2.0.0"

Version::Number[1,3,0,:pre,1].bump(:build).to_s #=> "1.3.0.pre.2"

Version::Number[1,3,0,:pre,2].bump(:state).to_s #=> "1.3.0.rc.1"



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/indexer/version/number.rb', line 316

def bump(which=:patch)
  case which.to_sym
  when :major, :first
    bump_major
  when :minor
    bump_minor
  when :patch
    bump_patch
  when :state, :status
    bump_state
  when :build
    bump_build
  when :revision
    bump_revision
  when :last
    bump_last
  else
    self.class.new(@tuple.dup.compact)
  end
end

#bump_buildObject



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/indexer/version/number.rb', line 370

def bump_build
  if i = state_index
    if i == @tuple.size - 1
      v = @tuple + [1]
    else
      v = @tuple[0...-1] + [inc(@tuple.last)]
    end
  else
    if @tuple.size <= 3
      v = @tuple + [1]
    else
      v = @tuple[0...-1] + [inc(@tuple.last)]
    end
  end
  self.class.new(v.compact)
end

#bump_build_numberObject

revision



388
389
390
391
392
393
394
395
# File 'lib/indexer/version/number.rb', line 388

def bump_build_number #revision
  if i = state_index
    v = @tuple[0...-1] + [inc(@tuple.last)]
  else
    v = @tuple[0..2] + ['alpha', 1]
  end
  self.class.new(v.compact)
end

#bump_lastObject



398
399
400
401
# File 'lib/indexer/version/number.rb', line 398

def bump_last
  v = @tuple[0...-1] + [inc(@tuple.last)]
  self.class.new(v.compact)
end

#bump_majorObject



338
339
340
# File 'lib/indexer/version/number.rb', line 338

def bump_major
  self.class[inc(major), 0, 0]
end

#bump_minorObject



343
344
345
# File 'lib/indexer/version/number.rb', line 343

def bump_minor
  self.class[major, inc(minor), 0]
end

#bump_patchObject



348
349
350
# File 'lib/indexer/version/number.rb', line 348

def bump_patch
  self.class[major, minor, inc(patch)]
end

#bump_stateObject Also known as: bump_status



353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/indexer/version/number.rb', line 353

def bump_state
  if i = state_index
    if n = inc(@tuple[i])
      v = @tuple[0...i] + [n] + (@tuple[i+1] ? [1] : [])
    else
      v = @tuple[0...i]
    end
  else
    v = @tuple.dup
  end
  self.class.new(v.compact)
end

#crush?Boolean

Does the version string representation compact string segments with the subsequent number segement?

Returns:

  • (Boolean)


423
424
425
# File 'lib/indexer/version/number.rb', line 423

def crush?
  @crush
end

#crush_point(string) ⇒ Object (private)

Take a point string rendering of a version and crush it!



465
466
467
# File 'lib/indexer/version/number.rb', line 465

def crush_point(string)
  string.gsub(/(^|\.)(\D+)\.(\d+)(\.|$)/, '\2\3')
end

#each(&block) ⇒ Object

Iterate of each segment of the version. This allows all enumerable methods to be used.

Version::Number[1,2,3].map{|i| i + 1} #=> [2,3,4]

Though keep in mind that the state segment is not a number (and techincally any segment can be a string instead of an integer).



284
285
286
# File 'lib/indexer/version/number.rb', line 284

def each(&block)
  @tuple.each(&block)
end

#inc(val) ⇒ Object (private)

Segement incrementor.



485
486
487
488
489
490
491
# File 'lib/indexer/version/number.rb', line 485

def inc(val)
  if i = STATES.index(val.to_s)
    STATES[i+1]
  else
    val.succ
  end
end

#inspectObject

Returns a String detaling the version number. Essentially it is the same as #to_s.

VersionNumber[1,2,0].inspect #=> "1.2.0"



225
226
227
# File 'lib/indexer/version/number.rb', line 225

def inspect
  to_s
end

#majorObject

Major version number



71
72
73
# File 'lib/indexer/version/number.rb', line 71

def major
  (state_index && state_index == 0) ? nil : self[0]
end

#major=(number) ⇒ Object

Parameters:

  • major (Integer)

    The major version number.



124
125
126
# File 'lib/indexer/version/number.rb', line 124

def major=(number)
  @tuple[0] = number.to_i
end

#match?(*constraints) ⇒ Boolean

Does this version match a given constraint? The constraint is a String

in the form of "operator number".

TODO: match? will change as Constraint class is improved. ++

Returns:

  • (Boolean)


432
433
434
435
436
# File 'lib/indexer/version/number.rb', line 432

def match?(*constraints)
  constraints.all? do |c|
    Constraint.constraint_lambda(c).call(self)
  end
end

#minorObject

Minor version number



76
77
78
# File 'lib/indexer/version/number.rb', line 76

def minor
  (state_index && state_index <= 1) ? nil : self[1]
end

#minor=(number) ⇒ Object

Parameters:

  • minor (Integer, nil)

    The minor version number.



130
131
132
# File 'lib/indexer/version/number.rb', line 130

def minor=(number)
  @tuple[1] = number.to_i
end

#patchObject

Patch version number



81
82
83
# File 'lib/indexer/version/number.rb', line 81

def patch
  (state_index && state_index <= 2) ? nil : self[2]
end

#patch=(number) ⇒ Object

Parameters:

  • patch (Integer, nil)

    The patch version number.



136
137
138
# File 'lib/indexer/version/number.rb', line 136

def patch=(number)
  @tuple[2] = number.to_i
end

#prerelease?Boolean

Returns:

  • (Boolean)


166
167
168
# File 'lib/indexer/version/number.rb', line 166

def prerelease?
  status == 'pre'
end

#release_candidate?Boolean

Returns:

  • (Boolean)


171
172
173
# File 'lib/indexer/version/number.rb', line 171

def release_candidate?
  status == 'rc'
end

#restate(state, revision = 1) ⇒ Object

Return a new version have the same major, minor and patch levels, but with a new state and revision count.

Version::Number[1,2,3].restate(:pre,2).to_s #=> "1.2.3.pre.2"

Version::Number[1,2,3,:pre,2].restate(:rc,4).to_s #=> "1.2.3.rc.4"



412
413
414
415
416
417
418
419
# File 'lib/indexer/version/number.rb', line 412

def restate(state, revision=1)
  if i = state_index
    v = @tuple[0...i] + [state.to_s] + [revision]
  else
    v = @tuple[0...3] + [state.to_s] + [revision]
  end
  self.class.new(v)
end

#sane_point(point) ⇒ Object (private)

Convert a segment into an integer or string.



446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/indexer/version/number.rb', line 446

def sane_point(point)      
  point = point.to_s if Symbol === point
  case point
  when Integer
    point
  when /^\d+$/
    point.to_i
  when /^(\d+)(\w+)(\d+)$/
    @crush = true
    [$1.to_i, $2, $3.to_i]
  when /^(\w+)(\d+)$/
    @crush = true
    [$1, $2.to_i]
  else
    point
  end
end

#sizeObject

Return the number of version segements.

Version::Number[1,2,3].size #=> 3



293
294
295
# File 'lib/indexer/version/number.rb', line 293

def size
  @tuple.size
end

#stable?Boolean Also known as: stable_release?

Returns:

  • (Boolean)


147
148
149
# File 'lib/indexer/version/number.rb', line 147

def stable?
  build.nil?
end

#stateObject Also known as: status



101
102
103
# File 'lib/indexer/version/number.rb', line 101

def state
  state_index ? @tuple[state_index] : nil
end

#state_indexObject (private)

Return the index of the first recognized state.

VersionNumber[1,2,3,'pre',3].state_index #=> 3

You might ask why this is needed, since the state position should always be 3. However, there isn't always a state entry, which means this method will return +nil+, and we also leave open the potential for extra-long version numbers --though we do not recommend the idea, it is possible.



480
481
482
# File 'lib/indexer/version/number.rb', line 480

def state_index
  @tuple.index{ |s| String === s }
end

#to_aObject

Returns a duplicate of the underlying version tuple.



193
194
195
# File 'lib/indexer/version/number.rb', line 193

def to_a
  @tuple.dup
end

#to_sObject

Converts version to a dot-separated string.

Version::Number[1,2,0].to_s #=> "1.2.0"

TODO: crush



203
204
205
206
207
# File 'lib/indexer/version/number.rb', line 203

def to_s         
  str = @tuple.compact.join('.')
  str = crush_point(str) if crush?
  return str
end

#to_strObject

This method is the same as #to_s. It is here becuase File.join calls it instead of #to_s.

VersionNumber[1,2,0].to_str #=> "1.2.0"



215
216
217
# File 'lib/indexer/version/number.rb', line 215

def to_str
  to_s
end

#to_yaml(opts = {}) ⇒ String

Converts the version to YAML.

-- TODO: Should this be here? ++

Parameters:

  • opts (Hash) (defaults to: {})

    Options supporte by YAML.

Returns:

  • (String)

    The resulting YAML.



241
242
243
# File 'lib/indexer/version/number.rb', line 241

def to_yaml(opts={})
  to_s.to_yaml(opts)
end