Jak porównać wersje w Rubim?

120

Jak napisać fragment kodu, aby porównać ciągi niektórych wersji i pobrać najnowszą?

Na przykład ciągi lubię: '0.1', '0.2.1', '0.44'.

user239895
źródło
Musiałem porównać pesymistyczne ograniczenia wersji jakiś czas temu, ale nie chciałem polegać na RubyGems, aby to zrobić, więc napisałem prostą Versionklasę, która robi wszystko, czego potrzebuję: shorts.jeffkreeftmeijer.com/2014/…
jkreeftmeijer

Odpowiedzi:

233
Gem::Version.new('0.4.1') > Gem::Version.new('0.10.1')
grubszy
źródło
14
Gem::Version...Składnia mnie myślał, że muszę zainstalować gem. Ale nie było to wymagane.
Guillaume
Uwaga: powoduje to błąd dotyczący niezdefiniowanej zmiennej „Gem” dla mnie w Ruby 1.x, ale działa zgodnie z oczekiwaniami w Ruby 2.x. W moim przypadku sprawdzałem, czy RUBY_VERSION nie jest Ruby 1.x (nie 2.x), więc właśnie zrobiłem RUBY_VERSION.split ('.') [0] == "1", tak jak zrobili to John Hyland i DigitalRoss.
uliwitness
5
Gem::Dependency.new(nil, '~> 1.4.5').match?(nil, '1.4.6beta4')
levinalex
6
@uliwitness to nie Ruby 1.x vs 2.x; jest 1.8.x vs 1.9+. Ruby do 1.8.x nie zawiera domyślnie rubygemów; potrzebujesz, require 'rubygems'aby uzyskać dostęp do Gemprzestrzeni nazw. Jednak od 1.9 jest to automatycznie uwzględniane.
Mark Reed,
To działało również przy porównywaniu wersji NPM z symbolami wieloznacznymi. +1
deepelement
35

Jeśli chcesz sprawdzić pesymistyczne ograniczenia wersji , możesz użyć Gem :: Dependency w następujący sposób:

Gem::Dependency.new('', '~> 1.4.5').match?('', '1.4.6beta4')
levinalex
źródło
1
Wydaje się, że nowsze wersje wymagają ciągu dla nazwy. Pusty napis działa dobrze, np.Gem::Dependency.new('', '~> 1.4.5').match?('', '1.4.6beta4')
Peter Wagenet
19
class Version < Array
  def initialize s
    super(s.split('.').map { |e| e.to_i })
  end
  def < x
    (self <=> x) < 0
  end
  def > x
    (self <=> x) > 0
  end
  def == x
    (self <=> x) == 0
  end
end
p [Version.new('1.2') < Version.new('1.2.1')]
p [Version.new('1.2') < Version.new('1.10.1')]
DigitalRoss
źródło
3
Podobnie jak niektóre inne odpowiedzi tutaj, wygląda na to, że porównujesz ciągi zamiast liczb, co będzie powodować problemy podczas porównywania wersji, takich jak „0,10” i „0,4”.
John Hyland,
7
Głosowano za zwięzłym rozwiązaniem, które nie wymaga instalowania klejnotu.
JD.
2
Co jest warte: vers = (1..3000000).map{|x| "0.0.#{x}"}; 'ok' puts Time.now; vers.map{|v| ComparableVersion.new(v) }.sort.first; puts Time.now # 24 seconds 2013-10-29 13:36:09 -0700 2013-10-29 13:36:33 -0700 => nil puts Time.now; vers.map{|v| Gem::Version.new(v) }.sort.first; puts Time.now # 41 seconds 2013-10-29 13:36:53 -0700 2013-10-29 13:37:34 -0700 kod blob sprawia, że ​​jest brzydki, ale w zasadzie użycie tego vs Gem :: Version jest około dwa razy szybsze.
Shai
Wersja nie jest jednak tablicą.
Sergio Tulentsev
15

Możesz użyć Versionomyklejnotu (dostępny na github ):

