Current section
Files
Jump to
Current section
Files
lib/feedex.ex
defmodule Feedex do
@moduledoc """
A simple elixir feed parser.
"""
require Logger
alias Feedex.Helpers.{Fetch, Xml, Sanitizer}
alias Feedex.Parsers.{Atom, RSS1, RSS2}
@doc """
Parse feed.
## Examples
iex> Feedex.parse url
{:ok, feed}
# {:error, :empty_url} -> Logger.warn "empty url"
# {:error, :fetch_error} -> Logger.warn "fetch error"
# {:error, :invalid_xml} -> Logger.warn "invalid xml"
# {:error, :unknown_feed_format} -> Logger.warn "unknown feed format"
# {:error, :unknown} -> Logger.error "Unknown error catched"
"""
@spec parse(String.t) :: String.t
def parse(url) do
try do
with {:ok, content} <- Fetch.get(url),
{:ok, xml} <- Xml.preprocess(content),
{:ok, parser} <- select_parser(xml),
{:ok, feed} <- parser.parse(xml) do
base_url = extract_base_url(feed.url)
entries =
feed.entries
|> Enum.filter(fn(e) -> e.title != nil && e.content != nil end)
|> Enum.sort(&(DateTime.compare(&1[:updated], &2[:updated]) != :gt))
|> Enum.map(&sanitize_entry(&1, base_url))
{:ok, %{feed | entries: entries}}
end
rescue
e -> {:error, :unknown, e}
end
end
# --
defp select_parser(xml) do
cond do
Atom.valid? xml -> {:ok, Atom}
RSS1.valid? xml -> {:ok, RSS1}
RSS2.valid? xml -> {:ok, RSS2}
true -> {:error, :unknown_feed_format}
end
end
defp extract_base_url(url) do
%{URI.parse(url) | path: "/"} |> URI.to_string
end
defp sanitize_entry(entry, base_url) do
content =
(entry.content || "")
|> String.replace(~r/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/i, "")
|> Sanitizer.basic_html
|> String.replace(~r/<\s*img\ssrc\s*=\s*(['"])\//i, "<img src=\\1#{base_url}")
%{entry | content: content}
end
end