@@ -139,6 +139,12 @@ defmodule Record do
139
139
records flexibility at the cost of performance since
140
140
there is more work happening at runtime.
141
141
142
+ The above calls (new and update) can interchangeably accept both
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`.
147
+
142
148
To sum up, `defrecordp` should be used when you don't want
143
149
to expose the record information while `defrecord` should be used
144
150
whenever you want to share a record within your code or with other
@@ -624,17 +630,27 @@ defmodule Record do
624
630
# an ordered dict of options (opts) and it will try to fetch
625
631
# the given key from the ordered dict, falling back to the
626
632
# default value if one does not exist.
627
- selective = lc { k, v } inlist values do
633
+ atom_selective = lc { k, v } inlist values do
628
634
quote do: Keyword.get(opts, unquote(k), unquote(v))
629
635
end
636
+ string_selective = lc { k, v } inlist values do
637
+ k = atom_to_binary(k)
638
+ quote do
639
+ case :lists.keyfind(unquote(k), 1, opts) do
640
+ false -> unquote(v)
641
+ {_, v} -> v
642
+ end
643
+ end
644
+ end
630
645
631
646
quote do
632
647
@doc false
633
648
def new(), do: new([])
634
649
635
650
@doc false
636
651
def new([]), do: { __MODULE__, unquote_splicing(defaults) }
637
- 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) }
638
654
end
639
655
end
640
656
@@ -725,24 +741,38 @@ defmodule Record do
725
741
# Define an updater method that receives a
726
742
# keyword list and updates the record.
727
743
defp updater(values) do
728
- fields =
744
+ atom_fields =
745
+ lc {key, _default} inlist values do
746
+ index = find_index(values, key, 1)
747
+ quote do: Keyword.get(keywords, unquote(key), elem(record, unquote(index)))
748
+ end
749
+
750
+ string_fields =
729
751
lc {key, _default} inlist values do
730
752
index = find_index(values, key, 1)
753
+ key = atom_to_binary(key)
731
754
quote do
732
- Keyword.get(keywords, unquote(key), elem(record, unquote(index)))
755
+ case :lists.keyfind(unquote(key), 1, keywords) do
756
+ false -> elem(record, unquote(index))
757
+ {_, value} -> value
758
+ end
733
759
end
734
760
end
735
761
736
- 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) }
737
764
738
765
quote do
739
766
@doc false
740
767
def update([], record) do
741
768
record
742
769
end
743
770
744
- def update(keywords, record) do
745
- 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)
746
776
end
747
777
end
748
778
end
@@ -765,14 +795,15 @@ defmodule Record do
765
795
defp core_specs(values) do
766
796
types = lc { _, _, spec } inlist values, do: spec
767
797
options = if values == [], do: [], else: [options_specs(values)]
798
+ values_specs = if values == [], do: [], else: values_specs(values)
768
799
769
800
quote do
770
801
unless Kernel.Typespec.defines_type?(__MODULE__, :t, 0) do
771
802
@type t :: { __MODULE__, unquote_splicing(types) }
772
803
end
773
804
774
805
unless Kernel.Typespec.defines_type?(__MODULE__, :options, 0) do
775
- @type options :: unquote(options)
806
+ @type options :: unquote(options) | [{String.t, unquote(values_specs)}]
776
807
end
777
808
778
809
@spec new :: t
@@ -790,6 +821,11 @@ defmodule Record do
790
821
{ :|, [], [{ k, v }, acc] }
791
822
end, { k, v }, t
792
823
end
824
+ defp values_specs([{ _, _, v }|t]) do
825
+ :lists.foldl fn { _, _, v }, acc ->
826
+ { :|, [], [v, acc] }
827
+ end, v, t
828
+ end
793
829
794
830
defp accessor_specs([{ :__exception__, _, _ }|t], 1, acc) do
795
831
accessor_specs(t, 2, acc)
0 commit comments