require 'versionomy'

v1 = Versionomy.parse('0.1')
v2 = Versionomy.parse('0.2.1')
v3 = Versionomy.parse('0.44')

v1 < v2  # => true
v2 < v3  # => true

v1 > v2  # => false
v2 > v3  # => false
notnoop
źródło
4
Widziałem to, ale wymagaj ode mnie użycia 2 klejnotów, aby zrobić naprawdę prostą rzecz. Chcę użyć tego jako ostatniego wyboru.
user239895
8
„Nie odkrywaj na nowo koła”. To, że jest proste, nie oznacza, że ​​programista nie włożył w to pracy i nie przemyślał. Użyj klejnotu, przeczytaj kod i ucz się z niego - i przejdź do większych i lepszych rzeczy!
Trevoke,
Zarządzanie zależnościami i utrzymanie wersji to trudny problem, prawdopodobnie znacznie trudniejszy niż zadanie porównania 2 wersji. Całkowicie się zgadzam, że wprowadzenie 2 dodatkowych zależności powinno być w tym przypadku ostatecznością.
kkodev
10

chciałbym zrobić

a1 = v1.split('.').map{|s|s.to_i}
a2 = v2.split('.').map{|s|s.to_i}

Wtedy możesz to zrobić

a1 <=> a2

(i prawdopodobnie wszystkie inne „zwykłe” porównania).

... a jeśli chcesz <lub >test, możesz zrobić np

(a1 <=> a2) < 0

lub zrób więcej zawijania funkcji, jeśli masz na to ochotę.

Carl Smotricz
źródło
1
Array.class_eval {include Comparable} sprawi, że wszystkie tablice będą reagować na <,>, itd. Lub, jeśli chcesz to zrobić tylko z określonymi tablicami: a = [1, 2]; a. rozszerzenie (porównywalne)
Wayne Conrad
4
Problem, który znalazłem w przypadku tego rozwiązania polega na tym, że zwraca ono, że „1.2.0” jest większe niż „1.2”
Maria S
9

Gem::Version to łatwy sposób na przejście tutaj:

%w<0.1 0.2.1 0.44>.map {|v| Gem::Version.new v}.max.to_s
=> "0.44"
Mark Reed
źródło
Znacznie lepsze niż wersjonowanie, które wymaga rozszerzenia c !?
W.Andrew Loe III
nie sądzę, żeby „max” zadziałało .. zgłosi 0,5, aby było większe niż 0,44. Co nie jest prawdą przy porównywaniu wersji Semver.
Flo Woo
2
wydaje się, że zostało to naprawione w najnowszej wersji Gem :: Version. 0,44 jest teraz poprawnie zgłaszane jako wyższe niż 0,5.
Flo Woo,
5

Jeśli chcesz to zrobić ręcznie, bez użycia klejnotów, coś takiego powinno działać, chociaż wygląda to trochę zręcznie.

versions = [ '0.10', '0.2.1', '0.4' ]
versions.map{ |v| (v.split '.').collect(&:to_i) }.max.join '.'

Zasadniczo zamieniasz każdy ciąg wersji na tablicę liczb całkowitych, a następnie używasz operatora porównania tablicy . Możesz wyłamać kroki komponentu, aby uzyskać coś łatwiejszego do wykonania, jeśli jest to kod, który ktoś będzie musiał utrzymywać.

John Hyland
źródło
-1

Miałem ten sam problem, chciałem komparatora wersji bez klejnotów, wymyśliłem to:

def compare_versions(versionString1,versionString2)
    v1 = versionString1.split('.').collect(&:to_i)
    v2 = versionString2.split('.').collect(&:to_i)
    #pad with zeroes so they're the same length
    while v1.length < v2.length
        v1.push(0)
    end
    while v2.length < v1.length
        v2.push(0)
    end
    for pair in v1.zip(v2)
        diff = pair[0] - pair[1]
        return diff if diff != 0
    end
    return 0
end
Wivlaro
źródło