124 votes

Comment comparer les versions en Ruby ?

Comment écrire un morceau de code pour comparer des chaînes de versions et obtenir la plus récente ?

Par exemple, des chaînes de caractères comme : '0.1', '0.2.1', '0.44' .

0 votes

J'ai eu besoin de comparer des contraintes de version pessimistes il y a quelque temps, mais je ne voulais pas dépendre de RubyGems pour le faire, alors j'ai écrit un simple Version qui fait tout ce dont j'ai besoin : shorts.jeffkreeftmeijer.com/2014/

245voto

grosser Points 4300
Gem::Version.new('0.4.1') > Gem::Version.new('0.10.1')

16 votes

El Gem::Version... La syntaxe m'a fait penser que j'aurais besoin d'installer une gemme. Mais ce n'était pas nécessaire.

1 votes

Note : Cette méthode donne une erreur à propos d'une variable indéfinie 'Gem' pour moi sur Ruby 1.x, mais fonctionne comme prévu sur Ruby 2.x. Dans mon cas, je vérifiais que RUBY_VERSION n'était pas Ruby 1.x (et non 2.x), donc j'ai simplement fait RUBY_VERSION.split('.')[0] == "1" comme John Hyland et DigitalRoss le font.

5 votes

Gem::Dependency.new(nil, '~> 1.4.5').match?(nil, '1.4.6beta4')

37voto

levinalex Points 2580

Si vous devez vérifier contraintes de la version pessimiste vous pouvez utiliser Gem::Dependency comme ça :

Gem::Dependency.new('', '~> 1.4.5').match?('', '1.4.6beta4')

1 votes

Les versions plus récentes semblent exiger une chaîne de caractères pour le nom. Une chaîne vide fonctionne bien, par exemple Gem::Dependency.new('', '~> 1.4.5').match?('', '1.4.6beta4')

20voto

DigitalRoss Points 80400
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')]

3 votes

Comme certaines des autres réponses, il semble que vous fassiez des comparaisons de chaînes de caractères au lieu de comparaisons numériques, ce qui pose des problèmes lorsque vous comparez des versions comme '0.10' et '0.4'.

7 votes

Upvoted pour la solution concise qui ne nécessite pas l'installation d'une gemme.

2 votes

Pour ce que ça vaut : 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 Le blob de code le rend moche, mais en gros, l'utilisation de cette méthode par rapport à Gem::Version est environ deux fois plus rapide.

15voto

notnoop Points 30218

Vous pouvez utiliser le Versionomy pierre précieuse (disponible à l'adresse 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

4 votes

Je l'ai vu, mais il me faut utiliser 2 gemmes pour faire une chose très simple. Je veux l'utiliser comme dernier choix.

8 votes

"Ne réinventez pas la roue". Ce n'est pas parce que c'est simple que le programmeur n'y a pas mis du travail et de la réflexion. Utilisez le joyau, lisez le code et tirez-en des leçons - et passez à des choses plus grandes et meilleures !

0 votes

La gestion des dépendances et la maintenance des versions est un problème difficile, probablement beaucoup plus difficile que la tâche de comparer 2 versions. Je suis tout à fait d'accord pour dire que l'introduction de deux dépendances supplémentaires devrait être un dernier recours dans ce cas.

11voto

Carl Smotricz Points 36400

Je ferais

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

Alors vous pouvez faire

a1 <=> a2

(et probablement toutes les autres comparaisons "habituelles").

...et si vous voulez un < ou > vous pouvez faire, par exemple, le test suivant

(a1 <=> a2) < 0

ou faire un peu plus d'emballage de fonction si vous êtes si enclin.

1 votes

Array.class_eval {include Comparable} fera en sorte que tous les tableaux répondent à <, >, etc. Ou, si vous voulez seulement faire cela pour certains tableaux : a = [1, 2] ; a.extend(Comparable)

4 votes

Le problème que j'ai trouvé avec cette solution est qu'elle renvoie que "1.2.0" est plus grand que "1.2".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X