RailsでHTMLをいじるhtml-pipelineを独自実装する方法サンプル
html-pipelineはHTMLをparseするGem
html-pipeline github.com
RailsでHTMLをいじる代表といえば、Nokogiriだと思いますが、今回は複数の「フィルター」を疎結合に使えるhtml-pipelineを採用しました。
なぜhtml-pipelineか
- 「フィルター」を複数組み合わせることができる
- 書き方もシンプル
- 既存実装にあわせた
Nokogiriでもいいのですが、例えば以下のようなケースを考えるとhtml-pipelineの方が拡張性があると判断しました。
ex) * ページAでは、フィルターAのみを使う * ページBでは、フィルターAに加えて、Bも使う * ページCでは、フィルターAは不要で、Bと新たに作成するCを使いたい
みたいなケースの時に、html-pipelineではフィルターA,B,Cを疎結合に作成、定義して、適宜組み合わせて使うことができます。例えばこんな感じで
EmojiPipeline = Pipeline.new [ PlainTextInputFilter, EmojiFilter ], context
独自実装する場合
html-pipeline github.com
曰く、
require 'uri' class RootRelativeFilter < HTML::Pipeline::Filter def call doc.search("img").each do |img| next if img['src'].nil? src = img['src'].strip if src.start_with? '/' img["src"] = URI.join(context[:base_url], src).to_s end end doc end end
うん、わからん
解読するに、大事なことはこれだけらしい
HTML::Pipeline::Filter
を継承した独自classを定義することdef call
で始めること- 呼ぶときは
pipeline.call(hoge)
のような感じ
実際のサンプルがこちら
class Html::Pipeline::LazyloadImageFilter < HTML::Pipeline::Filter def call doc.search('img').each do |image_node| next if image_node['src'].nil? image_node['data-src'] = image_node.attribute('src').value image_node['src'] = ActionController::Base.helpers.asset_path('lazy-dummy-rectangle.jpg') image_node['class'] = 'lozad lazyload lazyload--wide' end doc end end
この中で行なっているのは、imgタグの属性やらclassを変換しているだけです。ちなみに、Railsのレールにそって lib/
に置いています。
基本的にNokogiriに似たような感じで使えて、シンプルですね。
続いて使う側がこちら
pipeline = ::HTML::Pipeline.new([ Html::Pipeline::LazyloadImageFilter ]) hoge = pipeline.call(html_text)[:output].to_s
html_text
にはHTML形式のtextが入っていて、それをcallの引数に渡しています。実際に手元で試すとわかるのですが、pipeline.call(html_text)
だけだとオブジェクト形式のデータがかえってくるので、その中からoutput
だけを抜き出す必要があります。to_sはなくてもいいかも?