Packages
mazurka
0.3.12
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/model.ex
defmodule Mazurka.Model do
@moduledoc """
"""
defmacro __using__(_) do
quote do
use Ecto.Model
@before_compile unquote(__MODULE__)
end
end
defmacro __before_compile__(_ENV) do
module = __CALLER__.module
struct_fields = Module.get_attribute(module, :struct_fields)
fields = format_struct_fields(struct_fields)
field_vars = format_struct_vars(struct_fields)
valid_fields = format_valid_fields(struct_fields)
assoc_fields = format_assoc_fields(struct_fields)
## TODO pull in the @primary_key
field_clauses = for field <- valid_fields do
keys = [field, to_string(field)]
quote do
def fetch(model = %{id: id, __meta__: %{state: :built, repo: repo, opts: opts} = meta}, key, ref) when key in unquote(keys) do
pid = Etude.Async.spawn(ref, fn ->
unquote(field_vars) = repo.get!(unquote(module), id, opts)
model = Map.put(unquote(field_vars), :__meta__, %{meta | state: :loaded})
{:ok, model}
end)
{:pending, pid, %{model | __meta__: %{meta | state: :loading}}}
end
def fetch(model = %{__meta__: %{state: :loading}}, key, _) when key in unquote(keys) do
{:pending, model}
end
def fetch(model = %{__meta__: %{state: :loaded}}, key, ref) when key in unquote(keys) do
{:ok, Map.get(model, unquote(field)), model}
end
end
end
quote do
def get(var!(repo), var!(id), var!(opts)) do
{:ok, unquote({:%{}, [], [{:__struct__, module} | fields]})}
end
defimpl Etude.Dict, for: unquote(module) do
use Etude.Dict
def cache_key(%{id: id, __meta__: %{repo: repo, opts: opts}}) do
{repo, unquote(module), to_string(id), :erlang.phash2(opts)}
end
def cache_key(%{id: id}) do
{unquote(module), to_string(id)}
end
def fetch(model = %{id: id}, :id, _) do
{:ok, id, model}
end
unquote_splicing(field_clauses)
unquote_splicing(assoc_fields)
def fetch(model, _, _) do
{:error, model}
end
end
end
end
defp format_struct_fields(fields) do
Enum.map(fields, fn
{:id, _} ->
{:id, Macro.var(:id, nil)}
{key, %Ecto.Association.NotLoaded{} = assoc} ->
{key, Macro.escape(assoc)}
{:__meta__, meta} ->
meta = Map.merge(meta, %{
__struct__: Mazurka.Model.Metadata,
repo: Macro.var(:repo, nil),
opts: Macro.var(:opts, nil)
}) |> Map.to_list
{:__meta__, {:%{}, [], meta}}
{key, _} ->
{key, nil}
end)
end
defp format_valid_fields(fields) do
Enum.reduce(fields, [], fn
({_, %Ecto.Association.NotLoaded{}}, acc) ->
acc
({:__meta__, _}, acc) ->
acc
({key, _}, acc) when is_binary(key) ->
[String.to_atom(key) | acc]
({key, _}, acc) ->
[key | acc]
end)
end
defp format_assoc_fields(fields) do
Enum.reduce(fields, [], fn
({key, %Ecto.Association.NotLoaded{} = assoc}, acc) ->
[format_assoc_field(key, assoc) | acc]
(_, acc) ->
acc
end)
end
defp format_assoc_field(key, _assoc) do
## TODO
keys = [key, to_string(key)]
quote do
def fetch(model = %{unquote(key) => %Ecto.Association.NotLoaded{}}, key, ref) when key in unquote(keys) do
# TODO: add error handling
{:ok, model.__meta__.repo.all(assoc(model, unquote(key))), model}
end
def fetch(model = %{unquote(key) => %{__struct__: Mazurka.Model.Association.Loading}}, key, _) when key in unquote(keys) do
{:pending, model}
end
def fetch(model = %{unquote(key) => value}, key, _) when key in unquote(keys) do
{:ok, value, model}
end
end
end
defp format_struct_vars(fields) do
{:%{}, [], Enum.reduce(fields, [], fn
({_, %Ecto.Association.NotLoaded{}}, acc) ->
acc
({:__meta__, _}, acc) ->
acc
({key, _}, acc) ->
[{key, Macro.var(key, nil)} | acc]
end)}
end
end