defmodule Philomena.Polymorphic do alias Philomena.Repo import Ecto.Query @classes %{ "Channel" => Philomena.Channels.Channel, "Comment" => Philomena.Comments.Comment, "Commission" => Philomena.Commissions.Commission, "Conversation" => Philomena.Conversations.Conversation, "DnpEntry" => Philomena.DnpEntries.DnpEntry, "Filter" => Philomena.Filters.Filter, "Forum" => Philomena.Forums.Forum, "Gallery" => Philomena.Galleries.Gallery, "Image" => Philomena.Images.Image, "LivestreamChannel" => Philomena.Channels.Channel, "Post" => Philomena.Posts.Post, "Report" => Philomena.Reports.Report, "Topic" => Philomena.Topics.Topic, "User" => Philomena.Users.User } @preloads %{ "Comment" => [:user, image: [:sources, tags: :aliases]], "Commission" => [:user], "Conversation" => [:from, :to], "DnpEntry" => [:requesting_user, :tag], "Gallery" => [:creator], "Image" => [:user, :sources, tags: :aliases], "Post" => [:user, topic: :forum], "Topic" => [:forum, :user], "Report" => [:user] } # Deal with Rails polymorphism BS def load_polymorphic(structs, associations) when is_list(associations) do Enum.reduce(associations, structs, fn asc, acc -> load_polymorphic(acc, asc) end) end def load_polymorphic(structs, {name, [{id, type}]}) do modules_and_ids = structs |> Enum.group_by(&Map.get(&1, type), &Map.get(&1, id)) loaded_rows = modules_and_ids |> Map.new(fn {nil, _ids} -> {nil, []} {type, ids} -> pre = Map.get(@preloads, type, []) rows = @classes[type] |> where([m], m.id in ^ids) |> preload(^pre) |> Repo.all() |> Map.new(fn r -> {r.id, r} end) {type, rows} end) structs |> Enum.map(fn struct -> type = Map.get(struct, type) id = Map.get(struct, id) row = loaded_rows[type][id] %{struct | name => row} end) end end