Falvotech.
Conquering the world is easy — what do you do with it afterwards?

L I N K S


⇐ Unsuitable on Unsuitable: Index Generation

⇒ Unsuitable on Unsuitable: Rendering Articles


Unsuitable on Unsuitable: RSS Feed Generation
Samuel A. Falvo II
kc5tja -at- arrl.net
2010 Jul 22 23:00 PDT

Consumers rarely seek new information from their primary sources. Over the years, particularly as newspapers and magazines have revised their business models to include direct delivery as a part of their subscription program, consumers have grown accustomed to having information delivered directly to them. Unsuitable caters to this established custom by offering an RSS feed. Readers may use whatever aggregation software they please to view Unsuitable content when it becomes available, without having to visit a plurality of websites.

1 Introduction

I cannot live without my Google Reader. Of all the RSS aggregators available, the convenience and overall functionality of Reader keeps me coming back. I subscribe to tons of blogs and web comics. Having to visit the web sites of each content producer would require substantial amounts of time, and a bookmarks menu longer than the height of my screen. Besides, and even thankfully, not everyone updates their website every time I check my subscriptions. RSS readers clearly save time, by providing a single source of content for all my subscriptions, and saving me the wasted time of checking sites which haven't updated yet.

If I desired RSS capability from the websites I regularly visit, then I knew others would demand similar functionality from Unsuitable. Thus, I implemented an Unsuitable endpoint just for this purpose.

2 Code Walk-Through

The RSS standards dictate the use of XML to format feed data, so we inform the reader of this format.

.( Content-type: application/rss+xml) cr cr

Since our RSS feed generates up to sixteen entries, it makes sense to collate up to sixteen of the most recent blog posts. We'll re-use our insertion sort implementation to provide this information. This will produce an array of 16 article IDs.

create e0   16 cells allot
e0 16 cells -1 fill
e0 15 cells + constant en-1
en-1 cell+ constant en
variable ep
include latest.fs
scan

Unfortunately, the RSS feed requires text, not article IDs. Therefore, we convert the array of 16 article IDs into a corresponding number of leads. The RSS standards require us to escape significant HTML characters. Escaping characters directly out of the GOS requires a more sophisticated implementation which breaks the GOS encapsulation, so we cache the leads in dictionary space for later consumption by encode.

create p0   17 cells allot
variable pp
: exists  ep @ @ article! lead gob! get, ;
: c       ep @ @ -1 xor if exists then [ 1 cells ] literal dup pp +! ep +! here pp @ ! ;
: 16c     c c c c  c c c c  c c c c  c c c c ;
: cache   e0 ep ! p0 pp ! here p0 ! 16c ;
cache

Notice that the p vector holds 17 addresses, not 16. Unsuitable lays the text excerpts down in memory sequentially; so, we can refer to the address of any lead L via pL and derive its length by evaluating pL+1pL. The vector length ensures we never overflow the buffer even with a full set of sixteen articles in the feed.

We define a macro, 16items, invoked in the RSS XML template file. This will expand to a collection of tags within the RSS structure.

: 16items       >web e0 ep ! p0 pp ! 16items >con ;

We render the sixteen items by walking the article ID table, looking for each occupied slot. For each valid entry in the sort, we emit an item tag and its required nested sub-tags. This way, if fewer than sixteen articles exist, we don't produce bogus RSS feed data.

: title         ." <title>" title gob! get, ." </title>" ;
: (link)        ." http://www.falvotech.com/blog2/blog.fs/articles/" articleId . ;
: link          ." <link>" (link) ." </link>" ;
: ?more         body -1 xor if ." &lt;p&gt;&lt;i&gt;(continued . . .)&lt;/i&gt;&lt;/p&gt;" then ;
: description   ." <description>" encode ?more ." </description>" ;
: author        ." <author>kc5tja@arrl.net (Samuel A. Falvo II)</author>" ;
: guid          ." <guid>" (link) ." </guid>" ;
: pubDate       ." <pubDate>" timestamp .time822 ." </pubDate>" ;
: item          ." <item>" title link description author guid pubDate ." </item>" ;
: i             ep @ @ -1 xor if ep @ @ article! item then [ 1 cells ] literal ep +! ;
: 16items       i i i i  i i i i  i i i i  i i i i ;

The RSS standards require we encode all HTML escape characters.

