Go to English Blog

Adicionando novas marcações do Textile com RedCloth 4

Leia em 1 minuto

Se você usa Textile no Ruby, já deve ter usado o RedCloth. A versão 4 foi totalmente reescrita como uma extensão nativa, e por isso é muito mais rápida.

Embora o Textile tenha muitas marcações, você pode querer adicionar as suas próprias. Existem dois tipos de marcações: bloco e inline e você verá como adicionar ambas.

Para adicionar novas marcações de bloco, você só precisa definir um novo método no módulo RedCloth::Formatters::HTML. Este método irá receber um Hash com algumas opções.

module RedCloth
  module Formatters
    module HTML
      def figure(options)
        <<-HTML
          <p class="figure">
            <img src="#{options[:text]}" alt="#{options[:class]}" />
            <span>#{options[:class]}</span>
          </p>
        HTML
      end
    end
  end
end

E para ver o novo formatador em ação:

RedCloth.new("figure(Some figure). images/img.png").to_html

Já as marcações inlines são um pouco mais complicadas, se você não conhece expressões regulares. Ao contrário das marcações de bloco, você precisará criar um método no módulo RedCloth que altera o texto que é passado ao método.

module RedCloth
  URL_RE = /
    <
      ((?:https?|ftp):\/\/.*?)
    >
  /xm

  def link(text)
    text.gsub!(LINK_RE) do |m|
      %(<a href="#{$1}">#{$1}</a>)
    end
  end
end

Agora vem a parte capsiosa: o retorno do método é ignorado, por isso, você precisa usar o método String#gsub!. Além disso, o RedCloth precisa saber que você quer utilizar este método e isso deve ser feito na chamada ao método to_html.

RedCloth.new("<http://nandovieira.com.br>").to_html(:textile, :link)

Se você adicionar muitas marcações inline personalizadas, uma solução pode ser criar um novo formatador, ou apenas um wrapper, como no exemplo abaixo.

module Textile
  FORMATTERS = [:textile, :link]

  def self.to_html(text)
    RedCloth.new(text).to_html(*FORMATTERS)
  end
end

De quebra, você deixou o código mais objetivo e fácil de entender.

Textile.to_html(some_textile_text)