Ruby: вложенные регулярные выражения и замена строк

Я использую CodeRay для подсветки синтаксиса, но у меня проблемы с этим регулярным выражением. Текст будет выглядеть так:

<pre><code>:::ruby
def say_hello
  puts 'hello!'
end
</code></pre>

Эта часть: :::ruby сообщит CodeRay, на каком языке следует интерпретировать кодовый блок (но он должен быть необязательным). Итак, вот что у меня так далеко:

def coderay(text)
  text.gsub(/\<pre\>\<code\>(.+?)\<\/code\>\<\/pre\>/m) do
    CodeRay.scan($2, $3).div()
  end
end

$2 содержит код, который я форматирую (включая строку, которая говорит, на каком языке его форматировать), но мне нужно извлечь эту первую строку, чтобы я мог передать его в качестве второго параметра scan() или передайте ему параметр по умолчанию, если эта языковая строка не была найдена. Как я могу это сделать?

1 ответ

В Ruby 1.9, используя именованные группы:

default_lang=:ruby

def coderay(text)
  text.gsub(%r!<pre><code>(?::{3}(?<lang>\w+)\s+)?(?<code>.+?)</code></pre>!m) do
    if $~[:lang].nil?
      lang=default_lang
    else
      lang = $~[:lang].intern
    end
    CodeRay.scan($~[:code], lang).div()
  end
end

default_lang также может быть переменной класса или объекта, а не локальной, в зависимости от контекста coderay,

То же самое, но с использованием встроенного выражения для обработки необязательного языка:

default_lang=:ruby

def coderay(text)
  text.gsub(%r!<pre><code>(?::{3}(?<lang>\w+)\s+)?(?<code>.+?)</code></pre>!m) do
    CodeRay.scan($~[:code], $~[:lang].nil? ? default_lang : $~[:lang].intern).div()
  end
end

Второй вариант немного сложнее, поэтому вы можете его избежать.

Оказывается, что именованные группы в несоответствующей необязательной группе все еще учитываются в Ruby, поэтому обработка несопоставленных нумерованных групп ничем не отличается от несопоставленных именованных групп, в отличие от того, что я впервые подумал. Таким образом, вы можете заменить именованные групповые ссылки позиционными ссылками в приведенном выше, и это должно работать так же.

default_lang=:ruby

def coderay(text)
  text.gsub(%r!<pre><code>(?::{3}(?<lang>\w+)\s+)?(?<code>.+?)</code></pre>!m) do
    CodeRay.scan($2, $1.nil? ? default_lang : $1.intern).div()
  end
end

def coderay(text)
  text.gsub(%r!<pre><code>(?::{3}(?<lang>\w+)\s+)?(?<code>.+?)</code></pre>!m) do
    if $1.nil?
      lang=default_lang
    else
      lang = $1.intern
    end
    CodeRay.scan($2, lang).div()
  end
end
Другие вопросы по тегам