|
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 1 IntroductionI 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 2 Code Walk-ThroughThe 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+1 − pL. 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 ." <p><i>(continued . . .)</i></p>" 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 ." "" r> drop then ; : -& dup [char] & = if drop ." &" r> drop then ; : -< dup [char] < = if drop ." <" r> drop then ; : -> dup [char] > = if drop ." >" 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 NextAs 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.fsrequire 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 ." "" r> drop then ; : -& dup [char] & = if drop ." &" r> drop then ; : -< dup [char] < = if drop ." <" r> drop then ; : -> dup [char] > = if drop ." >" 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 ." <p><i>(continued . . .)</i></p>" 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>
|