Ruby Quiz - Challenge #18 - Up-to-Date? Version Check All Your Libraries

Hello,

   It's Friday. Ruby Quiz time! [1] Join us for the third challenge in
the new year in 2020! Here we go:

  Challenge #18 - Up-to-Date? Version Check All Your Libraries [2]

Let's say you have a program that depends on many libraries / gem.
How can you make sure the minimum version requirements are fulfilled?

  Let's look at a real-world example. If you run the pluto feed reader
using the about option:

    $ pluto about

It will print:

Gems versions:
  - pakman 1.1.0
  - fetcher 0.4.5
  - feedparser 2.1.2
  - feedfilter 1.1.1
  - textutils 1.4.0
  - logutils 0.6.1
  - props 1.2.0

  - pluto 1.3.4
  - pluto-models 1.6.0
  - pluto-update 1.6.3
  - pluto-merge 1.1.0
  - pluto-tasks 1.5.3

Now the challenge: Code a `version_check( versions )`
method to version check the minimum requirements and return - if any -
the outdated libraries and versions AND that passes the RubyQuizTest
:-).

def version_check( versions )
  # ...
end

Let's say you run:

outdated = version_check(
  ['pakman',            '1.1.0', Pakman::VERSION],
  ['fetcher',           '0.4.5', Fetcher::VERSION],
  ['feedparser',        '2.1.2', FeedParser::VERSION],
  ['feedfilter',        '1.1.1', FeedFilter::VERSION],
  ['textutils',         '1.4.0', TextUtils::VERSION],
  ['logutils',          '0.6.1', LogKernel::VERSION],
  ['props',             '1.2.0', Props::VERSION],

  ['pluto-models',      '1.5.4', Pluto::VERSION],
  ['pluto-update',      '1.6.3', PlutoUpdate::VERSION],
  ['pluto-merge',       '1.1.0', PlutoMerge::VERSION] )

pp outdated

Where the version check record entries are
1) the name, 2) the minimum requirement and 3) the used version
e.g. `Pluto::VERSION` will become at runtime `1.5.3` or something - depending
on the installation / setup.

If all libraries are up-to-date
this will result in an empty outdated list / array:

[]

And if some libraries are NOT matching the minimum requirement
the version record will get returned in the outdated list / array
resulting in, for example:

[['pluto-models',      '1.5.4', '1.5.3'],
 ['pluto-update',      '1.6.3', '1.4.1']]

Note: For the starter level
the version in use MUST always be greater or equal
to the minimum requirement version.

And the minimum requirement might be just a major (e.g. `2` same as `2.x.x`)
or a major plus minor (e.g. `2.1` same as `2.1.x`) version.

To qualify for solving the code challenge / puzzle you must pass the test:

require 'minitest/autorun'

class RubyQuizTest < MiniTest::Test

  def test_version_check

    # 1) name, 2) minimum requirement, 3) used version
    versions_a =
    [['pakman',            '1.1.0', '1.1.1'],
     ['fetcher',           '0.4.5', '0.5.4'],
     ['feedparser',        '2.1.2', '2.2.1'],
     ['feedfilter',        '1.1',   '1.1.14'],
     ['textutils',         '2',     '2.0.5'],
     ['logutils',          '0.6',   '0.6.0'],
     ['props',             '1.2.1', '1.3.0'],

     ['pluto-models',      '1.5.4', '1.5.7'],
     ['pluto-update',      '1.6',   '2.0.0'],
     ['pluto-merge',       '1.1.0', '1.2.0']]

    versions_a_outdated = []

    versions_b =
    [['pakman',            '1.1.0', '1.0.1'],
     ['fetcher',           '0.4.5', '0.5.4'],
     ['feedparser',        '2.1.2', '2.2.1'],
     ['feedfilter',        '1.1',   '1.1.1'],
     ['textutils',         '2',     '1.2.7'],
     ['logutils',          '0.6',   '0.6.0'],
     ['props',             '1.2.1', '1.3.0'],

     ['pluto-models',      '1.5.4', '1.5.7'],
     ['pluto-update',      '1.6',   '1.5.11'],
     ['pluto-merge',       '1.1.0', '1.2.0']]

     versions_b_outdated =
     [['pakman',            '1.1.0', '1.0.1'],
      ['textutils',         '2',     '1.2.7'],
      ['pluto-update',      '1.6',   '1.5.11']]

    assert_equal versions_a_outdated,
                 version_check( versions_a )

    assert_equal versions_b_outdated,
                 version_check( versions_b )
  end
