#272 Markdown with Redcarpet
- Download:
- source codeProject Files in Zip (204 KB)
- mp4Full Size H.264 Video (12.4 MB)
- m4vSmaller H.264 Video (7.75 MB)
- webmFull Size VP8 Video (8.32 MB)
- ogvFull Size Theora Video (19.8 MB)
Github a présenté, il y à quelques mois, une gem nommée Redcarpet. Cette gem permet d'interpréter du code Markdown et Github l'utilise en interne, conjointement à la librairie Upskirt, pour fournir un moyen simple de baliser les documents. Redcarpet est facile à utiliser et, dans cet épisode, nous allons l'ajouter à une application Rails, montrer comment elle peut être personnalisée et mettre en place la coloration syntaxique des blocs de code.
Nous allons travailler avec une simple application de blog (voir ci-dessous). Le contenu saisi pour l'article et affiché directement en HTML. Bien que nous ayons entré deux paragraphes, ils sont montrés comme un seul bloc.
Le contenu n'est, pour le moment, interprété d'aucune façon. Nous allons changer notre application pour qu'il passe au travers de Markdown. De cette manière, tout utilisateur créant ou modifiant du contenu aura une grande flexibilité sur le format de sortie.
Installer Redcarpet
La première étape est, comme vous vous en doutez, d'installer la gem Redcarpet. Comme d'habitude, nous référençons la gem dans le Gemfile et nous faisons appel à bundle pour l'installer.
source 'http://rubygems.org' gem 'rails', '3.0.9' gem 'sqlite3' gem 'nifty-generators' gem 'redcarpet'
Nous allons ensuite aller dans la page show
de ArticleController
et ajouter Redcarpet à la ligne affichant le contenu de l'article. Pour ce faire, nous créons un nouvel objet Redcarpet
, lui passons le contenu à baliser et appelons to_html
sur la chaine retournée.
<% title @article.name %> <%= Redcarpet.new(@article.content).to_html %> <p> <%= link_to "Edit", edit_article_path(@article) %> | <%= link_to "Destroy", @article, :confirm => 'Are you sure?', :method => :delete %> | <%= link_to "View All", articles_path %> </p>
Cependant, lorsque nous rechargeons la page, nous n'obtenons pas vraiment le résultat attendu.
Rails 3 a automatiquement échappé le code et nous voyons donc les balises ajoutées par Redcarpet. La méthode rapide pour régler ce problème est d'appeler raw
sur le contenu.
<% title @article.name %> <%= raw Redcarpet.new(@article.content).to_html %> <p> <%= link_to "Edit", edit_article_path(@article) %> | <%= link_to "Destroy", @article, :confirm => 'Are you sure?', :method => :delete %> | <%= link_to "View All", articles_path %> </p>
Lorsque nous rechargeons la page, elle s'affiche comme nous le voulions.
Faire cela chaque fois que nous voulons afficher du code Markdown est un peu pénible. Nous allons donc créer un helper appelé markdown
pour rendre cela plus aisé.
module ApplicationHelper def markdown(text) Redcarpet.new(text).to_html.html_safe end end
Lorsque nous retournons du contenu depuis un helper, nous appelons dessus html_safe
plutôt que raw
. Nous pouvons maintenant utiliser notre helper dans la page d'un article.
<% title @article.name %> <%= markdown @article.content %> <p> <%= link_to "Edit", edit_article_path(@article) %> | <%= link_to "Destroy", @article, :confirm => 'Are you sure?', :method => :delete %> | <%= link_to "View All", articles_path %> </p>
Personnaliser Redcarpet
Nous allons maintenant voir la personnalisation de Redcarpet. Une chose qu'elle ne fait pas par défaut est la gestion des sauts de lignes uniques : une ligne vide est requise pour marquer un nouveau paragraphe. Si nous entrons du texte avec un saut de ligne comme ceci
give me a break
il sera montré comme une seule ligne à l'affichage.
Il serait préférable que saisir du texte comme ceci insère une balise de saut de ligne au bon endroit dans le code HTML. Si nous regardons la documentation de Redcarpet, nous voyons une liste d'options dont l'une est hard_wrap qui fera exactement cela.
Nous ajoutons les options sous forme de symboles passés au constructeur de Redcarpet, comme ceci :
module ApplicationHelper def markdown(text) Redcarpet.new(text, :hard_wrap).to_html.html_safe end end
Lorsque nous rechargeons la page de notre article, le texte “Give me a break” sera affiché sur deux lignes comme nous le voulions.
Il existe un certain nombre d'options utiles que nous pouvons ajouter. L'option filter_html
empêchera tout code HTML d'être affiché. Nous allons également ajouter l'option autolink
afin que toute URL détectée dans le code soit transformée en lien. Puisque nous prévoyons d'inclure des exemples de code dans les articles, nous allons utiliser l'option no_intraemphasis
. Elle évite que les underscores dans les mots soient traités comme des marqueurs de texte italique et permet donc un traitement normal des méthodes et variables Ruby avec underscores. Enfin, pour aider à l'affichage des extraits de code dans nos articles, nous allons ajouter deux options : fenced_code
(nous allons voir rapidement son utilisation) et gh_blockcode
.
Nous avons maintenant une longue liste d'options que nous allons extraire sous forme de tableau dans notre helper.
module ApplicationHelper def markdown(text) options = [:hard_wrap, :filter_html, :autolink, :no_intraemphasis, :fenced_code, :gh_blockcode] Redcarpet.new(text, *options).to_html.html_safe end end
Avec ces options, nous pouvons ajouter des blocs de code à nos articles tout comme nous le faisons sur Github. Ces blocs commencent et se terminent par une ligne contenant trois apostrophes inverses et le langage est spécifié sur la ligne d'ouverture. Nous allons modifier notre article pour y ajouter un bloc de code.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ``` ruby puts "Hello, world!"
<p>Le rendu est le suivant, l'extrait de code étant affiché dans une balise <code>pre</code> avec l'attribut <code>lang</code> approprié pour définir le langage.</p> <div class="imageWrapper"> <img src="http://railscasts.com/static/episodes/asciicasts/E272I06.png" width="802" height="428" alt="Les blocs de code sont maintenant affiches."/> </div> <p>Nous pouvons également utiliser une méthode plus traditionnelle de définition des blocs de code en utilisant des tildes au lieu des apostrophes inverses.</p> ``` html Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ~~~ruby puts "Hello, world!" ~~~
Ceci sera affiché exactement de la même façon qu'avec les apostrophes inverses.
Coloration syntaxique
Maintenant que nous pouvons afficher des blocs de code dans nos articles, comment pouvons nous ajouter la coloration syntaxique ? Github gère cela grâce à une combinaison de la librairie Python Pygments et de la gem Albino. Nous allons faire de même dans notre application.
Nous allons ajouter Albino à notre Gemfile
mais également Nokogiri de façon à ce qu'il interprète et parse le code HTML généré par Redcarpet.
source 'http://rubygems.org' gem 'rails', '3.0.9' gem 'sqlite3' gem 'nifty-generators' gem 'redcarpet' gem 'albino' gem 'nokogiri'
Comme toujours, nous appelons bundle pour s'assurer que tout est installé.
Nous allons également avoir besoin de Pygments. Comme nous avons python sur notre machine, nous pouvons l'installer en faisant
$ sudo easy_install pygments
Dans notre ApplicationHelper
, nous allons ajouter une méthode, que nous allons appeler syntax_highlighter
, autour de la ligne qui convertit le code Redcarpet en HTML. Cette méthode utilisera Nokogiri pour chercher les éléments pre
ayant un attribut lang
dans le document puisque c'est la façon dont Redcarpet définit les blocs de code. Elle va ensuite remplacer le contenu de ces éléments par la syntaxe interprétée.
module ApplicationHelper def markdown(text) options = [:hard_wrap, :filter_html, :autolink, :no_intraemphasis, :fenced_code, :gh_blockcode] syntax_highlighter(Redcarpet.new(text, *options).to_html).html_safe end def syntax_highlighter(html) doc = Nokogiri::HTML(html) doc.search("//pre[@lang]").each do |pre| pre.replace Albino.colorize(pre.text.rstrip, pre[:lang]) end doc.to_s end end
Ce code est basé sur la gem rack-pygmentize et cela vaut la peine de jeter un coup d'œil pour voir comment elle fonctionne. Cette méthode rend facile la migration vers d'autres systèmes de coloration syntaxique comme Coderay si vous préférez.
Nous avons ajouté une feuille de style pour montrer la coloration syntaxique que vous pouvez trouver sur Github. Lorsque nous rechargeons la page, l'exemple de code est colorisé.
C'est tout pour cet épisode. Pour plus d'informations, regardez la page Github du projet.