Skip to content

Commit cfc968b

Browse files
authored
Add :name option to attribute, embeds_one and embeds_many (#149)
1 parent 0db61da commit cfc968b

File tree

3 files changed

+130
-112
lines changed

3 files changed

+130
-112
lines changed

lib/mongo/collection.ex

Lines changed: 100 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -545,133 +545,94 @@ defmodule Mongo.Collection do
545545
|> Enum.map(fn {name, opts} -> {name, opts[:default]} end)
546546
|> Enum.filter(fn {_name, fun} -> is_function(fun) end)
547547

548-
case @timestamps != [] do
549-
true ->
550-
def new() do
551-
new_timestamps(%__MODULE__{unquote_splicing(Collection.struct_args(args))})
552-
end
553-
554-
false ->
555-
def new() do
556-
%__MODULE__{unquote_splicing(Collection.struct_args(args))}
557-
end
548+
def new do
549+
if @timestamps != [],
550+
do: new_timestamps(%__MODULE__{unquote_splicing(Collection.struct_args(args))}),
551+
else: %__MODULE__{unquote_splicing(Collection.struct_args(args))}
558552
end
559553
end
560554

561555
load_function =
562556
quote unquote: false do
563-
attribute_names = @attributes |> Enum.map(&elem(&1, 0))
557+
attribute_names = Enum.map(@attributes, fn {name, opts} -> {name, opts[:name]} end)
564558

565559
embed_ones =
566560
@embed_ones
567561
|> Enum.filter(fn {_name, mod, _opts} -> Collection.has_load_function?(mod) end)
568-
|> Enum.map(fn {name, mod, _opts} -> {name, mod} end)
562+
|> Enum.map(fn {name, mod, opts} -> {name, {opts[:name], mod}} end)
569563

570564
embed_manys =
571565
@embed_manys
572566
|> Enum.filter(fn {_name, mod, _opts} -> Collection.has_load_function?(mod) end)
573-
|> Enum.map(fn {name, mod, _opts} -> {name, mod} end)
567+
|> Enum.map(fn {name, mod, opts} -> {name, {opts[:name], mod}} end)
574568

575-
def load(map, use_atoms \\ false)
569+
def load(_map, _use_atoms \\ false)
570+
def load(nil, _use_atoms), do: nil
571+
def load(xs, use_atoms) when is_list(xs), do: Enum.map(xs, fn map -> load(map, use_atoms) end)
576572

577-
def load(nil, _use_atoms) do
578-
nil
579-
end
580-
581-
def load(xs, use_atoms) when is_list(xs) do
582-
Enum.map(xs, fn map -> load(map, use_atoms) end)
583-
end
584-
585-
def load(map, false) when is_map(map) do
586-
struct =
587-
Enum.reduce(
588-
unquote(attribute_names),
589-
%__MODULE__{},
590-
fn name, result ->
591-
Map.put(result, name, map[Atom.to_string(name)])
592-
end
593-
)
573+
def load(map, use_atoms) when is_map(map) do
574+
struct = Enum.reduce(unquote(attribute_names), %__MODULE__{}, fn {name, src_name}, result -> Map.put(result, name, map[src_name(src_name, use_atoms)]) end)
594575

595576
struct =
596577
unquote(embed_ones)
597-
|> Enum.map(fn {name, mod} -> {name, mod.load(map[Atom.to_string(name)])} end)
578+
|> Enum.map(fn {name, {src_name, mod}} -> {name, mod.load(map[src_name(src_name, use_atoms)], use_atoms)} end)
598579
|> Enum.reduce(struct, fn {name, doc}, acc -> Map.put(acc, name, doc) end)
599580

600581
unquote(embed_manys)
601-
|> Enum.map(fn {name, mod} -> {name, mod.load(map[Atom.to_string(name)])} end)
582+
|> Enum.map(fn {name, {src_name, mod}} -> {name, mod.load(map[src_name(src_name, use_atoms)], use_atoms)} end)
602583
|> Enum.reduce(struct, fn {name, doc}, acc -> Map.put(acc, name, doc) end)
603584
|> @after_load_fun.()
604585
end
605586

606-
def load(map, true) when is_map(map) do
607-
struct =
608-
Enum.reduce(
609-
unquote(attribute_names),
610-
%__MODULE__{},
611-
fn name, result ->
612-
Map.put(result, name, map[name])
613-
end
614-
)
615-
616-
struct =
617-
unquote(embed_ones)
618-
|> Enum.map(fn {name, mod} -> {name, mod.load(map[name], true)} end)
619-
|> Enum.reduce(struct, fn {name, doc}, acc -> Map.put(acc, name, doc) end)
620-
621-
unquote(embed_manys)
622-
|> Enum.map(fn {name, mod} -> {name, mod.load(map[name], true)} end)
623-
|> Enum.reduce(struct, fn {name, doc}, acc -> Map.put(acc, name, doc) end)
624-
|> @after_load_fun.()
625-
end
587+
defp src_name({src_name, _}, true), do: src_name
588+
defp src_name({_, src_name}, _), do: src_name
626589
end
627590

628591
dump_function =
629592
quote unquote: false do
593+
attribute_names = Enum.map(@attributes, fn {name, opts} -> {name, opts[:name]} end)
594+
630595
embed_ones =
631596
@embed_ones
632597
|> Enum.filter(fn {_name, mod, _opts} -> Collection.has_dump_function?(mod) end)
633-
|> Enum.map(fn {name, mod, _opts} -> {name, mod} end)
598+
|> Enum.map(fn {name, mod, opts} -> {name, {opts[:name], mod}} end)
634599

635600
embed_manys =
636601
@embed_manys
637602
|> Enum.filter(fn {_name, mod, _opts} -> Collection.has_dump_function?(mod) end)
638-
|> Enum.map(fn {name, mod, _opts} -> {name, mod} end)
603+
|> Enum.map(fn {name, mod, opts} -> {name, {opts[:name], mod}} end)
639604

640-
def dump(nil) do
641-
nil
642-
end
605+
def dump(nil), do: nil
606+
def dump(xs) when is_list(xs), do: Enum.map(xs, fn struct -> dump(struct) end)
643607

644-
def dump(xs) when is_list(xs) do
645-
Enum.map(xs, fn struct -> dump(struct) end)
646-
end
647-
648-
def dump(%__MODULE__{} = struct) do
649-
struct =
608+
def dump(%{} = map) do
609+
map =
650610
unquote(embed_ones)
651-
|> Enum.map(fn {name, mod} -> {name, mod.dump(Map.get(struct, name))} end)
652-
|> Enum.reduce(struct, fn {name, doc}, acc -> Map.put(acc, name, doc) end)
611+
|> Enum.map(fn {name, {_src_name, mod}} -> {name, mod.dump(Map.get(map, name))} end)
612+
|> Enum.reduce(map, fn {name, doc}, acc -> Map.put(acc, name, doc) end)
653613

654-
struct =
614+
map =
655615
unquote(embed_manys)
656-
|> Enum.map(fn {name, mod} -> {name, mod.dump(Map.get(struct, name))} end)
657-
|> Enum.reduce(struct, fn {name, doc}, acc -> Map.put(acc, name, doc) end)
616+
|> Enum.map(fn {name, {_src_name, mod}} -> {name, mod.dump(Map.get(map, name))} end)
617+
|> Enum.reduce(map, fn {name, doc}, acc -> Map.put(acc, name, doc) end)
658618

659-
struct
619+
map
660620
|> Map.drop(unquote(@derived))
661621
|> @before_dump_fun.()
662622
|> Collection.dump()
623+
|> Enum.into(%{}, fn {name, value} ->
624+
case unquote(attribute_names)[name] || unquote(embed_ones)[name] || unquote(embed_manys)[name] do
625+
{{src_name, _}, _mod} -> {src_name, value}
626+
{src_name, _} -> {src_name, value}
627+
end
628+
end)
663629
end
664630
end
665631

666632
timestamps_function =
667633
quote unquote: false do
668-
def timestamps(nil) do
669-
nil
670-
end
671-
672-
def timestamps(xs) when is_list(xs) do
673-
Enum.map(xs, fn struct -> timestamps(struct) end)
674-
end
634+
def timestamps(nil), do: nil
635+
def timestamps(xs) when is_list(xs), do: Enum.map(xs, fn struct -> timestamps(struct) end)
675636

676637
def timestamps(struct) do
677638
updated_at = @timestamps[:updated_at]
@@ -700,6 +661,41 @@ defmodule Mongo.Collection do
700661
end
701662
end
702663

664+
@doc """
665+
Inserts name option for the attribute, embeds_one and embeds_many.
666+
"""
667+
def add_name(mod, opts, name) do
668+
opts =
669+
case opts[:name] do
670+
nil -> Keyword.put(opts, :name, {name, to_string(name)})
671+
name when is_atom(name) -> Keyword.replace(opts, :name, {name, to_string(name)})
672+
name when is_binary(name) -> Keyword.replace(opts, :name, {String.to_atom(name), name})
673+
_ -> raise ArgumentError, "name must be an atom or a binary"
674+
end
675+
676+
{src_name, _} = opts[:name]
677+
678+
if name_exists?(mod, src_name),
679+
do: raise(ArgumentError, "attribute #{inspect(name)} has duplicate name option key\n\n [name: #{inspect(src_name)}] already exist\n"),
680+
else: opts
681+
end
682+
683+
defp name_exists?(mod, name) do
684+
name_exists?(mod, :attributes, name) || name_exists?(mod, :embed_ones, name) || name_exists?(mod, :embed_manys, name)
685+
end
686+
687+
defp name_exists?(mod, :attributes, name) do
688+
mod
689+
|> Module.get_attribute(:attributes)
690+
|> Enum.any?(fn {_name, opts} -> elem(opts[:name], 0) == name end)
691+
end
692+
693+
defp name_exists?(mod, embeds, name) do
694+
mod
695+
|> Module.get_attribute(embeds)
696+
|> Enum.any?(fn {_name, _mod, opts} -> elem(opts[:name], 0) == name end)
697+
end
698+
703699
@doc """
704700
Inserts the specified `@id_generator` to the list of attributes. Calls `add_id/3`.
705701
"""
@@ -720,7 +716,7 @@ defmodule Mongo.Collection do
720716

721717
def add_id(mod, {id, type, fun}, _name) do
722718
Module.put_attribute(mod, :types, {id, type})
723-
Module.put_attribute(mod, :attributes, {id, default: fun})
719+
Module.put_attribute(mod, :attributes, {id, default: fun, name: {:_id, "_id"}})
724720
end
725721

726722
@doc """
@@ -791,7 +787,7 @@ defmodule Mongo.Collection do
791787
Adds the struct to the `embeds_one` list.
792788
"""
793789
def __embeds_one__(mod, name, target, opts) do
794-
Module.put_attribute(mod, :embed_ones, {name, target, opts})
790+
Module.put_attribute(mod, :embed_ones, {name, target, add_name(mod, opts, name)})
795791
end
796792

797793
@doc """
@@ -809,7 +805,7 @@ defmodule Mongo.Collection do
809805
"""
810806
def __embeds_many__(mod, name, target, type, opts) do
811807
Module.put_attribute(mod, :types, {name, type})
812-
Module.put_attribute(mod, :embed_manys, {name, target, opts})
808+
Module.put_attribute(mod, :embed_manys, {name, target, add_name(mod, opts, name)})
813809
end
814810

815811
@doc """
@@ -825,34 +821,44 @@ defmodule Mongo.Collection do
825821
Adds the attribute to the attributes list.
826822
"""
827823
def __attribute__(mod, name, type, opts) do
828-
case opts[:derived] do
829-
true -> Module.put_attribute(mod, :derived, name)
830-
_ -> []
831-
end
824+
if opts[:derived],
825+
do: Module.put_attribute(mod, :derived, name),
826+
else: []
832827

833828
Module.put_attribute(mod, :types, {name, type})
834-
Module.put_attribute(mod, :attributes, {name, opts})
829+
Module.put_attribute(mod, :attributes, {name, add_name(mod, opts, name)})
830+
end
831+
832+
@doc """
833+
Inserts src_name for the timestamp attribute
834+
"""
835+
def timestamp(opts, name) when is_atom(name) do
836+
case Keyword.get(opts, name, {name, name}) do
837+
{name, src_name} -> {name, src_name}
838+
name -> {name, name}
839+
end
835840
end
836841

837842
@doc """
838843
Defines the `timestamps/1` function.
839844
"""
840845
defmacro timestamps(opts \\ []) do
841846
quote bind_quoted: [opts: opts] do
842-
inserted_at = Keyword.get(opts, :inserted_at, :inserted_at)
843-
updated_at = Keyword.get(opts, :updated_at, :updated_at)
847+
{inserted_at, inserted_at_name} = timestamp(opts, :inserted_at)
848+
{updated_at, updated_at_name} = timestamp(opts, :updated_at)
849+
844850
type = Keyword.get(opts, :type, DateTime)
845851

846-
new_opts =
852+
ops =
847853
opts
848854
|> Keyword.drop([:inserted_at, :updated_at, :type])
849855
|> Keyword.put_new(:default, &DateTime.utc_now/0)
850856

851857
Module.put_attribute(__MODULE__, :timestamps, {:inserted_at, inserted_at})
852858
Module.put_attribute(__MODULE__, :timestamps, {:updated_at, updated_at})
853859

854-
Collection.__attribute__(__MODULE__, inserted_at, Macro.escape(type), new_opts)
855-
Collection.__attribute__(__MODULE__, updated_at, Macro.escape(type), new_opts)
860+
Collection.__attribute__(__MODULE__, inserted_at, Macro.escape(type), Keyword.put(ops, :name, inserted_at_name))
861+
Collection.__attribute__(__MODULE__, updated_at, Macro.escape(type), Keyword.put(ops, :name, updated_at_name))
856862
end
857863
end
858864

@@ -863,8 +869,7 @@ defmodule Mongo.Collection do
863869
end
864870

865871
def dump(%{__struct__: _} = struct) do
866-
map = Map.from_struct(struct)
867-
:maps.map(&dump/2, map) |> filter_nils()
872+
:maps.map(&dump/2, Map.from_struct(struct)) |> filter_nils()
868873
end
869874

870875
def dump(map), do: :maps.map(&dump/2, map)
@@ -882,16 +887,16 @@ defmodule Mongo.Collection do
882887
defp ensure_nested_map(%{__struct__: BSON.LongNumber} = data), do: data
883888

884889
defp ensure_nested_map(%{__struct__: _} = struct) do
885-
map = Map.from_struct(struct)
886-
:maps.map(&dump/2, map) |> filter_nils()
890+
:maps.map(&dump/2, Map.from_struct(struct)) |> filter_nils()
887891
end
888892

889893
defp ensure_nested_map(list) when is_list(list), do: Enum.map(list, &ensure_nested_map/1)
890894

891895
defp ensure_nested_map(data), do: data
892896

893897
def filter_nils(map) when is_map(map) do
894-
Enum.reject(map, fn {_key, value} -> is_nil(value) end)
898+
map
899+
|> Enum.reject(fn {_key, value} -> is_nil(value) end)
895900
|> Enum.into(%{})
896901
end
897902

0 commit comments

Comments
 (0)