Atom 订阅
2007 年 1 月 2 日
(Sam Ruby 说服我使用日期的双精度,因此更新了。)
当我开始这个 bliki 时,在飞往班加罗尔的航班上,我决定使用 RSS 0.91 作为我的订阅源,因为它的简单性。我所要做的就是查看一个示例(恰好是 PragDave 的示例),我就可以轻松地创建 XML 来提供这种功能。它为我服务得很好,但我确实偶尔会收到一些抱怨,说帖子没有日期。
我咨询了 Ade Oshineye,他负责维护 ThoughtBlogs 的运行,询问了他的建议。他给了我一张纸,上面详细列出了坚持使用 RSS 0.91 还是选择新格式(以及选择哪种格式)的理由。最后,我遵循了他更热情的结论:“看在上帝的份上,请使用 Atom。”
长话短说,我现在有 Atom 订阅源。我仍然保留了 RSS 0.91 订阅源,但如果我需要做任何工作来维护它们,我将立即放弃它们。因此,我建议您尽快切换到 Atom 订阅源。我已经更新了网页上的引用,或者您可以在现在命名不当的 RssFeeds 页面中找到它们。
以下是关于转换的一些经验和想法。
在圣诞节期间,我尽可能地收集了有关 Atom 的信息。我的第一个想法是找到并使用 Ruby 库。Ruby 有一个非常成熟的订阅源处理库,叫做 FeedTools。它声称可以生成订阅源,我相信它可以。但是,所有文档都是关于使用和转换订阅源、将它们缓存到数据库中等等。它引入了一堆依赖项,而且如何使用它来仅仅创建订阅源并不明显。
因此,我决定自己创建 XML 文件。毕竟,这在 Ruby 中非常容易,尤其是现在我们有了很棒的 builder 库。
所以,下一个难题是弄清楚 Atom 文件是什么样的,以及各个部分的含义。我发现三件事对我非常有帮助。
- 我这个人就是这样,我总是想要一个真实的例子。我认为 Sam Ruby 的 订阅源应该是一个很好的示例。
- Ade 支持 Atom 的一个重要原因是它有一个可靠的 规范。像大多数规范一样,我浏览了一下,以回答我需要的问题。一般来说,我更喜欢从一个例子开始,然后逐渐调整它,直到它起作用,只有在遇到问题时才去看规范。这是一种典型的 傻瓜 行为。
- 也许使用 Atom 最好的理由是它有一个优秀的测试框架:feedvalidator。我发现它非常有用。
我有三个订阅源需要处理:我的更新订阅源、我的 bliki 订阅源和 refactoring.com 的订阅源。订阅源的数据来自不同的格式,因此这是一项常见但乏味的数据转换任务,从一种任意格式转换为另一种格式。许多企业软件都是这样的,这不是有趣的部分。
我首先创建了自己的订阅源和条目对象,作为 网关。这样,我就可以对对我来说有意义的对象进行编程,以便进行三种转换,并将 XML 转换以及 Atom 中可能出现的任何奇怪之处集中在一个地方。最初,我怀疑这是否值得,毕竟 builder 非常易于使用。我很快发现它非常值得。
大部分过程非常简单。我只是查看了如何创建 RSS 订阅源,然后对 Atom 订阅源做了同样的事情。(是的,我知道我应该对旧的 RSS 订阅源使用网关。我也有犯傻的时候。)真正棘手的部分是 Atom 订阅源中的一些新东西。
第一个是 id。Atom 要求您为每个条目提供一个 id。这使得聚合器更容易发现来自不同来源的同一条目的多个副本,或者只是判断新条目是真正的新条目还是更新的旧条目。对于我的 bliki 来说,选择一个 id 很容易 - 条目与 Web bliki 条目完全对应,所以我只使用了 bliki 条目的 URL。
对于新闻更新,没有特定的页面。查看 Sam Ruby 的页面,我发现他使用了标签。这些对我来说是新的,但谷歌搜索找到了一个 解释。我使用我的域名、日期和清理后的标题文本生成了标签 - 再次从 Sam Ruby 那里复制。
def calculate_atom_id specific = title.gsub(/\W/,'-') return "tag:#{domain_name},#{date.strftime("%Y-%m-%d")}:#{specific}" end
这背后的真正驱动力是添加日期,这引入了一些奇怪之处。第一个是 RFC 3339 日期,我不得不查找它们是如何工作的。看起来 Ruby 日期类没有方法返回 RFC 3339 日期,但经过一番摸索,我意识到 Time 类正好有我需要的东西,即 Time.xmlschema 方法。
规范中不清楚的一点是更新日期的真正含义。规范只是说它是“发布者认为重要的条目或订阅源修改的最新时间点”。当我更改 bliki 条目时,要么是为了更正错别字,要么是为了以某种方式修改条目。我并不认为错别字很重要。我确实希望聚合器更新他们对条目的副本,但我并不希望他们将其突出显示为新条目或更改的条目。对于后一种更改,我确实希望它们被突出显示。如果规范能给出一些关于聚合器和读者如何解释日期的建议,那就太好了 - 毕竟,正是这种解释传达了该字段的真正含义。我经常遇到这个问题,规范的编写者不愿意在标准中说明客户端应该做什么,因为他们不想限制客户端。我理解这种担忧,但我确实认为说明他们想象它可能如何使用以及一些场景非常有帮助。
对我来说,更新日期最尴尬的一点是更新日期的精度。Atom 规范说“日期值应该尽可能准确。例如,对于发布系统来说,将相同的日期戳应用于在同一天发布的多个条目通常是不合适的。”但是,我一直将我的更新视为具有日期精度的。我将条目上传到服务器的时间对我来说并不重要,重要的是我上传的日期。因此,我的时间戳反映了这一点 - 它们只提到了日期(实际上使用了 Ruby 的 Date 类,它是日期精度)。
我最初的想法是将它们保留为日期精度,为时间部分选择一个任意的 00:00Z 以满足 RFC 3339。Atom 规范说的是“应该”而不是“必须”,这在 StandardsSpeak 中是一个重要的区别,feedvalidator 将具有相同时间戳的两个条目标记为警告而不是错误。除非我能理解下游问题,否则我不明白为什么我应该花时间处理秒精度更新而不是日期精度。
Sam Ruby 提供了一个引人注目的场景。有些人,包括他,会聚合多个订阅源,并通过从最新开始,然后向后阅读,直到读到他们之前读过的内容来阅读它们。我的条目通常会比它们应该插入的时间日志更早插入,并且不会被阅读。(我可以给他们一个任意的延迟时间部分,这会使它们保持在列表的顶部,但这只会让读者感到厌烦。)
因此,我决定使用秒精度。我需要用 Ruby 的 Time 对象替换我用来处理时间戳的 Date 对象。我现在还需要开始在帖子中添加完整的时间,从这篇帖子开始,我将这样做。
我下载了 feedvalidator 的副本,以便在逐渐填充订阅源时对其进行测试。这很容易,尽管我不得不实际安装 raw 而不是仅仅使用 apt-get - 我想我正在变得软弱。
最后说一个故事。一两年前,一家非常大的软件公司(一家肯定生产您熟悉的软件的公司)问我是否介意将我的订阅源聚合到他们正在制作的架构订阅源中。我的回答,和往常一样,是“没问题,订阅源就是用来做这个的”。一两个月后,我收到一封电子邮件,说他们无法使用我的订阅源,我需要将其更改为 RSS 2.0。这比我想象的要费力,所以我拒绝了。但我忍不住笑了,这个大组织,显然已经建立了一个完整的项目来做这项工作,却无法做到 Ade 在业余时间为我们做的事情。