Created
May 17, 2017 19:23
-
-
Save boxxxie/500b27844be4f0eeca34849ecf689b47 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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