Packages

RSS processing library for Elixir language.

Current section

Files

Jump to
rss_flow lib rss_flow.ex
Raw

lib/rss_flow.ex

defmodule RssFlow do
@moduledoc """
This module uses output provided by XmlParser, transforms it to RSS-specific
format, filters it, and transforms to format that can be consumed by
XmlBuilder
"""
@doc """
Transform XML string or XmlParser format to RSS-specific format (a map).
"""
@spec parse(binary | {atom, map, list}) :: map
def parse(data) when is_binary(data) do
data |> XmlParser.parse |> parse
end
def parse(data) when is_tuple(data) do
do_parse data
end
defp do_parse({:rss, attributes, content}) do
[channel, items] = parse_channel(content)
%{
rss: attributes,
channel: channel,
items: items
}
end
defp parse_channel([{:channel, nil, content}]) do
%{false: channel, true: items} = group_items(content)
[Enum.into(parse_elements(channel), %{}), parse_elements(items)]
end
defp group_items(data) when is_list(data) do
Enum.group_by data, fn(tuple) -> elem(tuple, 0) == :item end
end
defp parse_elements([head | tail]) do
[parse_elements(head) | parse_elements(tail)]
end
defp parse_elements({:item, nil, item}) do
item |> parse_elements |> Enum.into(%{})
end
defp parse_elements({:image, nil, item}) do
{:image, item |> parse_elements |> Enum.into(%{})}
end
defp parse_elements([]), do: []
defp parse_elements({name, nil, value}), do: {name, value}
defp parse_elements({name, attributes, value}) do
{name, Map.merge(attributes, %{value: value})}
end
@doc """
Filter RSS items. Filter can be applied to a map representing XML or directly
to an XML string.
"""
@spec filter(map | binary, binary) :: map
def filter(data, pattern) when is_binary(data) do
data |> parse |> filter(pattern) |> generate |> XmlBuilder.doc
end
def filter(data, pattern) when is_map(data) do
%{
rss: data[:rss],
channel: data[:channel],
items: Enum.filter(
data[:items],
fn(item) -> contains_i?(item[:title] <> item[:description], pattern) end
)
}
end
defp contains_i?(string, pattern) do
string
|> String.downcase
|> String.contains?(String.downcase(pattern))
end
@doc """
Transform RSS-specific format to XmlBuilder format.
"""
@spec generate(map) :: {atom, map, list}
def generate(data) do
{
:rss,
data[:rss],
[{:channel, nil, generate_elements(data[:channel]) ++ generate_items(data[:items])}]
}
end
defp generate_elements(elements) do
elements
|> Enum.map(fn({name, raw_value}) -> generate_value(name, raw_value) end)
end
defp generate_value(name, raw_value) when is_binary(raw_value), do: {name, nil, raw_value}
defp generate_value(name, %{value: _} = raw_value) do
{value, attributes} = Map.pop(raw_value, :value)
{name, attributes, value}
end
defp generate_value(name, raw_value) when is_map(raw_value) do
{name, nil, generate_elements(raw_value)}
end
defp generate_items(items) do
items
|> Enum.map(fn(item) -> {:item, nil, generate_elements(item)} end)
end
end