Packages
tableau
0.26.1
0.30.0
0.29.0
0.28.0
0.27.1
0.27.0
0.26.1
0.26.0
0.25.1
0.25.0
0.24.1
0.24.0
0.23.1
0.23.0
0.22.0
0.21.0
0.20.1
0.20.0
0.19.0
0.18.0
0.17.1
0.17.0
0.16.0
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.0
0.12.0
0.11.1
0.11.0
0.10.1
0.10.0
0.9.0
0.8.0
0.7.1
0.7.0
0.6.0
0.5.0
0.4.0
0.3.1
0.3.0
0.2.0
0.1.2
0.1.1
0.1.0
Static site generator for elixir
Current section
Files
Jump to
Current section
Files
lib/tableau/extensions/rss_extension.ex
defmodule Tableau.RSSExtension do
@moduledoc """
Generate one or more RSS feeds.
## Configuration
Configuration is a keyword list of configuration lists with the key being the name of the XML file to be generated.
- `:enabled` - boolean - Extension is active or not.
- `:title` - string (required) - Title of your feed.
- `:description` - string (required) - Description of your feed.
- `:language` - string - Language to use in the `<language>` tag. Defaults to "en-us".
- `:include` - keyword list - List of front matter keys and values to include in the feed. If a post has any value in the list, it'll be included. Defaults to including everything.
- `:exclude` - keyword list - List of front matter keys and values to exclude from the feed. If a post has any value in the list, it'll be included. Defaults to not excluding anything.
### Example
```elixir
config :tableau, Tableau.RSSExtension,
enabled: true,
feeds: [
super: [
enabled: true,
language: "pt-BR",
title: "The Super Feed",
description: "Includes all posts on the site"
],
posts: [
enabled: true,
language: "pt-BR",
title: "My Elixir Devlog",
description: "My Journey on Becoming the Best Elixirist",
exclude: [category: "til"] # excludes posts with this category
],
til: [
enabled: true,
language: "en-US",
title: "Today I Learned",
description: "Short log of what I learn every day",
include: [category: "til"]
]
]
```
"""
use Tableau.Extension, key: :rss, priority: 200, enabled: false
import Schematic
@impl Tableau.Extension
def config(config) do
unify(
oneof([
map(%{enabled: false}),
feed_s(&map/1),
map(%{
enabled: true,
feeds: keyword(values: feed_s(&keyword/1))
})
]),
config
)
end
defp feed_s(type) do
type.(%{
optional(:enabled, true) => bool(),
optional(:language, "en-us") => str(),
optional(:include) => include_s(),
optional(:exclude) => include_s(),
title: str(),
description: str()
})
end
defp include_s do
keyword(values: list(str()))
end
@impl Tableau.Extension
def post_write(
%{site: %{config: %{url: url, out_dir: out_dir}}, posts: posts, extensions: %{rss: %{config: feeds}}} = token
) do
feeds =
if Map.has_key?(feeds, :feeds) do
feeds.feeds
else
[feed: feeds]
end
for {name, feed} <- feeds, feed[:enabled] do
feed = Map.new(feed)
prelude =
"""
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<atom:link href="#{url}/#{name}.xml" rel="self" type="application/rss+xml" />
<title>#{HtmlEntities.encode(feed.title)}</title>
<link>#{url}</link>
<description>#{HtmlEntities.encode(feed.description)}</description>
<language>#{feed.language}</language>
<generator>Tableau v#{version()}</generator>
"""
items =
for post <- posts,
feed[:include] == nil || filter(post, feed[:include]),
feed[:exclude] == nil || not filter(post, feed[:exclude]),
into: "" do
"""
<item>
<title>#{HtmlEntities.encode(post.title)}</title>
<link>#{URI.merge(url, post.permalink)}</link>
<pubDate>#{Calendar.strftime(post.date, "%a, %d %b %Y %X %Z")}</pubDate>
<guid>#{URI.merge(url, post.permalink)}</guid>
<description><![CDATA[ #{post.renderer.(token)} ]]></description>
</item>
"""
end
# html
postlude =
"""
</channel>
</rss>
"""
File.mkdir_p!(out_dir)
File.write!("#{out_dir}/#{name}.xml", prelude <> items <> postlude)
end
{:ok, token}
end
defp version do
case :application.get_key(:tableau, :vsn) do
{:ok, version} -> to_string(version)
_ -> "dev"
end
end
defp filter(post, include_filter) do
Enum.any?(include_filter, fn {front_matter_key, values_to_accept} ->
post[front_matter_key]
|> List.wrap()
|> Enum.any?(fn front_matter_key ->
front_matter_key in List.wrap(values_to_accept)
end)
end)
end
end