Mix.install([
{:tesla, "~> 1.7"},
{:fast_xml, "~> 1.1"}
])
num_posts = 4
doc = Tesla.get!("https://fly.io/phoenix-files/feed.xml").body
defmodule FlyFeedRecursive do
def parse(doc, n \\ 4) do
doc
|> :fxml_stream.parse_element()
|> parse_el(n)
end
def parse_el({:xmlel, "feed", _, doc}, n) do
parse_el([], doc, n)
end
def parse_el(acc, _, n) when length(acc) == n do
acc
end
def parse_el(acc, [], _n) do
acc
end
def parse_el(acc, [{:xmlel, "entry", _, entry} | doc], n) do
entry =
Enum.map(entry, fn
{:xmlel, key, attr, child} ->
{key, node_to_val(attr, child)}
_ ->
nil
end)
|> Enum.reject(&is_nil/1)
|> Enum.into(%{})
parse_el([entry | acc], doc, n)
end
def parse_el(acc, [_ | doc], n) do
parse_el(acc, doc, n)
end
def node_to_val(attr, children)
def node_to_val(_, [{:xmlcdata, val}]), do: val
def node_to_val(attr, children) do
if href = has_href?(attr) do
href
else
%{attr: attr, children: children}
end
end
def has_href?(list) do
Enum.find_value(list, nil, fn
{"href", val} -> val
_ -> false
end)
end
end
FlyFeedRecursive.parse(doc, num_posts)
defmodule FlyFeed do
def parse(doc, n \\ 4) do
doc
|> :fxml_stream.parse_element()
|> parse_el(n)
end
def parse_el({:xmlel, "feed", _, doc}, n) do
Enum.reduce_while(doc, [], fn
_doc, acc when length(acc) == n ->
{:halt, acc}
{:xmlel, "entry", _, entry}, acc ->
entry = handle_entry(entry)
{:cont, [entry | acc]}
_, acc ->
{:cont, acc}
end)
end
def handle_entry(entry) do
Enum.map(entry, fn
{:xmlel, key, attr, child} ->
{key, node_to_val(attr, child)}
_ ->
nil
end)
|> Enum.reject(&is_nil/1)
|> Enum.into(%{})
end
def node_to_val(attr, children)
def node_to_val(_, [{:xmlcdata, val}]), do: val
def node_to_val(attr, children) do
if href = has_href?(attr) do
href
else
%{attr: attr, children: children}
end
end
def has_href?(list) do
Enum.find_value(list, nil, fn
{"href", val} -> val
_ -> false
end)
end
end
FlyFeed.parse(doc, num_posts)
Here's my updated module for now:
it reverses the final output, handles author name a bit better, removed the way we handle href, etc.. This is the output.