Packages
mazurka
0.3.21
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.3.39
0.3.38
0.3.37
0.3.36
0.3.35
0.3.34
0.3.33
0.3.32
0.3.31
0.3.30
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
hypermedia api toolkit
Current section
Files
Jump to
Current section
Files
lib/mazurka/resource/link.ex
defmodule Mazurka.Resource.Link do
@moduledoc """
Represents a link in a response. This is used by mediatypes to serialize the link in the appropriate
format. It's broken into its separate parts (method, host, path, etc.) for easy manipulation.
"""
defstruct resource: nil,
mediatype: nil,
method: nil,
scheme: nil,
host: nil,
port: nil,
path: nil,
query: nil,
fragment: nil
def compile(_opts, _env) do
nil
end
def format_params(nil) do
{:%{}, [], []}
end
def format_params({:%{}, meta, items}) do
{:%{}, meta, Enum.map(items, fn({name, value}) ->
{to_string(name), value}
end)}
end
def format_params(items) when is_list(items) do
{:%{}, [], Enum.map(items, fn({name, value}) ->
{to_string(name), value}
end)}
end
def link_to(args, _conn, _parent, _ref, _attrs) do
try do
[module, params, query, fragment] = unwrap_args(args)
props = %{params: params, query: query, fragment: fragment}
## FOR BACKWARDS COMPATIBILITY - remove once markdown is removed
|> Dict.merge(params)
|> Dict.merge(%{"_params" => params, "_query" => query, "_fragment" => fragment})
{:partial, {module, :affordance_partial, props}}
catch
{:undefined_param, _} ->
{:ok, :undefined}
end
end
def transition_to([href, _, nil, nil], %{private: private} = conn, _parent, _ref, _attrs) when is_binary(href) do
{:ok, nil, %{conn | private: Dict.put(private, :mazurka_transition, href)}}
end
def transition_to(args, conn, parent, ref, attrs) do
case args |> unwrap_args |> resolve(conn, parent, ref, attrs) do
{:ok, :undefined} ->
{:error, :transition_to_unknown_location}
{:ok, affordance} ->
[to_string(affordance), nil, nil, nil]
|> transition_to(conn, parent, ref, attrs)
end
end
def invalidates([href, _, nil, nil], %{private: private} = conn, _parent, _ref, _attrs) when is_binary(href) do
invalidations = Map.get(private, :mazurka_invalidations, [])
{:ok, nil, %{conn | private: Map.put(private, :mazurka_invalidations, [href | invalidations])}}
end
def invalidates(args, conn, parent, ref, attrs) do
case args |> unwrap_args |> resolve(conn, parent, ref, attrs) do
{:ok, :undefined} ->
{:error, :invalidates_unknown_location}
{:ok, affordance} ->
[to_string(affordance), nil, nil, nil]
|> invalidates(conn, parent, ref, attrs)
end
end
defp unwrap_args([module, params, query, fragment]) do
[module, unwrap_ids(params), unwrap_ids(query), fragment]
end
defp unwrap_ids(kvs) when kvs in [nil, :undefined] do
kvs
end
defp unwrap_ids(kvs) do
Enum.reduce(kvs, %{}, fn
({key, %{"id" => id}}, acc) ->
Map.put(acc, to_string(key), to_string(id))
({key, %{id: id}}, acc) ->
Map.put(acc, to_string(key), to_string(id))
({key, value}, _) when value in [nil, :undefined] ->
throw {:undefined_param, key}
({key, value}, acc) ->
Map.put(acc, to_string(key), to_string(value))
end)
end
def encode_qs(params) do
out = Enum.filter_map(params, fn({_k, v}) ->
case v do
nil -> false
:undefined -> false
false -> false
"" -> false
_ -> true
end
end, fn({k, v}) ->
[k, "=", URI.encode_www_form(v)]
end)
|> Enum.join("&")
if out == "" do
nil
else
out
end
end
def resolve([module, params, query, fragment], %{private: private} = conn, _parent, _ref, _attrs) do
%{mazurka_router: router, mazurka_mediatype_handler: mediatype_module} = private
link = %__MODULE__{
resource: module,
mediatype: mediatype_module,
scheme: conn.scheme,
host: conn.host,
port: conn.port,
query: query,
fragment: fragment
}
link = case router.resolve(module, params) do
{:ok, method, scheme, host, path} ->
%{link | method: method,
scheme: scheme,
host: host,
path: request_path(%{conn | path_info: path})}
|> apply_link_transform(conn)
{:ok, method, path} ->
%{link | method: method,
path: request_path(%{conn | path_info: path})}
|> apply_link_transform(conn)
{:error, :not_found} ->
:undefined
end
{:ok, link}
end
defp apply_link_transform(link, conn = %{private: %{mazurka_link_transform: {module, function}}}) do
apply(module, function, [link, conn])
end
defp apply_link_transform(link, _) do
link
end
def from_conn(%{private: %{mazurka_mediatype_handler: mediatype_module}} = conn) do
%__MODULE__{mediatype: mediatype_module,
method: conn.method,
scheme: conn.scheme,
host: conn.host,
port: conn.port,
path: request_path(conn),
query: conn.query_string}
end
def from_conn(conn, path_info) do
from_conn(%{conn | path_info: path_info})
end
defp request_path(%{script_name: [], path_info: []}) do
"/"
end
defp request_path(%{script_name: script, path_info: path}) do
"/" <> Enum.join(script ++ path, "/")
end
end
defimpl String.Chars, for: Mazurka.Resource.Link do
def to_string(%{fragment: fragment, host: host, method: method, path: path, port: port, query: query, scheme: scheme}) do
%URI{fragment: format_fragment(fragment),
host: host,
path: format_path(path),
port: port,
query: format_query(query, method),
scheme: Kernel.to_string(scheme)}
|> Kernel.to_string
end
defp format_path(nil), do: nil
defp format_path(""), do: nil
defp format_path([]), do: nil
defp format_path("/"), do: nil
defp format_path(path) when is_list(path), do: "/" <> Enum.join(path, "/")
defp format_path(path), do: Kernel.to_string(path)
defp format_query(_, method) when method in ["POST", "PUT", "PATCH"], do: nil
defp format_query(nil, _), do: nil
defp format_query("", _), do: nil
defp format_query(%{__struct__: _} = qs, _), do: Kernel.to_string(qs)
defp format_query(qs, _) when is_map(qs), do: Mazurka.Resource.Link.encode_qs(qs)
defp format_query(qs, _), do: Kernel.to_string(qs)
defp format_fragment(nil), do: nil
defp format_fragment([]), do: nil
defp format_fragment(fragment) when is_list(fragment), do: "/" <> Enum.join(fragment, "/")
defp format_fragment(fragment), do: Kernel.to_string(fragment)
end