end

Start from scratch or, yes, use any library / gem you can find.

Post your code snippets on the "official" Ruby Quiz Channel,
that is, the ruby-talk mailing list right here.

Happy list processing and version checking with Ruby.

[1] https://github.com/planetruby/quiz
[2] https://github.com/planetruby/quiz/tree/master/018

Gerald Bauer wrote:

Challenge #18 - Up-to-Date? Version Check All Your Libraries
https://github.com/planetruby/quiz/tree/master/018

$ ruby -v test.rb
ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
Run options: --seed 14264
# Running:
.
Finished in 0.004554s, 219.5933 runs/s, 439.1865 assertions/s.
1 runs, 2 assertions, 0 failures, 0 errors, 0 skips

$ cat test.rb
...
def version_check( versions )
  versions.select do |name, min, cur|
    m = min.split(?.).map(&:to_i)
    c = cur.split(?.).map(&:to_i)
    l = [m.size, c.size].max
    o = (m+c).map(&:digits).map(&:size).max
    m.each.with_index.inject(0){|s,(n,i)| s+=n*10**(o*(l-i))} >
    c.each.with_index.inject(0){|s,(n,i)| s+=n*10**(o*(l-i))}
  end
end
...

$ ruby test2.rb
Run options: --seed 13237
# Running:
.
Finished in 0.002300s, 434.7764 runs/s, 869.5527 assertions/s.
1 runs, 2 assertions, 0 failures, 0 errors, 0 skips

$ cat test2.rb
...
def version_check( versions )
  versions.select do |name, min, cur|
    Gem::Version.create(min) > Gem::Version.create(cur)
  end
end
...

Hello,

   Wow. As always thanks for sharing your ruby quiz puzzle solution.
Love the Gem version:

...
def version_check( versions )
  versions.select do |name, min, cur|
    Gem::Version.create(min) > Gem::Version.create(cur)
  end
end
...

   For reference here's my version_check test script:

def version_check( versions )

  outdated = []   ## list of outdated entries

  versions.each do |rec|
      name         = rec[0]
      ##  convert "2.0.17" to  [2,0,17] or
      ##          "2.1"    to  [2,1]
      ## note: use Integer and NOT to_i; will through exception if
version is not a number
      min_version  = rec[1].split( '.' ).map { |part| Integer(part) }
      used_version = rec[2].split( '.' ).map { |part| Integer(part) }

      ## check for minimum version requirement
      min_version.zip( used_version ) do |part|
          min_num  = part[0]
          used_num = part[1] || 0    ## support unlikley edge case
(used_version has less version parts than min_version)
          if used_num > min_num
              ## used version is greater; stop comparing
              break
          elsif used_num == min_num
              ## continue compare with next version number part
              next
          else
              ## used version is smaller; add and report outdated (!!)
entry; stop comparing
              outdated << rec
              break
          end
      end
  end

  outdated
end

  Have a great weekend. Cheers. Prost.

Gerald Bauer wrote:

Love the Gem version:
def version_check( versions )
  versions.select do |name, min, cur|
    Gem::Version.create(min) > Gem::Version.create(cur)
  end
end

Yes, that was much better than this:

def version_check( versions )
  versions.select do |_, m, c|
    [m,c]
    .map{|v|v
    .split(?.)
    .map(&:to_i)}
    .tap{|v|m,c=[v
    .map(&:size),v
    .flatten
    .map(&:digits)
    .map(&:size)]
    .map{|v|v
    .max}}
    .map{|v|v
    .each_with_index
    .inject(0){|s,(n,i)|s+=n*10**(c*(m-i))}}
    .each_cons(2)
    .map{|m,c|m>c}[0]
  end
end

:slight_smile:

Hello,

  Ha. Yes, that version really is a beauty in the do-it-yourself or
"list processing functional pipeline" style way :-).
  Again thanks for sharing. Love it. Cheers. Prost.

PS: If anyone is interested I have wrapped up my humble script into a
rubygem with the original name version-check [1] :slight_smile: for easy (re)use
e.g.:

      require 'version/check'

      version_check( ... )

  that hides all the devils that are in the details.

[1] https://github.com/feedreader/pluto/tree/master/version-check