Class: QB::Package::Version::Leveled

Inherits:
QB::Package::Version show all
Defined in:
lib/qb/package/version/leveled.rb

Overview

An attempt to unify NPM and Gem version schemes to a reasonable extend, and hopefully cover whatever else the cat may drag in.

Intended to be immutable for practical purposes.

Defined Under Namespace

Modules: Types

Constant Summary collapse

DEV =
'dev'
RC =
'rc'
RELEASE =
'release'
LEVELS =
Set[ DEV, RC, RELEASE ].freeze

Constants inherited from QB::Package::Version

IDENTIFIER_SEPARATOR, MIXED_SEGMENT, NAME_SEGMENT, NUMBER_IDENTIFIER_RE, NUMBER_SEGMENT, POSSIBLE_VERSION_RE

Constants included from Util::DockerMixin

Util::DockerMixin::DOCKER_TAG_MAX_CHARACTERS, Util::DockerMixin::DOCKER_TAG_VALID_RE

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from QB::Package::Version

#<=>, #==, #build?, #build_commit, #build_dirty?, #build_version, #docker_tag, #eql?, extract, from, from_string, #hash, #level?, #prerelease?, #prerelease_version, #release, #release?, #release_version, #semver, #to_a, #to_s, to_time_segment

Methods included from Util::DockerMixin

included

Constructor Details

#initialize(**values) ⇒ Leveled

Construct a new Version



120
121
122
123
124
# File 'lib/qb/package/version/leveled.rb', line 120

def initialize **values
  # Just to do the type check...
  self.class.level_for! **values
  super **values
end

Class Method Details

.level_for(prerelease: [], build: [], **etc) ⇒ nil, 'dev' | 'rc' | 'release'

Get the level for version prop values. Returns nil if they are not "leveled".

Parameters:

  • prerelease: (Array<String | Integer>) (defaults to: [])

    The prerelease segments of the version.

  • build: (Array<String | Integer>) (defaults to: [])

    The build segments of the version.

  • **etc (Hash<Symbol, Object>)

    Really, anything, but meant to allow you to just pass all QB::Package::Version prop values to the method.

Returns:

  • (nil)

    If the prop values don't have a level.

  • ('dev' | 'rc' | 'release')

    If the values do have a level.



78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/qb/package/version/leveled.rb', line 78

def self.level_for prerelease: [], build: [], **etc
  return RELEASE if prerelease.empty?
  
  return DEV if prerelease[0] == DEV
  
  if  prerelease[0] == RC &&
      prerelease.length == 2 &&
      t.non_neg_int.test( prerelease[1] )
    return RC
  end
  
  nil
end

.level_for!(**values) ⇒ nil, 'dev' | 'rc' | 'release'

Just like level_for but raises if the props don't represent a valid level.

Parameters:

  • prerelease: (Array<String | Integer>)

    The prerelease segments of the version.

  • build: (Array<String | Integer>)

    The build segments of the version.

  • **etc (Hash<Symbol, Object>)

    Really, anything, but meant to allow you to just pass all QB::Package::Version prop values to the method.

Returns:

  • (nil)

    If the prop values don't have a level.

  • ('dev' | 'rc' | 'release')

    If the values do have a level.

Raises:

  • (ArgumentError)

    If the prop values don't represent a version level.



102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/qb/package/version/leveled.rb', line 102

def self.level_for! **values
  level_for( **values ).tap { |response|
    if response.nil?
      raise ArgumentError.new binding.erb <<-END
        Prop values not valid for a leveled version:
        
            <%= values.pretty_inspect %>
        
      END
    end
  }
end

Instance Method Details

#dev?Boolean

Returns True if this version is a dev prerelease (first prerelease element is 'dev').

Returns:

  • (Boolean)

    True if this version is a dev prerelease (first prerelease element is 'dev').



141
142
143
# File 'lib/qb/package/version/leveled.rb', line 141

def dev?
  level == DEV
end

#levelObject

Instance Methods



130
131
132
133
134
# File 'lib/qb/package/version/leveled.rb', line 130

def level
  self.class.level_for \
    prerelease: prerelease,
    build: build
end

#rc?Boolean

Returns True if this version is a release candidate (first prerelease element is 'rc').

Returns:

  • (Boolean)

    True if this version is a release candidate (first prerelease element is 'rc').



150
151
152
# File 'lib/qb/package/version/leveled.rb', line 150

def rc?
  level == RC
end

#transition_to(level, **options) ⇒ return_type

TODO:

Document bump method.

Returns @todo Document return value.

Parameters:

  • arg_name (type)

    @todo Add name param description.

Returns:

  • (return_type)

    @todo Document return value.



283
284
285
286
287
288
289
290
291
292
# File 'lib/qb/package/version/leveled.rb', line 283

def transition_to level, **options
  Types.level.check level.to_s
  
  method_name = "transition_to_#{ level }"
  if options.empty?
    public_send method_name
  else
    public_send method_name, **options
  end
end

#transition_to_dev(inc: :patch) ⇒ return_type

TODO:

Document bump_dev method.

Returns @todo Document return value.

Parameters:

  • arg_name (type)

    @todo Add name param description.

Returns:

  • (return_type)

    @todo Document return value.



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/qb/package/version/leveled.rb', line 170

def transition_to_dev inc: :patch
  props = { prerelease: ['dev'] }
  
  t.match level,
    'release', ->(_) {
      succ = public_send( inc ).succ
      
      merge inc => succ, **props
    },
    
    'rc', ->(_) {
      merge **props
    },
    
    'dev',  ->(_) {
      raise QB::VersionError,
        "Version #{ self } is already at `dev` level"
    }
end

#transition_to_rc(existing_versions: nil) ⇒ return_type

Transition to next release-candidate version.

This is a little tricky because we need to know what the last rc version was, which is not in the version in most cases.

Parameters:

  • existing_versions: (nil | String | Array) (defaults to: nil)

    Required when transitioning from dev level; ignored from rc and release levels.

    When transitioning from dev to rc we need to know what rc.X versions have already been used in order to figure out the correct next one.

    Value details:

    • nil - Default value; fine when #level is release or rc. An ArgumentError will be raised if #level is dev.

    • String -

Returns:

  • (return_type)

    @todo Document return value.



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/qb/package/version/leveled.rb', line 214

def transition_to_rc existing_versions: nil
  t.match level,
    'release', ->(_) {
      raise QB::VersionError,
        "Can not transition from `release` to `rc` levels (for #{ self })"
    },
    
    'rc', ->(_) {
      merge prerelease: ['rc', prerelease[1].succ]
    },
    
    'dev', ->(_) {
      if existing_versions.nil?
        raise ArgumentError.squished <<-END
          Can't bump to next rc version without knowing what rc versions have
          already been used.
        END
      elsif existing_versions.is_a? String
        existing_versions = self.class.extract existing_versions
      end
      
      last_existing_rc = existing_versions.
        select { |version|
          version.rc? && version.release == release
        }.
        sort.
        last
      
      rc_number = if last_existing_rc.nil?
        0
      else
        last_existing_rc.prerelease[1].succ
      end
      
      merge prerelease: ['rc', rc_number]
    }
end

#transition_to_releaseQB::Package::Version

TODO:

Document transition_to_release method.



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/qb/package/version/leveled.rb', line 257

def transition_to_release
  t.match level,
    'release', ->(_) {
      raise QB::VersionError,
        "Version #{ self } is already at `release` level"
    },
    
    'dev', ->(_) {
      raise QB::VersionError,
        "Can not transition from `dev` to `release` levels (for #{ self })"
    },
    
    'rc', ->(_) {
      release_version
    }
end