Current section

Files

Jump to
manganese_entity_kit lib structs entities_collection.ex
Raw

lib/structs/entities_collection.ex

defmodule Manganese.EntityKit.Structs.EntitiesCollection do
alias Manganese.EntityKit.Structs
@type t :: %Structs.EntitiesCollection{
entities: %{ optional(atom) => %{ optional(integer) => term }}
}
defstruct [
entities: %{}
]
# Utilities
@doc """
Create an empty entities collection.
"""
@spec new :: t
def new, do: %Structs.EntitiesCollection{}
@doc """
Retrieve an entity from the collection.
If no entity of the given type and ID pair exist, `nil` is returned instead.
"""
@spec get_entity(t, atom, pos_integer) :: term | nil
def get_entity(entities_collection, entity_type, id) do
case Map.get entities_collection.entities, entity_type do
%{ ^id => entity } -> entity
_ -> nil
end
end
@doc """
Add an entity to the collection.
If the entity of the given type and ID pair already exist, it will be overwritten with the new value.
"""
@spec put_entity(t, atom, term) :: t
def put_entity(entities_collection, entity_type, entity) do
%Structs.EntitiesCollection{
entities: entities_collection.entities
|> Map.put(
entity_type,
(Map.get(entities_collection.entities, entity_type) || %{})
|> Map.put(entity.id, entity)
)
}
end
@doc """
Add multiple entities of the same type to the collection.
If an entity of the given type and ID pair already exist, it will be overwritten with the new value.
"""
@spec put_entities(t, atom, [ term ]) :: t
def put_entities(entities_collection, entity_type, entities) do
entities
|> Enum.reduce(entities_collection, fn entity, entities_collection ->
entities_collection
|> put_entity(entity_type, entity)
end)
end
@doc """
Combine two collections into one.
If an entity of the same type and ID pair is present in both collections, the value in `entities_collection2` will take precedence.
"""
@spec merge(t, t) :: t
def merge(entities_collection1, entities_collection2) do
entities1 = (entities_collection1 || Structs.EntitiesCollection.new).entities
entities2 = (entities_collection2 || Structs.EntitiesCollection.new).entities
%Structs.EntitiesCollection{
entities: Map.merge(entities1, entities2, fn _, entities_of_type1, entities_of_type2 ->
Map.merge(entities_of_type1 || %{}, entities_of_type2 || %{}, fn _, _entity1, entity2 ->
entity2
end)
end)
}
end
@doc """
"""
@spec map(t, (term -> term)) :: t
def map(entities_collection, mapper) do
%Structs.EntitiesCollection{
entities:
entities_collection.entities
|> Map.new(fn { entity_type, entities_of_type } ->
{
entity_type,
entities_of_type
|> Enum.map(fn { id, entity } ->
{ id, mapper.(entity_type, entity) }
end)
|> Enum.into(%{})
}
end)
|> Enum.into(%{})
}
end
end
defimpl Manganese.EntityKit.Protocols.FilterableCollection, for: Manganese.EntityKit.Structs.EntitiesCollection do
alias Manganese.EntityKit.Structs
def filter(entities_collection, filter) do
%Structs.EntitiesCollection{
entities:
entities_collection.entities
|> Map.new(fn { entity_type, entities_of_type } ->
{
entity_type,
entities_of_type
|> Enum.filter(fn { id, _entity } ->
filter.(entity_type, id)
end)
|> Enum.into(%{})
}
end)
|> Enum.filter(fn { _, entities_of_type } ->
!(is_nil(entities_of_type) || length(Map.keys entities_of_type) == 0)
end)
|> Enum.into(%{})
}
end
end
defimpl Manganese.EntityKit.Protocols.SerializableCollection, for: Manganese.EntityKit.Structs.EntitiesCollection do
alias Manganese.EntityKit.Behaviors
def serialize(entities_collection, serializer) do
entities_collection.entities
|> Map.new(fn { entity_type, entities_of_type } ->
{
Atom.to_string(entity_type),
entities_of_type
|> Map.new(fn { id, entity } ->
serialized_entity =
entity
|> Behaviors.Entity.serialize(entity_type, serializer)
{ id, serialized_entity }
end)
}
end)
|> Enum.filter(fn { _, entities_of_type } ->
!is_nil(entities_of_type)
end)
|> Enum.into(%{})
end
end
defimpl Manganese.EntityKit.Protocols.ReloadableCollection, for: Manganese.EntityKit.Structs.EntitiesCollection do
alias Manganese.EntityKit.Structs
alias Manganese.EntityKit.Behaviors
def reload(entities_collection, repo) do
reloaded_entities_collection =
entities_collection
|> Structs.EntitiesCollection.map(fn entity_type, entity ->
{ :ok, reloaded_entity } =
entity
|> Behaviors.Entity.reload(entity_type, repo)
reloaded_entity
end)
{ :ok, reloaded_entities_collection }
end
def reload!(entities_collection, repo) do
{ :ok, reloaded_entities_collection } = reload entities_collection, repo
reloaded_entities_collection
end
end