We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
I like being able to subscribe to random websites using rss/atom feeds. I self host a miniflux instance, where I manage my subscriptions and read articles. So naturally I wanted to implement such a feed also for thepid.de. I mean how hard can that be?
Basically it involves two steps
- Implementing a function, which builds the feed
- Serving the feed as raw xml
The first step was straightforward as soon as I stumbled upon the Atomex
library. I just had to copy the example from the docs and make some adoptions regarding my schema.
alias Atomex.Entry
alias Atomex.Feed
def build_feed(posts) do
"https://thepid.de"
|> Feed.new(DateTime.utc_now(), "ThePid Feed")
|> Feed.author("THE", email: "thepid@mailbox.org")
|> Feed.link("https://thepid.de/feed", rel: "self")
|> Feed.entries(Enum.map(posts, &get_entry/1))
|> Feed.build()
|> Atomex.generate_document()
end
defp get_entry(post) do
inserted_at = DateTime.from_naive!(post.inserted_at, "Europe/Berlin", Tz.TimeZoneDatabase)
updated_at = DateTime.from_naive!(post.updated_at, "Europe/Berlin", Tz.TimeZoneDatabase)
content = Earmark.as_html!(post.content, %Earmark.Options{code_class_prefix: "language-", smartypants: false})
"https://thepid.de/blog/#{post.id}"
|> Entry.new(updated_at, "#{post.title}")
|> Entry.content(content, type: "html")
|> Entry.published(inserted_at)
|> Entry.build()
end
The build_feed
function builds the feed with entries, created through the get_entry
function.
I decided to include the whole article as html in the feed, so you can read everything through your reader application.
Unfortunately this is not possible with some feeds out there.
Now we have to serve the generated document at the /feed route. Therefor just add this line to your router.ex
scope "/", ThePidWeb do
pipe_through :browser
get "/feed", FeedController, :index
end
The router will try to hit the feed controller with an index function.
defmodule ThePidWeb.FeedController do
use ThePidWeb, :controller
plug :put_layout, false
plug :put_root_layout, false
def index(conn, _assigns) do
conn
|> put_resp_content_type("text/xml")
|> render(:feed)
end
end
Here I specified the response type as text/xml and removed all layouts.
Finally the render functions expects a view with a feed function.
This is were the build_feed
function is finally called.
defmodule ThePidWeb.FeedHTML do
use ThePidWeb, :html
alias ThePid.Blog
def feed(_assigns) do
Blog.get_all_posts()
|> Blog.build_feed()
|> Phoenix.HTML.raw()
end
end
That's it. Easy peasy. Subscribe now.