はじめに
Webアプリに限らず様々な場面で「HTMLタグを無毒化して、URLを<a>
タグで囲んで...」という共通した処理をする必要があるかと思います。sanitize
や gsub
でもいいですけど、emojiを使ったり@user
や#hashtag
にもリンクを貼ったりすると、コードが煩雑化するのが目に見えています。
この問題を解決できるhtml-pipelineというGemがあります。html-pipelineはこれらのフィルタ処理をパイプラインで処理します。またデフォルトで多くのフィルタを使うことができ、独自のフィルタも定義できます。
この記事では、元のテキストを以下の手順でフィルタする方法を紹介します。
- &"<> を実体参照に置換
- URLを
<a>
タグのリンクで置換
インストール
Gemfileにhtml-pipelineを記述します。今回使うフィルタの依存gemもインストールします。
# Gemfile
gem 'html-pipeline'
gem 'rinku'
gem 'escape_utils'
Railsアプリに組み込む
Railsアプリでhtml-pipelineを使うときは、ヘルパーメソッドでラップするとviewからも呼び出せて便利です。ここでは ApplicationHelper
にヘルパーメソッドを追加します。
# app/helpers/application_helper.rb
module ApplicationHelper
def html_pipeline(source)
pipeline = HTML::Pipeline.new [
HTML::Pipeline::PlainTextInputFilter,
HTML::Pipeline::AutolinkFilter
]
pipeline.call(source)[:output]
end
end
HTML::Pipelineのnew
に、フィルタクラスをArrayで渡します。call
を呼び出すと、戻り地のハッシュの :output
に結果が格納されます。
また次のコードは、ヘルパメソッドのテストとなります。
# spec/helpers/application_helper_spec.rb
require 'rails_helper'
RSpec.describe ApplicationHelper, type: :helper do
describe "#html_pipeline" do
context "when the text contains HTML tags" do
subject { html_pipeline("Hello <b>world</b>") }
it { should match /<b>/ }
end
context "when the text contains urls" do
subject { html_pipeline("Google on https://google.com/") }
it { should match /href/ }
end
end
end
おわりに
ヘルパーメソッド呼び出し時に毎回インスタンスが作られてたくない人は、initializersでインスタンスを作れば良いと思います。またhtml-pipelineはRailsアプリに限らず、いろいろなアプリケーションに使えるので、Middlemanやjekyllでも使ってみたいですね。