YAMLとSlimで論文リストを作る

概要

以下のようなYAMLファイル(publication.yaml)を食わせて、

-
  title: How to write web sites with slim
  author: R. Robota, T. Tanaka
  journal: Ruby Journal
  year: 2010
  volume: 10
  page: 1-7
  arxiv: 1234.56789

-
  title: Using YAML file with slim
  author: R. Robota
  journal: Journal of Slim
  year: 2018
  volume: 8
  page: 1275

-
  title: This is a sample file for Slim and YAML
  author: S. Yaml, T. XML and R. Robota
  journal: J. Yaml. Phys.
  year: 2017
  volume: 3
  page: 12-18
  arxiv: 1908.01234

以下のようなHTMLを吐くSlimテンプレートのサンプルです。

image0.png

ソースは以下においておきます。

github.com/kaityo256/yamlslim

何をしたいか

論文リストとかそうですが、とりあえずなんかリストがあり、それを静的なHTMLにしたい、ということはよくあります。とりあえず論文リストはJSONかYAMLで用意されてるとしましょう。これをHTMLにしたい。さほど更新頻度も高くないので、CMSとかデータベースを導入するほどでもない、でもHTMLを直書きするのは嫌だ、という気持ち、わかっていただけますよね?

で、どうしましょう。

そういう用途では、とりあえずPHPでゴリゴリ書いちゃう、という方法が考えられます。PHPはそれが本職の言語なので、わりとまっとうな気がします。

もしくはJSON+JavaScriptというのもありな気がしますね。JSのテンプレートエンジンは豊富にあるので、好きなのを使ってなんとかする、というのが最近では良い気がします。

ですが、個人的に母国語がRubyということもあって、Rubyスクリプトの埋め込みができる軽量テンプレートエンジン、Slimを使うことにします。

Slimとは

Slimは軽量テンプレート言語です。おそらくRuby on Railsと一緒に使われることが多いと思いますが、ローカルでHTMLを吐くのにも使えます。日本語の公式ドキュメントがわかりやすいので、使うのは簡単だと思います。

Rubyとgemが入っていればSlimのインストールは簡単です。

$ sudo gem install slim

このあと、

$ slimrb -v

とかやってバージョン情報が出てきたらインストールされています。

例えば、

doctype html
html
  head
    title My first Slim
  body
    h1 This is test
    h2 Hello Slim

こんなファイルを作って、

$ slimrb test.slim

とslimに食わせると、

<!DOCTYPE html><html><head><title>My first Slim</title></head><body><h1>This is test</h1><h2>Hello Slim</h2></body></html>

と、HTMLにしてくれます。-pオプションをつけると人間が読みやすい形にしてくれます。

$ slimrb -p test.slim
<!DOCTYPE html>
<html>
  <head>
    <title>My first Slim</title>
  </head>
  <body>
    <h1>
      This is test
    </h1>
    <h2>
      Hello Slim
    </h2>
  </body>
</html>

さて、Slimの特徴は、Rubyスクリプトをそのまま埋め込めることです。ハイフン-のあとにRubyスクリプトが書けます。また、=を使って、HTML要素にRubyの実行結果を代入できます。たとえば変数aを定義して、h1に突っ込むにはこうします。

doctype html
html
  head
    title My first Slim
  body
    - a = "Hello"
    h1 = a
<!DOCTYPE html>
<html>
  <head>
    <title>My first Slim</title>
  </head>
  <body>
    <h1>
      Hello
    </h1>
  </body>
</html>

上記のようにaに代入された値を、h1要素に突っ込むことができました。

また、配列などに対してeachを使い、その要素を列挙できます。

doctype html
html
  head
    title My first Slim
  body
    ol
      - ["Dog", "Cat", "Cow"].each do |animal|
        li = animal

これをslimrbに食わせるとこんなHTMLになります。

<!DOCTYPE html>
<html>
  <head>
    <title>My first Slim</title>
  </head>
  <body>
    <ol>
      <li>
        Dog
      </li>
      <li>
        Cat
      </li>
      <li>
        Cow
      </li>
    </ol>
  </body>
</html>

image1.png

リストとeachのからみは直感的なので、特に苦労することはないと思います。

YAMLから論文リストを作る

まずはナイーブに作る

ではいよいよYAMLで書かれた論文リストからHTMLにしましょう。とりあえず

  • タイトル
  • 著者
  • ジャーナル情報
  • (もしあれば)arXivへのリンク

を出力することにします。

RubyはYAMLを読み込めば、それがそのままハッシュとして使えるので、先程のeachの例を参考にすれば簡単に書けます。

doctype html
html
  - require 'yaml'
  head
    title Publication List
  body
    h1 Publication List
    ol
      - YAML.load_file("publication.yaml").each do |d|
        li :dl
          dt = d["title"]
          dd == "#{d["journal"]}, <strong>#{d["volume"]}</strong>, #{d["page"]} (#{d["year"]})"
          dd = d["author"]
          - if d.has_key? "arxiv"
            dd == "<a href=\"https://arxiv.org/abs/#{d["arxiv"]}\">arXiv:#{d["arxiv"]}</a>"

事前にrequire 'yaml'しておき、YAML.load_file("publication.yaml")でYAMLファイルをロードして、eachでそれぞれのアイテムを表示します。if文も使えるので、特に難しいことはありませんが、テンプレートとしてはかなり読みづらいので、少し修正しましょう。

ハッシュアクセスをプロパティっぽくする

いちいちd["title"]とか書くより、d.titleと書きたいものです。Hashieを使えばできるっぽいのですが、なぜかローカルで動かなかったので、Hashクラスにメソッドを追加してしまいましょう。

class Hash
  def method_missing(method, *args)
    key = method.to_s
    if self.has_key? key
      self[key]
    else
      false
    end
  end
def

存在しないメソッドを読んだら、そのメソッド名のキーがあればその値を、そうでなければfalseを返す関数を作ってやります。これにより、slimファイルはこんな感じになります。yamlをrequireした後に自分で作ったmyhash.rbをrequireしています。

doctype html
html
  - require 'yaml'
  - require './myhash.rb'
  head
    title Publication List
  body
    h1 Publication List
    ol
      - YAML.load_file("publication.yaml").each do |d|
        li :dl
          dt = d.title
          dd == "#{d.journal}, <strong>#{d.volume}</strong>, #{d.page} (#{d.year})"
          dd = d.author
          - if d.arxiv
            dd == "<a href=\"https://arxiv.org/abs/#{d.arxiv}\">arXiv:#{d.arxiv}</a>"

生のハッシュっぽさが消えて、少し読みやすくなりました。

そうそう、Slimでは普通にHTMLタグを使うとエスケープされてしまいます。それを防ぐためには=ではなく==を使います。

もう少しすっきりさせる

少し読みやすくなったものの、まだ"#{d.journal}, <strong>#{d.volume}</strong>, #{d.page} (#{d.year})"みたいなのがあって嫌です。これもmyhash.rbに逃がしてしまいましょう。

class Hash
  def journal_ref
    "#{journal}, <strong>#{volume}</strong>, #{page} (#{year})"
  end

  def arxiv_ref
    "<a href=\"https://arxiv.org/abs/#{arxiv}\">arXiv:#{arxiv}</a>"
  end
def

これで、slimファイルはこんな感じになります。

doctype html
html
  - require 'yaml'
  - require './myhash.rb'
  head
    title Publication List
  body
    h1 Publication List
    ol
      - YAML.load_file("publication.yaml").each do |d|
        li :dl
          dt = d.title
          dd == d.journal_ref
          dd = d.author
          - if d.arxiv
            dd == d.arxiv_ref

だいぶすっきりしましたね。

まとめ

軽量テンプレート言語Slimを使って、ローカルでYAMLファイルから論文リストを作ってみました。知っている人には簡単なのでしょうが、そのものずばりのサンプルがなくてちょっと苦労したのでここに公開しておきます。

ずいぶん昔、文献リストをXMLで管理していたことがありました。その時に「RubyとYAMLでやれば楽じゃない?」と言われたのですが、当時はPerlを使っていてRubyを使えなかったのと、将来XSLTを使う予定があったので、その時はXMLを選びました。でもXSLTは(僕には)難易度が高く、そのうち第一言語がRubyになったので、結局Ruby+YAMLでなんかすることが多くなりました(YAMLで履歴書とか)。

Slimは簡単で、かつRubyが埋め込めるので、HTMLを手書きに近い管理をしていて、かつRubyistな人(つまり私のような人)にはお勧めです。