: -"          dup [char] " = if drop ." &quot;" r> drop then ;
: -&          dup [char] & = if drop ." &amp;" r> drop then ;
: -<          dup [char] < = if drop ." &lt;" r> drop then ;
: ->          dup [char] > = if drop ." &gt;" r> drop then ;
: convert     -" -& -< -> emit ;
: translate   begin 2dup < while over c@ convert swap char+ swap repeat 2drop ;
: encode      pp @ dup @ swap cell+ @ translate [ 1 cells ] literal pp +! ;

You'll notice that 16items concerns itself only with the e vector pointer ep, while encode works with the p vector pointer pp. Because we populated p based on the contents of e, the two vectors correspond precisely. Thus, we need no pointer arithmetic to convert one vector reference to another — the pointers advance in lock-step.

We also provide a macro for printing out a RSS-compatible timestamp.

: pubDate       >web now .time822 >con ;

Finally, we introduce code to actually perform the template expansion.

variable s
variable end
here s !  s" theme/rss.xml" slurp  here end !
include response.fs
bye

3 What's Next

As you can see, the m-rss.fs module follows a structure not entirely unlike m-index.fs. Unsuitable uses dictionary space analogously to any typical reverse-Polish notation evaluation of the hypothetical algebraic equation

rssFeed = expand("theme/rss.xml", map(λl.encode(lead(l)), take(16, sort(articles)))).

We have one more module to discuss for Unsuitable: m-articles.fs. Regrettably, you'll need to wait until week after next for the next installment, for I intend on enjoying a nice week-long vacation in Boulder, Colorado. Until then, hang tight; I'll work to complete this series upon my return.

4 Source Listing for m-rss.fs

require mappings.fs
require general.fs
require articles.fs
require slurp.fs
require time.fs
require respond.fs

.( Content-type: application/rss+xml) cr cr

create e0   16 cells allot
e0 16 cells -1 fill
e0 15 cells + constant en-1
en-1 cell+ constant en
variable ep
include latest.fs
scan


create p0   17 cells allot
variable pp
: exists  ep @ @ article! lead gob! get, ;
: c       ep @ @ -1 xor if exists then [ 1 cells ] literal dup pp +! ep +! here pp @ ! ;
: 16c     c c c c  c c c c  c c c c  c c c c ;
: cache   e0 ep ! p0 pp ! here p0 ! 16c ;
cache


: -"          dup [char] " = if drop ." &quot;" r> drop then ;
: -&          dup [char] & = if drop ." &amp;" r> drop then ;
: -<          dup [char] < = if drop ." &lt;" r> drop then ;
: ->          dup [char] > = if drop ." &gt;" r> drop then ;
: convert     -" -& -< -> emit ;
: translate   begin 2dup < while over c@ convert swap char+ swap repeat 2drop ;
: encode      pp @ dup @ swap cell+ @ translate [ 1 cells ] literal pp +! ;

: title         ." <title>" title gob! get, ." </title>" ;
: (link)        ." http://www.falvotech.com/blog2/blog.fs/articles/" articleId . ;
: link          ." <link>" (link) ." </link>" ;
: ?more         body -1 xor if ." &lt;p&gt;&lt;i&gt;(continued . . .)&lt;/i&gt;&lt;/p&gt;" then ;
: description   ." <description>" encode ?more ." </description>" ;
: author        ." <author>kc5tja@arrl.net (Samuel A. Falvo II)</author>" ;
: guid          ." <guid>" (link) ." </guid>" ;
: pubDate       ." <pubDate>" timestamp .time822 ." </pubDate>" ;
: item          ." <item>" title link description author guid pubDate ." </item>" ;
: i             ep @ @ -1 xor if ep @ @ article! item then [ 1 cells ] literal ep +! ;
: 16items       i i i i  i i i i  i i i i  i i i i ;

: 16items       >web e0 ep ! p0 pp ! 16items >con ;
: pubDate       >web now .time822 >con ;

variable s
variable end
here s !  s" theme/rss.xml" slurp  here end !
include response.fs
bye

5 Source Listing for theme/rss.xml

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
 <channel>
  <title>Samuel's All New New News News</title>
  <link>http://www.falvotech.com/blog</link>
  <description>Touching upon computers, electronics, programming, and
    other matters of personal interest to Samuel.</description>
  <language>en</language>
  <generator>Unsuitable</generator>
  <pubDate>~pubDate </pubDate>
  ~16items
  <atom:link href="http://www.falvotech.com/blog2/blog.fs/rss" rel="self" type="application/rss+xml" />
 </channel>
</rss>