Current section

Files

Jump to
httpmock lib httpmock.ex
Raw

lib/httpmock.ex

defmodule HTTPMock do
@moduledoc """
Documentation for `HTTPMock`.
"""
defmacro __using__(opts) do
behaviour = opts[:behaviour] || :none
quote do
Module.register_attribute(__MODULE__, :routes, accumulate: true)
@behav unquote(behaviour)
@before_compile HTTPMock
import HTTPMock
require HTTPMock
end
end
@doc false
defmacro __before_compile__(env) do
routes = Module.get_attribute(env.module, :routes)
behav = Module.get_attribute(env.module, :behav)
defs =
for {method, url, module, function} <- routes do
list_url =
url
|> String.split("/")
|> Enum.map(fn x ->
if String.starts_with?(x, ":") do
v =
x
|> String.replace(":", "")
|> String.to_atom()
|> Macro.var(nil)
quote do: unquote(v)
else
x
end
end)
params =
list_url
|> Enum.reduce([], fn
{key, _, _} = x, acc -> [{to_string(key), x} | acc]
_, acc -> acc
end)
list_url_formated =
url
|> String.split("/")
|> Enum.map(fn x ->
if String.starts_with?(x, ":") do
nil
else
x
end
end)
quote do
def match_route(unquote(method), unquote(list_url), opts) do
body_params = (opts[:body_params] || %{}) |> HTTPMock.maybe_decode()
query_params = opts[:query_params] || %{}
method = unquote(method)
url = unquote(list_url_formated) |> Enum.join("/")
path_params = unquote(params) |> Map.new()
params =
body_params
|> Map.merge(query_params)
|> Map.merge(path_params)
conn = %{
url: url,
body_params: body_params,
path_params: path_params,
query_params: query_params,
method: method
}
apply(unquote(module), unquote(function), [conn, params])
end
end
end
default_dipatch =
quote do
def match_route(method, list_url, _) do
url = list_url |> Enum.join("/")
raise("Route `#{String.upcase(to_string(method))} #{url}` not defined")
end
end
defs = defs ++ [default_dipatch]
behaviour =
case behav do
:httpoison -> HTTPMock.Behaviours.HTTPoison.funs()
_ -> nil
end
quote do
def match_route(method, list_url), do: match_route(method, list_url, [])
unquote_splicing(defs)
def match(_method, _url, _opts \\ [])
def match(method, url, opts) when is_binary(url) and is_atom(method) do
{base, params} = HTTPMock.parse_url(url)
match_route(method, String.split(base, "/"), opts ++ [query_params: params])
end
def match(method, url, _),
do: raise("Route `#{String.upcase(to_string(method))} #{url}` not defined")
unquote(behaviour)
end
end
def parse_url(url) do
String.split(url, "?")
|> case do
[base, params] -> {base, URI.decode_query(params)}
_ -> {url, %{}}
end
end
defmacro endpoint(path, do: {:__block__, [], body}), do: do_endpoint(path, body)
defmacro endpoint(path, do: body), do: do_endpoint(path, body)
def do_endpoint(path, body) do
body =
Macro.postwalk(body, fn
x when is_bitstring(x) -> {:merge_url, [], [path, x]}
x -> x
end)
quote do
unquote(body)
end
end
defmacro merge_url(base, path) do
quote do
unquote(base) <> unquote(path)
end
end
defmacro get(path, module, function) do
quote do
Module.put_attribute(
__MODULE__,
:routes,
{:get, unquote(path), unquote(module), unquote(function)}
)
end
end
defmacro post(path, module, function) do
quote do
Module.put_attribute(
__MODULE__,
:routes,
{:post, unquote(path), unquote(module), unquote(function)}
)
end
end
defmacro put(path, module, function) do
quote do
Module.put_attribute(
__MODULE__,
:routes,
{:put, unquote(path), unquote(module), unquote(function)}
)
end
end
defmacro delete(path, module, function) do
quote do
Module.put_attribute(
__MODULE__,
:routes,
{:delete, unquote(path), unquote(module), unquote(function)}
)
end
end
def maybe_decode(body) when is_binary(body) do
JSON.decode(body)
|> case do
{:ok, body} -> body
_ -> %{}
end
end
def maybe_decode(body) when is_map(body), do: body
end