2008-09-10 5 views
17

J'ai un code Ruby qui prend date sur la ligne de commande au format:La meilleure façon de convertir une gamme de chaîne Ruby à un objet Range

-d 20080101,20080201..20080229,20080301

qui signifie que je veux courir pour toutes les dates entre 20080201 et 20080229 (inclus) et les autres dates présentes dans la liste.

Étant donné que je peux obtenir la chaîne 20080201..20080229 quelle est la meilleure façon de convertir cela en une instance de Range. Actuellement j'utilise eval, mais il me semble qu'il devrait y avoir un meilleur moyen.

@Purfideas J'étais plutôt à la recherche d'une réponse plus générale pour convertir n'importe quelle chaîne de type int..int en une gamme que je suppose.

Répondre

14

Mais juste faire

ends = '20080201..20080229'.split('..').map{|d| Integer(d)} 
ends[0]..ends[1] 

de toute façon je ne recommande pas eval, pour des raisons de sécurité

+0

Quelle est la raison de sécurité liés à l'utilisation eval? –

+1

cmd entrée ligne est "entrée utilisateur", alors pouvez-vous être toujours exécuté par des personnes de confiance? C'est l'injection SQL originale ... demandez q en sécurité ... ce sera votre meilleur score. :) – Purfideas

2

en supposant que vous voulez que la plage itérer correctement par mois etc, essayez

require 'date' 

ends = '20080201..20080229'.split('..').map{|d| Date.parse(d)} 
(ends[0]..ends[1]).each do |d| 
    p d.day 
end 
7

Injecter sans args fonctionne bien pour les baies de deux éléments:

rng='20080201..20080229'.split('..').inject { |s,e| s.to_i..e.to_i } 

Bien sûr, cela peut être générique

class Range 
    def self.from_ary(a) 
    a.inject{|s,e| s..e } 
    end 
end 

rng = Range.from_ary('20080201..20080229'.split('..').map{|s| s.to_i}) 
rng.class # => Range 
+0

Cela ne devrait-il pas être injecté {| s, e | (s.to_i .. e.to_i)}? Comme écrit, il renvoie un tableau avec une plage en tant qu'élément unique au lieu d'une plage. – cpm

+0

Commentant bien après le fait, comme je me suis précipité pour accepter la réponse, qui quand je l'ai regardé, alors l'intention avait du sens, mais j'avoue quand j'ai essayé, j'ai trouvé le même problème. –

+0

Correction de la syntaxe pour qu'elle indique maintenant correctement 'a.inject {| s, e | s..e} 'sans les parenthèses carrées –

31
Range.new(*self.split("..").map(&:to_i)) 
0

La combinaison @Purfideas répondre à une autre réponse quelque part sur StackOverflow, je résolu ce problème en entourant également le code avec un contrôle d'entrée, la seule chose utilisé est un nombre énumérable valide

if !value[/^[0-9]+\.\.[0-9]+$/].nil? 
    ends = value.split('..').map{|d| Integer(d)} 
    value = ends[0]..ends[1] 
end 

Il réécrit essentiellement votre valeur de chaîne à une valeur énumérable. Cela est pratique si vous ajoutez un champ énumérable dans un fichier de configuration yaml.

Si vous en avez besoin pour votre application, vous pouvez étendre la regex avec un troisième point littéral facultatif, qui peut être facultatif.

0

Si nous le faisons comme

v= "20140101..20150101" 
raise "Error: invalid format: #{v}" if /\d{8}\.\.\d{8}/ !~ v 
r= eval(v) 

et l'attaquant a une façon de contourner la vérification de relance (simplement au moyen de manipuler le temps d'exécution pour désactiver des exceptions) alors nous pouvons obtenir un eval dangereux qui détruira potentiellement l'univers.

Donc, pour le bien de réduire les vecteurs d'attaque, nous vérifions le format, puis effectuez l'analyse manuellement, puis vérifiez les résultats

v= "20140101..20150101" 
raise "Error: invalid format: #{v}" if /\d{8}\.\.\d{8}/ !~ v 
r= Range.new(*v.split(/\.\./).map(&:to_i)) 
raise "Error: invalid range: #{v}" if r.first> r.last 
1

Il y a un petit bijou pour cette here. Utilise regex pour valider la chaîne (pas de crainte d'injection SQL) et ensuite eval.

0

Supposons ici que vous souhaitiez stocker le hachage en tant que valeur constante du système et l'extraire dans n'importe quel modèle. La clé de hachage sera une valeur de plage.Créez ensuite la constante système avec la valeur hash_1.to_json.

hash_1 = {1..5 => 'a', 6..12 => 'b', 13..67 => 'c', 68..9999999 => 'd'} 
Ensuite, créez la constante système avec la valeur hash_1.to_json. .to_json convertira votre objet de hachage en objet JSON. maintenant à l'intérieur du code créer une nouvelle hash_2 de hachage,

JSON.parse(SystemConstant.get('Constant_name')).each{|key,val| temp_k=key.split('..').map{|d| Integer(d)}; hash_2[temp_k[0]..temp_k[1]] = val} 

Le nouveau hash_2 sera le hash_1 nécessaire

Questions connexes