2012-04-20 12 views
7

Jestem programistą PHP, który stara się zdobyć trochę biegłości w Rubim. Jednym z projektów, w które teraz zmierzam, jest narzędzie do kontroli kodu źródłowego, które skanuje pliki webapp pod kątem potencjalnie niebezpiecznych funkcji w kilku językach programowania sieciowego. Po znalezieniu dopasowań skrypt zapisuje odpowiednie informacje w klasie poi (punkt zainteresowania) do wyświetlenia później.Dynamiczne tworzenie wielowymiarowego skrótu w Ruby

Przykładem instancja tej klasy będzie wyglądać następująco (wzorowany na YAML):

poi: 
    file_type: "php" 
    file: "the-scanned-file.php" 
    line_number: 100 
    match: "eval()" 
    snippet: "echo eval()" 

Na wyświetlaczu, chcę zorganizować te ciekawostki tak:

- file_type 
-- file 
--- match (the searched payload) 

Zatem przed prezentacją próbuję ułożyć płaską tablicę obiektów poi w haszu odzwierciedlającym powyższą strukturę. Pozwoli mi to po prostu powtórzyć elementy w haszdzie, aby utworzyć pożądaną organizację na ekranie. (A przynajmniej taki jest plan).

A teraz, na moje pytanie: jak mam to zrobić w Ruby?

W PHP można zrobić coś takiego naprawdę łatwo:

<?php 

$sorted_pois = array(); 
foreach($points_of_interest as $point){ 
    $sorted_pois[$point->file_type][$point->file][$point->match][] = $point; 
} 

?> 

Próbowałem tłumacząc tę ​​myśl z PHP Ruby jak ten, ale bezskutecznie:

sorted_pois = {} 
@points_of_interest.each_with_index do |point, index| 
    sorted_pois[point.file_type.to_sym][point.file.to_sym][point.match.to_sym].push point 
end 

I Spędziłem w tym czasie kilka godzin, a ja w tym momencie w pewnym sensie uderzam głową w ścianę, więc prawdopodobnie jestem poza bazą. Jaki jest właściwy sposób radzenia sobie z tym w Ruby?

Aktualizacja:

Dla porównania, jest to dokładna metoda Mam zdefiniowane:

# sort the points of interest into a structured hash 
def sort 
    sorted_pois = {} 
    @points_of_interest.each_with_index do |point, index| 
    sorted_pois[point.file_type.to_sym][point.file.to_sym][point.match.to_sym].push point 
    end 
end 

Jest to błąd otrzymuję kiedy uruchomić kod:

./lib/models/vulnscanner.rb:63:in `sort': undefined method `[]' for nil:NilClass (NoMethodError) 
    from /usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `each_with_index' 
    from ./lib/models/vulnscanner.rb:62:in `each' 
    from ./lib/models/vulnscanner.rb:62:in `each_with_index' 
    from ./lib/models/vulnscanner.rb:62:in `sort' 
    from ./webapp-vulnscan:69 

Linia 62 (jak można się domyślić) to ta linia w szczególności:

@points_of_interest.each_with_index do |point, index| 

Jako dodatkowe odniesienia, oto co (urywek) @points_of_interest wygląda, gdy przekształca się YAML:

- !ruby/object:PoI 
    file: models/couponkimoffer.php 
    file_type: php 
    group: :dangerous_functions 
    line_number: "472" 
    match: ` 
    snippet: ORDER BY `created_at` DESC 
- !ruby/object:PoI 
    file: models/couponkimoffer.php 
    file_type: php 
    group: :dangerous_functions 
    line_number: "818" 
    match: ` 
    snippet: WHERE `company_slug` = '$company_slug' 
