RubyでRSSフィードからメールダイジェストを生成するスクリプトを作ってみよう
こんな感じのものを作ります。
メルマガの立ち上げは、TODOリストにずっと載ったままになっていました。今日こそは実現させようと思います。登録をご希望の方はこちらからどうぞ。
実は私は長文のニュースレターがあまり好きではありません。好きなのは、興味深いコンテンツを厳選してまとめた「ダイジェスト」形式のものです。RubyWeeklyやWistiaのブログダイジェストなどが思い浮かびます。
こうしたダイジェストを毎回手作業で作るのは時間がかかりすぎますし、かといって完全自動化するとどこか味気なくなってしまいます。そこで目指すのは「半自動」の仕組みです。具体的には、最新のブログ記事を取得して、後から自分好みにカスタマイズできるHTMLメールを出力してくれるスクリプトがあれば十分です。
それでは早速作っていきましょう!ユーザーは私一人だけ。気楽に楽しめる小さなプロジェクトになりそうです。
全体の設計(ゲームプラン)
今回のスクリプトに必要な処理は次の3つです。
HoneybadgerブログのRSSフィードを取得・解析する
カテゴリに基づいて対象記事を選択する
ERBテンプレートを使って記事一覧をHTMLとして描画する
スクリプトはコマンドラインから実行し、結果はSTDOUTに出力するシンプルな構成にします。
Rubyでのフィード取得と解析
ご存知でしたか?実はRubyの標準ライブラリには、RSSやATOMフィードを生成・解析するためのモジュールが最初から同梱されています。今回のようなユースケースでは、これ以上ないほどシンプルです。使い方は以下の通りです。
require 'rss'
feed = RSS::Parser.parse('https://www.honeybadger.io/blog/feed/')
feed.items.each do |item|
puts item.title
end
注目すべきは、このモジュールがフィードのダウンロードまで自動的に行ってくれる点です。まさに至れり尽くせりですね。
カテゴリによる記事の絞り込み
読者に関心のないリンクを送りたくはないので、カテゴリで記事をフィルタリングすることにします。RubyのRSSライブラリにはcategoriesメソッドが用意されていますが、これはXMLノードオブジェクトの配列を返すだけです。本当に欲しいのはカテゴリ名なので、RSSアイテムをArticleというデコレータクラスでラップすることにしました。
これで、「How To」カテゴリに属する記事だけを簡単に取り出せるようになります。
require 'rss'
require 'delegate'
class Article < SimpleDelegator
def category_names
categories.map &:content
end
end
feed = RSS::Parser.parse('https://www.honeybadger.io/blog/feed/')
articles = feed.items.map { |o| Article.new(o) }.select { |a| a.category_names.include?("How To") }
ERBテンプレートでの描画
出来上がるのはマークアップの少ないメールなので、テンプレートエンジンにはERBを採用します。下記のコードのように、テンプレート本体と描画ロジックをDigestViewという1つのクラスにまとめました。この規模の小さく単一目的なテンプレートなら、わざわざ別ファイルに分割するのは過剰だと判断したからです。
最終的な出力はSTDOUTに表示されます。これにより、出力をmacOSのpbcopyコマンドへパイプで渡せば、結果がそのままクリップボードにコピーされ、メール配信システムに貼り付けるだけで済みます。
require 'rss'
require 'delegate'
require 'erb'
class Article < SimpleDelegator
def category_names
categories.map &:content
end
end
class DigestView
attr_accessor :articles
def initialize(articles)
@articles = articles
end
def render
ERB.new(template, 0, '>').result(binding)
end
def template
%{<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head>
<body>
<h1>Headline: Replace me</h1>
<p>Intro paragraph: Replace me.</p>
<ul>
<% for article in @articles %>
<li>
<a href="<%= article.link %>">
</li>
<% end %>
</ul>
</body>
</html>}
end
end
feed = RSS::Parser.parse('https://www.honeybadger.io/blog/feed/')
articles = feed.items.map { |o| Article.new(o) }.select { |a| a.category_names.include?("How To") }
printf DigestView.new(articles).render
実際の出力はこのようになります。
ブログダイジェスト生成スクリプトの出力結果。
今後の課題
本番運用に向けるまでには、あともう少し作業が必要です。とはいえ、残りのタスクはほとんどHoneybadger固有のカスタマイズであり、一般向けにはあまり役に立たないものばかりです。今日中に片付けたいリストは以下の通りです。
テンプレートの見た目を整え、メール配信サービスでテストする
リンクにGoogle Analyticsのトラッキングパラメータを追加する
テンプレートに記事の概要文を追加する
-
TCMallocを使ってRubyのメモリ割り当てをプロファイリングする方法
Rubyでは、メモリの割り当てはどのように行われているのでしょうか? Rubyは「ページ(page)」と呼ばれる塊単位でメモリを確保し、新しく生成されたオブジェクトはこのページの中に格納されていきます。 しかし、 ページがいっぱいになると、さらなるメモリが必要になります。 そのときRubyは、malloc関数を使ってオペレーティングシステム(OS)に追加のメモリを要求します。 このmalloc関数はOS標準で提供されているものですが、代替となる実装に差し替えることも可能です。 その代表的な実装のひとつが、Google製のTCMallocです。 TCMallocは「gperftools(Goog
-
Rubyでパーサーを自作する方法!StringScannerを使った実装手順を徹底解説
パース(構文解析)とは、文字列の集まりから意味を読み取り、プログラムが扱える形のデータへと変換する技術です。正規表現でも文字列の解析は可能ですが、すべての場面に適しているわけではありません。 たとえば、正規表現でHTMLを解析するのはあまり良い方法ではないというのは、プログラミング界隈ではよく知られた話です。 Rubyにはnokogiriという強力なライブラリがあり、HTMLの解析はこれに任せられます。しかし、自分でパーサーを一から作ってみると、文字列処理や構文解析の仕組みについて多くのことを学べます。それでは早速始めていきましょう! Rubyでのパースの基本:StringScannerクラス