[Rails]redcarpetとrougeによるマークダウン機能
はじめに
Redcarpet
を使って投稿にMarkdownで書いた文章をHTMLに変換していきます。
Redcarpet
は、Markdown形式のテキストをHTMLに変換するためのgemです。
Redcarpet::Render::HTMLを使用してHTMLレンダラーを作成してくれます。
require 'redcarpet'
# Markdownテキストを定義
markdown_text = "# Hello, Redcarpet!\n\nThis is **bold** and this is *italic*."
# Redcarpetのインスタンスを作成
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
# HTMLに変換
html_output = markdown.render(markdown_text)
# HTMLを表示
puts html_output
マークダウンテキストの出力結果は次のようになります:
<h1>Hello, Redcarpet!</h1>
<p>This is <strong>bold</strong> and this is <em>italic</em>.</p>
html以外にも、Markdownを他の言語のフォーマットに変換することもできます。
環境
Rails 7.0.4
ruby 3.2.2
Redcarpet
をインストールする:
gem 'redcarpet'
bundle install
Loofah SafeList
を追加する
Loofah SafeList
は、RubyのgemであるLoofahを使用してHTMLをサニタイズ(安全にする)する際に、特定の要素や属性を許可するためのリストです。
サニタイズは、不正なコードや悪意のあるスクリプトが埋め込まれるのを防ぎ、安全なHTMLを生成するための重要な手段です。
Loofah SafeList
を追加することでマークダウンをHTMLに変換する場合HTMLの安全化を行うことができます。
require 'loofah'
# サニタイズするHTML
html = <<-HTML
<h1>Title</h1>
<p>Paragraph <span style="color: red;">with red color</span></p>
<script>alert('This is a malicious script!');</script>
HTML
# safelistを定義
safelist = Loofah::HTML5::SafeList.new
safelist.allow_elements("h1", "p", "span") # h1, p, span要素を許可
safelist.allow_attributes("style") # style属性を許可
# HTMLをサニタイズ
sanitized_html = Loofah.fragment(html).scrub!(safelist).to_s
puts sanitized_html
上記の例では、<h1>
、<p>
、および<span>
要素を許可し、style
属性を許可するSafeList
を定義しています。<script>
要素は許可されていないので、サニタイズされたHTMLから削除されます。
出力結果は次のようになります:
<h1>Title</h1>
<p>Paragraph <span style="color: red;">with red color</span></p>
<script>
要素が完全に削除されていることが分かります。Loofah SafeList
を使用することで、指定した要素と属性以外のHTMLが安全に削除され、安全なコンテンツのみが残されます。
これにより、悪意のあるコードが埋め込まれるのを防ぎ、セキュリティを向上させることができます。
Loofah
のファイルを使ってアプリにSafeList
を追加していきます。
lib/loofah/html5/safelist.rb
ファイルは、Loofah
gemの一部であり、HTMLのサニタイズ(安全化)のために使用されるSafeListクラスが定義されているファイルです。
SafeListクラスは、許可されたHTML要素や属性のリストを管理します。これにより、HTMLのサニタイズ処理において、指定した要素と属性以外の部分を削除することが可能となります。
safelist.rb
を作成する
アプリにlib/loofah/html5/safelist.rb
を作成し、Loofahのコードをペーストします。
application.rb
に読み込む
作成したsafelist.rb
を読み込みます。
class Application < Rails::Application
...
config.action_view.sanitized_allowed_tags = Loofah::HTML5::SafeList::ALLOWED_ELEMENTS
config.action_view.sanitized_allowed_attributes = Loofah::HTML5::SafeList::ALLOWED_ATTRIBUTES
end
マークダウンヘルパーを作成する
markdown
オブジェクトにマークダウさせたいテキストと拡張機能の二つの引数を渡す必要があります。
# Initializes a Markdown parser
markdown = Redcarpet::Markdown.new(renderer, extensions = {})
Redcarpet
でサポートされている主な拡張機能:
-
:tables
を有効にすることで、テーブルを使用できるようになります。 -
:tasklist
を有効にすることで、タスクリストを使用できるようになります。 -
:strikethrough
を有効にすることで、取り消し線のような取り消し線を使用できるようになります。 -
:autolink
を有効にすることで、URLやメールアドレスなどの自動リンクが有効になります。 -
:with_toc_data
拡張を有効にすることで、マークダウンに目次(Table of Contents)を作成する際に、各セクションに対して自動的にIDを追加します。 -
:no_intra_emphasis :
通常、_
で囲まれたテキスト(イタリック)の中に別の_
がある場合、それをイタリックとして解釈します。しかし、no_intra_emphasis
を有効にすると、中の_はイタリックとして解釈せず、テキスト内でアンダースコアを使用できるようになります。 -
:fenced_code_blocks
: バッククォート3つ(```/~~~)で囲むことでコードブロックを表現できるようにします。 -
:disabled_indented_code_blocks
: インデントによるコードブロック(4つのスペースで開始される行)を無効にします。 -
:lax_spacing
: マークダウンのパース時に、ヘッダーやリストなどのブロック要素の前後に厳密な空白がなくても解釈します。 -
:space_after_headers
: #ヘッダーの後に空白を必要とするかどうかを設定します。例えば、# Header
はヘッダーとして解釈されますが、#Header
はヘッダーとして解釈されません。 -
:superscript
:^テキスト^
のような上付き文字を有効にします。 -
:underline
:<u>テキスト</u>
のような下線を有効にします。 -
:highlight
:==テキスト==
のようなハイライトを有効にします。 -
:quote
:> テキスト
のような引用を有効にします。 -
:footnotes:
マークダウン内で脚注を使用することを有効にします。
redcarpet
がマークダウン以外に、目次を作成してくれるRedcarpet::Render::HTML_TOC
レンダラーも用意してます。
toc
はtable of contents
の最初の文字を取って作られた略称です。
module MarkdownHelper
def markdown(text)
options = {
hard_wrap: true,
link_attributes: { rel: 'nofollow', taget: '_blank' },
fenced_code_blocks: true,
no_intra_emphasis: true,
autolink: true,
tables: true,
lax_spacing: true,
underline: true,
highlight: true,
quote: true,
footnotes: true
}
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
sanitize(markdown.render(text))
end
def toc(text)
renderer = Redcarpet::Render::HTML_TOC.new(nesting_level: 3)
markdown = Redcarpet::Markdown.new(renderer)
markdown.render(text).html_safe
end
end
マークダウンヘルパーを使う
markdown()
ヘルパーを使うことで投稿の本文をマークダウンからHTMLに変換して表示しています。これにより、マークダウン形式のテキストがHTMLとして正しく表示されるようになります。
<h1><%= article.title %></h1>
<%= toc(article_body) %>
<%= markdown(article.body) %>
早速試してみます、投稿の本文にマークダウンテキストを入れてみます。
html
に変換されましたね。
本文の上に目次も作成されました。
Fakerを使ってダミーマークダウンを作成する
faker
がマークダウンにも対応しているため使っていきます。
5.times do |index|
Article.create!(
user: User.offset(rand(User.count)).first,
title: "投稿#{index}",
body: Faker::Markdown.sandwich(sentences: 6, repeat: 3)
)
end
bin/rails db:seed
投稿にマークダウンを使えるようになりました。
次ではrouge
を使ってコードのシンタックスハイライトを入れていきます。
rouge
はシンタックスハイライターgemであり、コードブロックなどのソースコードをカラフルにハイライト表示するために使用されます。
rouge
をインストールする
gem 'rouge'
bundle install
rouge
のカスタムレンダラーを作成する
module MarkdownHelper
require 'rouge'
require 'rouge/plugins/redcarpet'
class RougeHTML < Redcarpet::Render::HTML
include Rouge::Plugins::Redcarpet
end
def markdown(text)
options = {
hard_wrap: true,
link_attributes: { rel: 'nofollow', taget: '_blank' },
fenced_code_blocks: true,
no_intra_emphasis: true,
autolink: true,
tables: true,
lax_spacing: true,
underline: true,
highlight: true,
quote: true,
footnotes: true
}
- markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
+ markdown = Redcarpet::Markdown.new(RougeHTML, options)
sanitize(markdown.render(text))
end
end
rouge.scss.erb
を作る
rouge
のカラーテーマを使うためそれを読み込むスタイルシートファイルを作成します。
<%= Rouge::Themes::Thankful_eyes.render(:scope => '.highlight') %>
.scss.erb
.scss.erb
ファイルは、SCSSとERBの両方を組み合わせたファイルです。
.scss.erb
ファイルは、これらの2つの拡張子を組み合わせたもので、SCSSとERBの機能を組み合わせて利用することができます。このファイル形式は、例えば以下のような場面で使用されます:
-
動的なスタイルシートの生成: ERBを使って動的な値を計算し、SCSS内の変数に埋め込んでスタイルシートを生成することができます。
-
条件付きスタイルの設定: ERBを使って条件文を記述し、条件に応じて異なるスタイルを適用することができます。
-
ローカライズ: ERBを使って多言語サポートを実現する際に、言語ごとに異なるスタイルを適用することができます。
// app/assets/stylesheets/custom.scss.erb
$primary-color: <%= current_user.theme_color %>; // ERBで動的な値を取得
body {
background-color: $primary-color;
}
<% if current_user.admin? %>
.admin-link {
color: red;
}
<% else %>
.admin-link {
color: blue;
}
<% end %>
この例では、current_user.theme_color
によってユーザーごとに異なるテーマカラーを取得し、スタイルシートに反映しています。また、current_user.admin?
によってユーザーが管理者かどうかを判断し、条件に応じて異なるスタイルを適用しています。
rouge
のカラーテーマ:
こちらからカラーテーマをプレビューすることができます:
コードをハイライトされる前:
ハイライトされたことを確認します:
終わり
投稿にマークダウンテキストを書けるようになりましたね。
シンタックスハイライトを導入することでコードをかなり読みやすくなりましたね。
Discussion