docs/loader.md

# Configure a loader

A loader returns the data for a remote schema. The remote schemas are defined
in a schema like this.

```elixir
...
  properties: %{
    int: {:ref, "http://localhost:1234/int.exon"}
  }
...
```

A loader will be configured like this.

```elixir
config :xema, loader: My.Loader
```

A loader is a module which use the behaviour `Xema.Loader`.

```elixir
defmodule My.Loader do
  @moduledoc false

  @behaviour Xema.Loader

  @spec fetch(binary) :: {:ok, map} | {:error, any}
  def fetch(uri) do
    with {:ok, response} <- get(uri), do: eval(response, uri)
  end

  defp get(uri) do
    case HTTPoison.get(uri) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        {:ok, body}

      {:ok, %HTTPoison.Response{status_code: 404}} ->
        {:error, "Remote schema '#{uri}' not found."}

      {:ok, %HTTPoison.Response{status_code: code}} ->
        {:error, "code: #{code}"}

      {:error, reason} ->
        {:error, reason}
    end
  end

  defp eval(str, uri) do
    {data, _} = Code.eval_string(str)
    {:ok, data}
  rescue
    error -> {:error, %{error | file: URI.to_string(uri)}}
  end
end
```

The function `fetch/1` will be called by `Xema` and expects an `%URI{}`. The
return value must be a tuple of `:ok` and the required data for a schema or an
error tuple.

**Note!** This loader use `Code.eval_string/1` and eval is always evil.

> **Warning:** string can be any Elixir code and will be executed with the same
> privileges as the Erlang VM: this means that such code could compromise the
> machine (for example by executing system commands). Don’t use `eval_string/3`
> with untrusted input (such as strings coming from the network).
> -- Elixir API

## File loader

A loader to read schema from the local file system.

In the schema:
```elixir
...
  properties: %{
    int: {:ref, "int.exon"}
  }
...
```

The loader:
```elixir
defmodule My.Loader do
  @moduledoc false

  @behaviour Xema.Loader

  @spec fetch(binary) :: {:ok, map} | {:error, any}
  def fetch(uri) do
    "path/to/schemas"
    |> Path.join(uri.path)
    |> File.read!()
    |> eval(uri)
  end

  defp eval(str, uri) do
    {data, _} = Code.eval_string(str)
    {:ok, data}
  rescue
    error -> {:error, %{error | file: URI.to_string(uri)}}
  end
end
```

## JSON Schema loader

If the scheme is created with `Xema.from_json_schema/2`, the loader must return
the decoded JSON.

In this case the file loader looks like this:
```elixir
defmodule My.Loader do
  @moduledoc false

  @behaviour Xema.Loader

  @spec fetch(binary) :: {:ok, map} | {:error, any}
  def fetch(uri) do
    "path/to/schemas"
    |> Path.join(uri.path)
    |> File.read!()
    |> Jason.decode()
  end
end
```