Current section

Files

Jump to
afterbuy lib xml encoder.ex
Raw

lib/xml/encoder.ex

defprotocol Afterbuy.XML.Encoder do
@moduledoc """
Protocol for building XML content. A proxy to Saxy.Builder protocol
"""
@doc """
Encode to XML string. Proxy to Saxy.encode!
"""
@spec encode!(content :: term()) :: binary
def encode!(content)
@doc """
Sanitize value
"""
@spec sanitize(content :: term()) :: any
def sanitize(content)
end
defimpl Afterbuy.XML.Encoder, for: Any do
defmacro __deriving__(module, struct, options) do
name_option =
Keyword.get(
options,
:name,
module
|> Module.split()
|> List.last()
)
attributes_option = Keyword.get(options, :attributes, [])
children_option = Keyword.get(options, :children, [])
allowed_children =
struct
|> Map.keys()
|> Enum.filter(&(&1 != :__struct__))
children_names =
allowed_children
|> Enum.reduce([], &Keyword.put(&2, &1, &1 |> Inflex.camelize() |> String.to_atom()))
|> Keyword.merge(Keyword.get(options, :names, []))
quote location: :keep do
defimpl Saxy.Builder, for: unquote(module) do
@name_option unquote(name_option)
@attributes_option unquote(attributes_option)
@children_option unquote(children_option)
@allowed_children unquote(allowed_children)
@children_names unquote(children_names)
def build(struct) do
import Saxy.XML
alias Afterbuy.XML.Encoder
build_children = fn k, struct ->
if Map.has_key?(struct, k) do
v = Map.get(struct, k, nil)
@children_names
|> Keyword.get(k)
|> element([], Encoder.sanitize(v))
else
Map.get(struct, k, nil)
end
end
name =
case @name_option do
{:attr, n} -> Map.get(struct, String.to_atom(n))
n -> n
end
attributes =
struct
|> Map.take(@attributes_option)
|> Enum.to_list()
children =
@children_option
|> Enum.map(fn el ->
if el in @allowed_children do
build_children.(el, struct)
else
raise ArgumentError,
message:
"The children #{el} is invalid. Allowed filters are: " <>
"#{Enum.join(@allowed_children, ", ")}"
end
end)
element(name, attributes, children)
end
end
end
end
def encode!(data), do: Saxy.encode!(data)
def sanitize(value), do: value
end
defimpl Afterbuy.XML.Encoder,
for: [
Tuple,
BitString,
Atom,
Integer,
Float,
NaiveDateTime,
DateTime,
Date,
Time
] do
def encode!(data), do: Saxy.encode!(data)
def sanitize(v) when is_tuple(v) or is_list(v), do: v
def sanitize(v) when is_atom(v),
do: sanitize(Atom.to_string(v))
def sanitize(v),
do: {
if is_nil(Regex.run(~r/^[0-9A-Za-z]+$/, v)) do
:cdata
else
:characters
end,
v
}
end