Skip to content

Instantly share code, notes, and snippets.

@boxxxie
Created May 17, 2017 19:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boxxxie/500b27844be4f0eeca34849ecf689b47 to your computer and use it in GitHub Desktop.
Save boxxxie/500b27844be4f0eeca34849ecf689b47 to your computer and use it in GitHub Desktop.
defmodule Ecto.Schema.Includes do
defmacro include(module) do
quote do
Ecto.Schema.__include__(unquote(module), __MODULE__)
end
end
# Copy the field and association definitions from a module
def __include__(from_module, to_module) do
__include__(:fields, from_module, to_module)
__include__(:associations, from_module, to_module)
end
def __include__(:fields, from_module, to_module) do
Enum.each(from_module.__schema__(:fields), fn(field) ->
type = from_module.__schema__(:type, field)
Ecto.Schema.__field__(to_module, field, type, field_options_for(from_module, field))
if auto = List.keyfind(from_module.__schema__(:autogenerate, :insert), field, 0) do
{_,_,autogen} = auto
Module.put_attribute(to_module, :ecto_autogenerate_insert, {field, type, autogen})
end
if auto = List.keyfind(from_module.__schema__(:autogenerate, :update), field, 0) do
{_,_,autogen} = auto
Module.put_attribute(to_module, :ecto_autogenerate_update, {field, type, autogen})
end
end)
end
def __include__(:associations, from_module, to_module) do
Enum.each(from_module.__schema__(:associations), fn(association) ->
case assoc = from_module.__schema__(:association, association) do
%Ecto.Association.BelongsTo{} ->
opts = assoc_opts_from(assoc)
if Keyword.has_key?(Module.get_attribute(to_module, :ecto_fields), opts[:foreign_key]),
do: opts = opts ++ [define_field: false]
Ecto.Schema.__belongs_to__(to_module, assoc.field, assoc.queryable, opts)
%Ecto.Association.HasThrough{cardinality: :many} ->
Ecto.Schema.__has_many__(to_module, assoc.field, assoc_opts_from(assoc), [])
%Ecto.Association.HasThrough{cardinality: :one} ->
Ecto.Schema.__has_one__(to_module, assoc.field, assoc_opts_from(assoc), [])
%Ecto.Association.Has{cardinality: :many} ->
Ecto.Schema.__has_many__(to_module, assoc.field, assoc.queryable, assoc_opts_from(assoc))
%Ecto.Association.Has{cardinality: :one} ->
Ecto.Schema.__has_one__(to_module, assoc.field, assoc.queryable, assoc_opts_from(assoc))
%Ecto.Association.ManyToMany{} ->
Ecto.Schema.__many_to_many__(to_module, assoc.field, assoc.queryable, assoc_opts_from(assoc))
_ ->
raise ArgumentError, "Including unknown association type: #{inspect assoc}"
end
end)
end
# Mapping for all :belongs_to association
@opts_map %{owner_key: :foreign_key, related_key: :references}
defp assoc_opts_from(%Ecto.Association.BelongsTo{} = assoc) do
assoc_opts_from(assoc, @opts_map)
end
# Mapping for all other association types
@opts_map %{owner_key: :references, related_key: :foreign_key}
defp assoc_opts_from(assoc) do
assoc_opts_from(assoc, @opts_map)
end
@opts_reject [:cardinality, :field, :queryable, :relationship, :__struct__, :owner, :related]
def assoc_opts_from(assoc, map) do
Map.keys(assoc)
|> Enum.reject(fn(key) -> key in @opts_reject end)
|> Enum.map(fn(key) -> {Map.get(map, key, key), Map.get(assoc, key)} end)
|> Enum.reject(fn {_k, v} -> is_nil(v) end)
end
defp field_options_for(module, field) do
options = []
{:ok, default} = Map.fetch(module.__struct__, field)
if default, do: options = options ++ [default: default]
if includes?(module.__schema__(:primary_key), field), do: options = options ++ [primary_key: true]
if includes?(module.__schema__(:autogenerate_id), field), do: options = options ++ [autogenerate: true]
if includes?(module.__schema__(:read_after_writes), field), do: options = options ++ [read_after_writes: true]
if alias_for = module.__schema__(:aliases)[field] do
options = options ++ [alias_for: alias_for]
end
options
end
defp includes?(list, item) when is_tuple(list) do
{key, _} = list
key == item
end
defp includes?(list, item) do
item in list
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment