フィードの手書きに関して

ウェブフィードを手書きすることに関しての雑多なメモです。

自動生成ならいざ知らず、一から書くとなると仕様の確認が必要です。ウェブフィードの形式は[RDF Site Summary 1.0](https://web.resource.org/rss/1.0/ "web.resource.org") (RSS 1.0)、[Really Simple Syndication 2.0](https://www.rssboard.org/rss-2-0 "RSS Advisory Board") (RSS 2.0)、[Atom Syndication Format](https://www.rfc-editor.org/rfc/rfc4287.html "IETF")が代表的でしょう。以降、この記事では基本的にAtomを題材にします。また、ウェブフィードのことを単にフィードを呼ぶことにします。

Atomの形式について、RFC以外で読みやすいところでは[Feed Validation Service - Atom](https://validator.w3.org/feed/docs/atom.html "W3C")というページもあります。

フィードはよくウェブサイトの更新を知らせるために使われていますが、そうでない用途でも使われることがあります。一方通行ではありますが、いわゆるマイクロブログのような使い方も見られます。私がこのような発想のものを初めて見たのは[Unproductive Fun Time](http://gaarabis.free.fr/ "Unproductive Fun Time")の[RSS 2.0フィード](http://gaarabis.free.fr/rssfeed.xml "Unproductive Fun Time")だったと思います。

自分でAtomファイルを書くことを考えます。大枠としては以下になるでしょう。項目を追加するときは、`/feed`以下に`entry`要素を加えて、`/feed/updated`を更新します。

``` xml
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://FQDN/path/to/feed</id>
  <title>題名</title>
  <updated>2025-01-01T00:00:00+09:00</updated>
  <entry>
    <link href="https://FQDN/path/to/entry"/>
    <title>題名</title>
    <author><name>著者</name></author>
    <id>識別子</id>
    <updated>2024-01-01T00:00:00+09:00</updated>
  </entry>
  <!-- 以降も好きなだけentry要素を書く -->
</feed>
```

問題なのは、日時の形式が厳密に決まっているなど、毎回手入力するのが億劫なことです。これまでに3つの解決策を試してきました。

* RubyでAtomフィードを生成する領域特化言語を作り、その言語を使って作成
* AtomフィードでDocument Type Definition (DTD) による実体を定義
* テキストエディタのスニペット機能を利用

領域特化言語を使うと、ソースファイルが見やすく、日付の扱いも楽になるでしょう。既定の振舞いを決めておくことで略記が捗ります。なお、スクリプトを走らせる一工程は要ります。

実体の定義の例は以下のような感じで、特に日時については時刻を決め打ちできます。

``` xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE feed [
<!ENTITY time "T00:00:00+09:00">
<!ENTITY self "https://FQDN/path/to/feed#">
]>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://FQDN/path/to/feed</id>
  <title>題名</title>
  <updated>2025-01-01&time;</updated>
  <generator>手作り</generator>
  <entry>
    <link href="https://example.com"/>
    <title>題名</title>
    <author><name>著者</name></author>
    <id>&self;2024-01-01-foo</id>
    <updated>2024-01-01&time;</updated>
  </entry>
  <!-- ここに好きなだけentry要素を書く -->
</feed>
```

テキストエディタにスニペット機能がある場合、項目の追加が楽に行える可能性があります。ここではEmacsのYASnippetによる方法を示します。書き方は[Yet another snippet extension - Writing snippets](https://joaotavora.github.io/yasnippet/snippet-development.html "YASnippet")をご参照ください。

``` yasnippet
# -*- mode: snippet -*-
# name: new Atom entry
# key: entry
# --
<entry>
  <link href="$1"/>
  <title>$2</title>
  <author><name>$3</name></author>
  <id>&self;`(format-time-string "%Y-%m-%d")`-$4</id>
  <updated>`(format-time-string "%Y-%m-%dT%H:%M:%S+09:00")`</updated>
</entry>
```

上のスニペットでは、例えば以下の☆に地点があるとき、`entry<TAB>`とすると展開されます。

``` xml
<feed xmlns="http://www.w3.org/2005/Atom">
  ...
  <updated>2024-01-01&time;</updated>
  ☆
  <entry>
...
```

展開後は以下。☆が展開後の地点です。入力し終わったら`<TAB>`とすることで★の位置に地点が移動していきます。

``` xml
<feed xmlns="http://www.w3.org/2005/Atom">
  ...
  <updated>2025-01-01&time;</updated>
  <entry>
    <link href="☆"/>
    <title>★</title>
    <author><name>★</name></author>
    <id>&self;2024-01-01-★</id>
    <updated>2024-01-01T20:51:46+09:00</updated>
  </entry>
  <entry>
...
```

フィードを書いたら、それが正しい形式に従っているか確認する必要があります。これは、フィードを読む側でエラーになることを防ぐためです。XMLの構造的な検証の方法は色々あり、RELAX NGはその1つです。具体例として、フィードの形式を表すRELAX NGコンパクトスキーマファイルを入手し、EmacsのnXMLモードで設定する方法を示します。

AtomはRFCでRELAX NGコンパクトスキーマが付録されており、そこから抽出したファイルを[atom-schema](https://github.com/gemmaro/atom-schema "gemmaro/atom-schema")のリリースアセットで配布しています。RSSは仕様を参考に作成途中のものが[rss-schema](https://codeberg.org/gemmaro/rss-schema "gemmaro/rss-schema")にあります。よろしければお使いください。nXMLのスキーマの配置場所の設定ファイルは次のようにします。

``` xml
<?xml version="1.0"?>
<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
  <namespace uri="path/to/atom.rnc" ns="http://www.w3.org/2005/Atom" />
  <documentElement uri="path/to/rss2.rnc" localName="rss" />
</locatingRules>
```

少しだけフィードを手書きしやすくする工夫の紹介でした。

---

Copyright (c)  2025  gemmaro.

Permission is granted to copy, distribute and/or modify this document under the terms of the [GNU Free Documentation License, Version 1.3](https://www.gnu.org/licenses/fdl-1.3.html) or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.