Current section
Files
Jump to
Current section
Files
lib/taglet.ex
defmodule Taglet do
import Ecto.Query
alias Taglet.{Tagging, Tag}
@moduledoc """
Documentation for Taglet.
"""
@repo Taglet.RepoClient.repo
@type tags :: String.t | list
@type tag :: String.t
@type context :: String.t
@type tag_list :: list
@type persisted_struct :: struct
@doc """
Get a persisted struct and inserts a new tag associated to this
struct for a specific context.
You can pass a tag or a list of tags.
In case the tag is duplicated nothing will happen.
It returns a list of associated tags
"""
@spec add(struct, tags, context) :: tag_list
def add(struct, tags, context \\ "tag")
def add(struct, tags, context) when is_list(tags) do
tag_list = tag_list(struct, context)
new_tags = tags -- tag_list
case new_tags do
[] ->
tag_list
new_tags ->
taggings = Enum.map(new_tags, fn(tag) ->
generate_tagging(struct, tag, context)
end)
@repo.insert_all(Tagging, taggings)
tag_list ++ new_tags
end
end
def add(struct, tag, context) do
tag_list = tag_list(struct, context)
case tag in tag_list do
true -> tag_list
false ->
%Tagging{}
|> Tagging.changeset(generate_tagging(struct, tag, context))
|> @repo.insert!
List.insert_at(tag_list, -1, tag)
end
end
defp get_or_create(tag) do
case @repo.get_by(Tag, name: tag) do
nil -> @repo.insert!(%Tag{name: tag})
tag_resource -> tag_resource
end
end
defp generate_tagging(struct, tag, context) do
tag_resource = get_or_create(tag)
%{
taggable_id: struct.id,
taggable_type: struct.__struct__ |> Module.split |> List.last,
context: context,
tag_id: tag_resource.id,
inserted_at: Ecto.DateTime.utc
}
end
@doc """
Get a persisted struct and removes the tag association for
a specific context.
In case the association doesn't exist nothing will happen.
It returns a list of associated tags
"""
@spec remove(struct, tag, context) :: tag_list
def remove(struct, tag, context \\ "tag") do
tag_list = tag_list(struct, context)
case tag in tag_list do
true ->
struct
|> get_association(get_or_create(tag), context)
|> @repo.delete!
List.delete(tag_list, tag)
false -> tag_list
end
end
defp get_association(struct, tag_resource, context) do
@repo.get_by(Tagging,
taggable_id: struct.id,
taggable_type: struct.__struct__ |> Module.split |> List.last,
context: context,
tag_id: tag_resource.id
)
end
@doc """
Given a struct, it searchs the associated tags for a specific
context.
It returns a list of associated tags ordered by `insert_date`
"""
@spec tag_list(struct, context) :: tag_list
def tag_list(struct, context \\ "tag") do
id = struct.id
type = struct.__struct__ |> Module.split |> List.last
Tag
|> join(:inner, [t], tg in Tagging, t.id == tg.tag_id)
|> where([t, tg],
tg.context == ^context
and
tg.taggable_id == ^id
and
tg.taggable_type == ^type
)
|> order_by([t, tg], asc: tg.inserted_at)
|> @repo.all
|> Enum.map(fn(result) -> result.name end)
end
@doc """
Given a tag, model and context ('tag' by default), will find
all the model resources associated to the given tag.
"""
@spec tagged_with(tag, module, context) :: list
def tagged_with(tag, model, context \\ "tag") do
type = model |> Module.split |> List.last
model
|> join(:right, [m], tg in Tagging,
tg.taggable_type == ^type
and
tg.context == ^context
)
|> join(:inner, [m, tg], t in Tag,
t.id == tg.tag_id
and
t.name == ^tag
)
|> where([m, tg, t], m.id == tg.taggable_id)
|> @repo.all
end
end