@@ -140,8 +140,10 @@ defmodule Record do
140140 there is more work happening at runtime.
141141
142142 The above calls (new and update) can interchangeably accept both
143- atom and string keys for field names. Please note, however, that
144- atom keys are faster.
143+ atom and string keys for field names, however not both at the same time.
144+ Please also note that atom keys are faster. This feature allows to
145+ "sanitize" untrusted dictionaries and initialize/update records without
146+ using `binary_to_existing_atom/1`.
145147
146148 To sum up, `defrecordp` should be used when you don't want
147149 to expose the record information while `defrecord` should be used
@@ -628,16 +630,15 @@ defmodule Record do
628630 # an ordered dict of options (opts) and it will try to fetch
629631 # the given key from the ordered dict, falling back to the
630632 # default value if one does not exist.
631- selective = lc { k, v } inlist values do
632- string_k = atom_to_binary(k)
633- quote do
633+ atom_selective = lc { k, v } inlist values do
634+ quote do: Keyword.get(opts, unquote(k), unquote(v))
635+ end
636+ string_selective = lc { k, v } inlist values do
637+ k = atom_to_binary(k)
638+ quote do
634639 case :lists.keyfind(unquote(k), 1, opts) do
635- false ->
636- case :lists.keyfind(unquote(string_k), 1, opts) do
637- false -> unquote(v)
638- {_, value} -> value
639- end
640- {_, value} -> value
640+ false -> unquote(v)
641+ {_, v} -> v
641642 end
642643 end
643644 end
@@ -648,7 +649,8 @@ defmodule Record do
648649
649650 @doc false
650651 def new([]), do: { __MODULE__, unquote_splicing(defaults) }
651- def new(opts) when is_list(opts), do: { __MODULE__, unquote_splicing(selective) }
652+ def new([{key, _}|_] = opts) when is_atom(key), do: { __MODULE__, unquote_splicing(atom_selective) }
653+ def new([{key, _}|_] = opts) when is_binary(key), do: { __MODULE__, unquote_splicing(string_selective) }
652654 end
653655 end
654656
@@ -739,32 +741,38 @@ defmodule Record do
739741 # Define an updater method that receives a
740742 # keyword list and updates the record.
741743 defp updater(values) do
742- fields =
744+ atom_fields =
743745 lc {key, _default} inlist values do
744- string_key = atom_to_binary(key)
745746 index = find_index(values, key, 1)
747+ quote do: Keyword.get(keywords, unquote(key), elem(record, unquote(index)))
748+ end
749+
750+ string_fields =
751+ lc {key, _default} inlist values do
752+ index = find_index(values, key, 1)
753+ key = atom_to_binary(key)
746754 quote do
747755 case :lists.keyfind(unquote(key), 1, keywords) do
748- false ->
749- case :lists.keyfind(unquote(string_key), 1, keywords) do
750- false -> elem(record, unquote(index))
751- {_, value} -> value
752- end
756+ false -> elem(record, unquote(index))
753757 {_, value} -> value
754758 end
755759 end
756760 end
757761
758- contents = quote do: { __MODULE__, unquote_splicing(fields) }
762+ atom_contents = quote do: { __MODULE__, unquote_splicing(atom_fields) }
763+ string_contents = quote do: { __MODULE__, unquote_splicing(string_fields) }
759764
760765 quote do
761766 @doc false
762767 def update([], record) do
763768 record
764769 end
765770
766- def update(keywords, record) do
767- unquote(contents)
771+ def update([{key, _}|_] = keywords, record) when is_atom(key) do
772+ unquote(atom_contents)
773+ end
774+ def update([{key, _}|_] = keywords, record) when is_binary(key) do
775+ unquote(string_contents)
768776 end
769777 end
770778 end
0 commit comments