DEV Community

Mitch Stanley
Mitch Stanley

Posted on • Edited on • Originally published at fullstackstanley.com

Adding RSS Feeds to a Lucky app

Just a quick post for anyone looking to implement RSS Feeds in Crystal Lucky Framework. This post works with Lucky 0.14.1 but it should work with 0.15 as well.

Thanks to @paulcsmith and @jeremywoertink for helping me work this out in Gitter.

If you haven't heard of Lucky, check out the website here. It's a web framework written in Crystal

First, create an Action that inherits from Lucky::Action. We’ll add a method called xml which can be called in each of your actions, passing in the data for the feed. The xml method will then create the xml string with Crystal’s built in XML Builder and iterate over the data that you pass in.

# src/actions/xml_action.cr require "xml" abstract class XMLAction < Lucky::Action def title "Website RSS Feed" end def description "Updates for Website" end def link "https://websiteurl.dev" end private def xml(articles : ArticleQuery) string = XML.build(indent: " ", encoding: "UTF-8") do |xml| xml.element( "rss", version: "2.0", "xmlns:dc": "http://purl.org/dc/elements/1.1/", "xmlns:content": "http://purl.org/rss/1.0/modules/content/", "xmlns:atom": "http://www.w3.org/2005/Atom", "xmlns:media": "http://search.yahoo.com/mrss/" ) do xml.element("channel") do xml.element("title") { xml.cdata title } xml.element("description") { xml.cdata description } xml.element("link") { xml.text link } xml.element("generator") { xml.text "Lucky Framework" } xml.element("lastBuildDate") { xml.text Time.utc_now.to_s } xml.element("atom:link") { xml.attribute "href", "#{link}#{request.path}" xml.attribute "rel", "self" xml.attribute "type", "application/rss+xml" } xml.element("ttl") { xml.text "60" } articles.each do |article| xml.element("item") do # title, description, link, category, dc:creator, pubDate, content:encoded xml.element("title") { xml.cdata article.title } if article.meta_description xml.element("description") { xml.cdata article.meta_description.not_nil! } end xml.element("link") { xml.text "#{link}articles/#{article.slug}" } xml.element("dc:creator") { xml.cdata "Author Name" } xml.element("pubDate") { xml.text article.created_at.to_s } if article.og_image xml.element("media:content") do xml.attribute "url", "article.og_image" xml.attribute "medium", "image" end end if article.content content = Markdown.to_html(article.content.not_nil!) xml.element("content:encoded") { xml.cdata content } end end end end end end Lucky::TextResponse.new(context, content_type: "text/xml; charset=utf-8", body: string status: 200) end end 

Although this abstract class looks quite large it’s not really doing much except generating the XML to output to the browser. By doing this it will greatly simplify our action classes.

In the example we’re passing in an ArticleQuery which you will need to implement in your own app. For reference, mine has a scope for published so I can keep unpublished articles from being viewed.

class ArticleQuery < Article::BaseQuery def published published_at.lte(Time.now) end end 

The Article model has the following attributes: title, meta_description (optional), slug, content (optional), og_image (optional). You will need to update this for your own purposes as well.

At the end of the XMLAction#xml method we’re using Lucky::TextResponse to send the XML string to the browser. Note that application/rss+xml is the proper content type to respond with, however, if you want it to be viewable in a web browser you need to use text/xml; charset=utf-8 (This is how Ghost does it at least).

Example Action

Since all the XML logic is in the XMLAction we can make our actions really clean. In the example below Rss::Index overrides the feed title and then passes in a list of published articles through the xml method.

# src/actions/rss/index.cr class Rss::Index < ::XMLAction def title "Latest Dailies" end get "/feeds/all.rss" do articles = ArticleQuery.new.published.published_at.desc_order xml articles end end 

Top comments (2)

Collapse
 
jwoertink profile image
Jeremy Woertink

This is great! You should tag this with lucky so it shows up in the list of all the other lucky posts :D

Collapse
 
mitchartemis profile image
Mitch Stanley

Done! 😀