- !ruby/object:PoI 
    file: models/couponkimoffer.php 
    file_type: php 
    group: :dangerous_functions 
    line_number: "819" 
    match: ` 
    snippet: ORDER BY `created_at` DESC 
+1

Co się stało z tym, co mieć? Czy powoduje błędy lub czy dane wyjściowe nie są zgodne z oczekiwaniami? Pomocne jest również podanie przykładowego wejścia/wyjścia. –

+0

@AndrewMarshall, dzięki za spojrzenie. Właśnie zaktualizowałem pytanie. –

Odpowiedz

27

@ Jana Enumerable#group_by sugestia jest dobrym sposobem na rozwiązanie swoich potrzeb.Innym byłoby stworzyć auto-ożywiającą Hash (tak jak wydaje się mieć w PHP) tak:

hash = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) } 
hash[:a][:b][:c] = 42 
p hash 
#=> {:a=>{:b=>{:c=>42}}} 

Należy zauważyć, że ten rodzaj auto-vivification może być „niebezpieczne”, jeśli dostęp do kluczy, które NIE istnieje, gdyż tworzy je dla Ciebie:

p hash["does this exist?"] 
#=> {} 

p hash 
#=> {:a=>{:b=>{:c=>42}}, "does this exist?"=>{}} 

można nadal używać ożywiającą default_proc bez uderzania tego niebezpieczeństwa, jeśli używasz key? przetestować klucza pierwszy:

val = hash["OH NOES"] if hash.key?("OH NOES") 
#=> nil 

p hash 
#=> {:a=>{:b=>{:c=>42}}, "does this exist?"=>{}} 

FWIW, błąd otrzymujesz mówi „Hej, można umieścić [] po czymś, ocenianego na nil i nil nie ma metody []”. szczególności kod ...

sorted_pois[point.file_type.to_sym] 

oceniona nil (ponieważ hash jeszcze nie mają wartości dla tego klucza), a potem próbował prosić o

nil[point.file.to_sym] 
+1

Tak doświadczony ... – texasbruce

+0

+1 Nice! (Chociaż trochę przytłaczający dla początkujących Ruby.) –

+0

@Phrogz, dziękuję za poświęcenie czasu, aby mi to wyjaśnić. Naprawdę zaczynam lubić Ruby, ale stary, to trudne! To czyni oczywistym, że mam trochę więcej do zrobienia :) –

2

Oczywisty problem z powyższym przykładzie jest to, że zagnieżdżone mieszań i tablice próby korzystania don istnieje. Wypróbuj to:

sorted_pois = {} 
pois.each do |point| 
    # sanitize data - convert to hash of symbolized keys and values 
    poi = Hash[ %w{file_type file match}.map do |key| 
    [key.to_sym, point.send(key).to_sym] 
    end ] 

    # create nested hash/array if it doesn't already exist 
    sorted_pois[ poi[:file_type] ] ||= {} 
    sorted_pois[ poi[:file_type] ][ poi[:file] ] ||= {} 
    sorted_pois[ poi[:file_type] ][ poi[:file] ][ poi[:match] ] ||= [] 

    sorted_pois[ poi[:file_type] ][ poi[:file] ][ poi[:match] ] << point 
end 
+0

To jest "bezpieczniejszy" sposób ręcznego tworzenia zagnieżdżania; zobacz moją odpowiedź na mniej bezpieczny, ale wygodniejszy sposób. – Phrogz

+0

Phrogz, masz rację, dziękuję za uwagę, naprawiłem to. –

7

Możesz być zainteresowany group_by.

wykorzystanie próbki:

birds = ["Golden Eagle", "Gyrfalcon", "American Robin", 
     "Mountain BlueBird", "Mountain-Hawk Eagle"] 
grouped_by_first_letter = birds.group_by { |s| s[0] } 

# { "G"=>["Golden Eagle", "Gyrfalcon"], "A"=>["American Robin"], 
# "M"=>["Mountain BlueBird", "Mountain-Hawk Eagle"] } 
+1

+1 za bycie prawym; możesz zebrać więcej upvotes, jeśli pokażesz, w jaki sposób jest używany poza łączeniem się z dokumentami. – Phrogz

Powiązane problemy