Packages
tesla
0.3.3
1.20.0
1.18.3
1.18.2
1.18.1
1.18.0
1.17.0
1.16.0
1.15.3
1.15.2
1.15.1
1.15.0
1.14.3
1.14.2
1.14.1
1.14.0
1.13.2
1.13.1
1.13.0
1.12.3
1.12.2
1.12.1
1.12.0
1.11.2
1.11.1
1.11.0
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.1
retired
1.8.0
1.7.0
1.6.1
1.6.0
1.5.1
1.5.0
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.0
1.0.0
1.0.0-beta.1
0.10.0
0.9.0
0.8.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.2
0.5.1
0.5.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.2.2
0.2.1
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
HTTP client library, with support for middleware and multiple adapters.
Current section
Files
Jump to
Current section
Files
lib/tesla.ex
defmodule Tesla.Env do
defstruct url: "",
method: nil,
status: nil,
headers: %{},
body: nil,
opts: [],
_client: nil,
_module: nil
end
defmodule Tesla.Builder do
@http_methods [:get, :head, :delete, :trace, :options, :post, :put, :patch]
defmacro __using__(_what) do
method_defs = for method <- [:get, :head, :delete, :trace, :options] do
quote do
def unquote(method)(fun, {url, query}, opts) when is_function(fun) do
request(fun, unquote(method), {url, query}, nil, opts)
end
def unquote(method)(fun, url, opts) when is_function(fun) do
request(fun, unquote(method), {url, nil}, nil, opts)
end
def unquote(method)(fun, {url, query}) when is_function(fun) do
request(fun, unquote(method), {url, query}, nil, [])
end
def unquote(method)(fun, url) when is_function(fun) do
request(fun, unquote(method), {url, nil}, nil, [])
end
def unquote(method)({url, query}, opts) do
request(unquote(method), {url, query}, nil, opts)
end
def unquote(method)(url, opts) do
request(unquote(method), {url, nil}, nil, opts)
end
def unquote(method)({url, query}) do
request(unquote(method), {url, query}, nil, [])
end
def unquote(method)(url) do
request(unquote(method), {url, nil}, nil, [])
end
end
end
method_defs_with_body = for method <- [:post, :put, :patch] do
quote do
def unquote(method)(fun, {url, query}, body, opts) when is_function(fun) do
request(fun, unquote(method), {url, query}, body, opts)
end
def unquote(method)(fun, url, body, opts) when is_function(fun) do
request(fun, unquote(method), {url, nil}, body, opts)
end
def unquote(method)(fun, {url, query}, body) when is_function(fun) do
request(fun, unquote(method), {url, query}, body, [])
end
def unquote(method)(fun, url, body) when is_function(fun) do
request(fun, unquote(method), {url, nil}, body, nil)
end
def unquote(method)({url, query}, body, opts) do
request(unquote(method), {url, query}, body, opts)
end
def unquote(method)(url, body, opts) do
request(unquote(method), {url, nil}, body, opts)
end
def unquote(method)({url, query}, body) do
request(unquote(method), {url, query}, body, [])
end
def unquote(method)(url, body) do
request(unquote(method), {url, nil}, body, nil)
end
end
end
quote do
unquote(method_defs)
unquote(method_defs_with_body)
@adapter nil
defp request(fun, method, {url, query}, body, opts) when is_function(fun) do
env = %Tesla.Env{
method: method,
url: Tesla.Builder.append_query_string(url, query),
body: body,
opts: opts,
_client: fun,
_module: __MODULE__
}
fun.(env, &call_middleware/1)
end
defp request(method, {url, query}, body, opts) do
request(fn (env, run) -> run.(env) end, method, {url, query}, body, opts)
end
defp call_with_adapter(env) do
case call_adapter(env) do
{status, headers, body} ->
%{env | status: status, headers: headers, body: body}
e -> e
end
end
import Tesla.Builder
Module.register_attribute(__MODULE__, :middleware, accumulate: true)
@before_compile Tesla.Builder
end
end
defp generate_call_middleware(env) do
middleware = Module.get_attribute(env.module, :middleware)
reduced = Enum.reduce(middleware, (quote do: call_with_adapter(env)), fn {mid, args}, acc ->
args = Macro.escape(args)
quote do
unquote(mid).call(env, fn(env) -> unquote(acc) end, unquote(args))
end
end)
quote do
def call_middleware(env) do
unquote(reduced)
end
end
end
defp generate_call_adapter(env) do
adapter = Module.get_attribute(env.module, :adapter)
case adapter do
nil ->
quote do
def call_adapter(env) do
Tesla.call_module_adapter(nil, env)
end
end
{:fn, _, _} ->
quote do
def call_adapter(env) do
unquote(adapter).(env)
end
end
mod when is_atom(mod) ->
quote do
def call_adapter(env) do
Tesla.call_module_adapter(unquote(mod), env)
end
end
_ ->
raise "Adapter must be either Function or Module"
end
end
defmacro __before_compile__(env) do
[
generate_call_adapter(env),
generate_call_middleware(env)
]
end
defmacro adapter({:fn, _, _} = ad) do # pattern match for function
escaped = Macro.escape(ad)
quote do
@adapter unquote(escaped)
end
end
defmacro adapter(adapter) do
quote do
@adapter unquote(adapter)
end
end
defmacro plug(middleware, opts \\ []) do
quote do
@middleware {unquote(middleware), unquote(opts)}
end
end
defmacro defwrap(head, do: body) do
fun_var = Macro.var(:client_fun, __MODULE__)
head_with_client = case head do
{:when, ctx1, [{name, ctx2, args} | tl]} -> {:when, ctx1, [{name, ctx2, [fun_var | args]} | tl]}
{name, ctx, args} -> {:when, ctx, [{name, ctx, [fun_var | args]}, {:is_function, ctx, [fun_var]}]}
end
body_with_client = Macro.prewalk body, fn (ast) ->
case ast do
{fun, ctx, args} when fun in @http_methods -> {fun, ctx, [fun_var | args]}
other -> other
end
end
def_with_client = quote do
def unquote(head_with_client) do
unquote(body_with_client)
end
end
normal = quote do
def unquote(head) do
unquote(body)
end
end
[def_with_client, normal]
end
# Utility functions
def append_query_string(url, query) do
if query do
query_string = URI.encode_query(query)
if url |> String.contains?("?") do
url <> "&" <> query_string
else
url <> "?" <> query_string
end
else
url
end
end
end
defmodule Tesla do
use Tesla.Builder
@adapters [
ibrowse: Tesla.Adapter.Ibrowse,
httpc: Tesla.Adapter.Httpc,
hackney: Tesla.Adapter.Hackney
]
def build_client(stack) do
fn (env, run) ->
f = Enum.reduce(stack, run, fn ({mid, args}, acc) ->
fn (env) -> mid.call(env, fn(e) -> acc.(e) end, args) end
end)
f.(env)
end
end
def call_module_adapter(nil, env) do
call_module_adapter(default_adapter, env)
end
def call_module_adapter(mod, env) do
if Keyword.has_key?(@adapters, mod) do
@adapters[mod].call(env)
else
apply(mod, :call, [env])
end
end
def default_adapter do
Application.get_env(:tesla, :adapter) || :httpc
end
end