2012-04-20 2 views
7

Я разработчик PHP, который пытается получить некоторое знание Ruby. Одним из проектов, над которыми я сейчас режу зубы, является инструмент аудита исходного кода, который сканирует файлы webapp для потенциально опасных функций на нескольких языках веб-программирования. Когда совпадения найдены, скрипт сохраняет соответствующую информацию в классе poi (точка интереса) для отображения позже.Динамическое создание многомерного хэша в Ruby

Пример экземпляра этого класса будет выглядеть примерно так (по образцу в YAML):

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

На дисплее, я хочу организовать эти достопримечательности, как так:

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

Таким образом, , перед представлением, я пытаюсь структурировать плоский массив объектов poi в хеш-зеркалирование структуры выше. Это позволит мне просто перебрать элементы хэша, чтобы создать желаемую организацию на экране. (Или, по крайней мере, это план.)

И теперь, для моего вопроса: как мне это сделать в Ruby?

В PHP, я мог бы сделать что-то вроде этого действительно легко:

<?php 

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

?> 

Я пытался переводить эту мысль из PHP на Ruby, как это, но безрезультатно:

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 Я потратил несколько часов на это, и я как бы ударился головой о стену в этот момент, так что, по-видимому, я вне базы. Каков правильный способ справиться с этим в Ruby?

Update:

Для справки, это точный метод, который я определил:

# 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 

Это ошибка я получаю, когда я запускаю код:

./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 

Линия 62 (как вы, вероятно, можете вывести), это линия, в частности:

@points_of_interest.each_with_index do |point, index| 

В качестве дополнительной ссылки, вот что (фрагмент) @points_of_interest выглядит при преобразовании в 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

Что с чем ты иметь? Это приводит к ошибкам или является результатом не того, что вы ожидаете? Кроме того, предоставление ввода/вывода выборки полезно. –

+0

@AndrewMarshall, спасибо, что посмотрели. Я просто обновил вопрос. –

ответ

27

@Enumerable#group_by предложение Джона является один хороший способ, чтобы решить ваши потребности.Еще бы создать автоматическую живительную Hash (как вы, кажется, есть в PHP) как так:

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

Обратите внимание, что этот вид авто-оживление может быть «опасными», если доступ ключей, которые не существует, так как он создает их для вас:

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

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

вы все еще можете использовать живительную default_proc, не задев эту опасность, если вы используете key? для проверки ключа первого:

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

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

FWIW, ошибка вы получаете говорит, «Эй, вы положили [] после чего-то, что оценивается в nil и nil не метод [] В частности, код ...

sorted_pois[point.file_type.to_sym] 

оценивается в nil (поскольку хэш еще не имеет значения для этого ключа), а затем вы попытались попросить

nil[point.file.to_sym] 
+1

Столь опытный ... – texasbruce

+0

+1 Ницца! (Несмотря на то, что для новичков Ruby было незаметно подано). –

+0

@Phrogz, спасибо, что нашли время, чтобы объяснить это мне. Я действительно начинаю любить Руби, но человек, это сложно! Это делает очевидным, что мне нужно немного больше читать :) –

2

Очевидная проблема приведенном выше примере, что вложенные хэши и массивы вы пытаетесь использовать Дон не существует. Попробуйте следующее:

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

Это «безопасный» способ ручного создания гнезд; см. мой ответ для менее безопасного, но более удобного способа. – Phrogz

+0

Phrogz, вы правы, спасибо, что заметили, я исправил это. –

7

Возможно, вас заинтересует group_by.

Пример использования:

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 за право; вы можете собрать больше очков, если вы покажете, как они используются, за исключением ссылок на документы. – Phrogz

Смежные вопросы