joeldueckdotcom

RSS Library for Racket

This a nascent Coding and publishing project. It doesn’t exist yet, and it’s something I’ve had to do manually more than once.

Purposes (in order of importance and implementation):

  1. Convenient generating of valid, best-practice Atom 1.0 feeds suitable for blogging.
  2. Same, but for podcast feeds
  3. Parsing RSS feeds
  4. Validating RSS feeds. This might never happen. The W3C already has a reference validator, though a Racket implementation might be an interesting exercise.

You should only have to supply what is absolutely necessary to generate the elements required by the spec.

Everything you supply is validated, so the result is always either a valid feed or an exception.

The library will actually be a bit more opinionated than the spec about how your feed is constructed. This supports the convenience aspect because you have fewer decisions to make, and the validation aspect because there are fewer problems to look for.

Approach

I have pretty well fleshed out a constructs.rkt for making valid basic constructs: the Atom “Person” (which in turn implies email addresses and URIs), tag URIs for feed and entry IDs (which the spec doesn’t require but I will), as well as validating email addresses, DNS domains, and URIs. I’m having a hard time settling on names for these things.

I originally forgot that supporting podcast feeds will mean having to support RSS 2.0 as well as Atom, due to Apple’s podcast feed requirements. Hopefully what I’ve done so far will generalize. The RSS 2.0 specs look a lot sloppier than the Atom spec.

Useful links

Example

#lang racket

(require rss)

(define me
  (Person "Joel" "joel@me.com" "https://joeldueck.com")

(define/contract (make-entry src)
  (path-string? . -> . atom:entry?)
  (define doc (get-doc src))
  (define metas (get-doc metas))

  (atom:entry (hash-ref metas 'title)
              #:updated (hash-ref metas 'updated)
              #:author me
              #:generator "Racket"
              #:content (cdata (->html doc))))

; A <link rel='self'> can be derived from the #:feed-uri argument 
(define feed 
  (make-rss-feed 
    "Posts on joel dueck dot com"
    #:feed-uri "https://joeldueck.com/feed.atom"

    ; contract will specify atom:tag-uri? for the id
    #:feed-id (mint-tag "joeldueck.com" "2020" "/")

    ; likewise: list of atom:entry? required here
    #:entries (map make-entry post-sources))

; If you want, you can get the x-expression and fiddle with it before output :-/
(construct-xexpr feed)
; or, just:
(display-to-file "feed.xml" (construct-xhtml feed) #:exists 'replace)