Skip to content

Commit aa09359

Browse files
author
José Valim
committed
Add Kernel.struct/2
1 parent 9595fb1 commit aa09359

File tree

4 files changed

+74
-2
lines changed

4 files changed

+74
-2
lines changed

lib/elixir/lib/enum.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,10 @@ defmodule Enum do
12111211
:lists.foldl(fun, acc, collection)
12121212
end
12131213

1214+
def reduce(collection, acc, fun) when is_map(collection) do
1215+
:maps.fold(fn k, v, acc -> fun.({ k, v }, acc) end, acc, collection)
1216+
end
1217+
12141218
def reduce(collection, acc, fun) do
12151219
Enumerable.reduce(collection, { :cont, acc },
12161220
fn x, acc -> { :cont, fun.(x, acc) } end) |> elem(1)

lib/elixir/lib/kernel.ex

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,55 @@ defmodule Kernel do
14791479
:erlang.setelement(index + 1, tuple, value)
14801480
end
14811481

1482+
@doc """
1483+
Creates and updates structs.
1484+
1485+
The struct argument may be an atom (which defines `defstruct`)
1486+
or a struct itself. The second argument is any Enumerable that
1487+
emits two-item tuples (key-value) during enumeration.
1488+
1489+
If one of the keys in the Enumerable does not exist in the struct,
1490+
they are automatically discarded.
1491+
1492+
This function is useful for dynamically creating and updating
1493+
structs.
1494+
1495+
## Example
1496+
1497+
defmodule User do
1498+
defstruct name: "jose"
1499+
end
1500+
1501+
struct(User)
1502+
#=> %User{name: "jose"}
1503+
1504+
opts = [name: "eric"]
1505+
user = struct(User, opts)
1506+
#=> %User{name: "eric"}
1507+
1508+
struct(user, unknown: "value")
1509+
#=> %User{name: "eric"}
1510+
1511+
"""
1512+
def struct(struct, kv \\ [])
1513+
1514+
def struct(struct, []) when is_atom(struct) do
1515+
apply(struct, :__struct__, [])
1516+
end
1517+
1518+
def struct(struct, kv) when is_atom(struct) do
1519+
struct(apply(struct, :__struct__, []), kv)
1520+
end
1521+
1522+
def struct(%{ __struct__: _ } = struct, kv) do
1523+
Enum.reduce(kv, struct, fn { k, v }, acc ->
1524+
case :maps.is_key(k, acc) do
1525+
true -> :maps.put(k, v, acc)
1526+
false -> acc
1527+
end
1528+
end)
1529+
end
1530+
14821531
## Implemented in Elixir
14831532

14841533
@doc """
@@ -3197,7 +3246,7 @@ defmodule Kernel do
31973246
end
31983247

31993248
@doc """
3200-
Defines the current module as a struct.
3249+
Defines a struct for the current module.
32013250
32023251
A struct is a tagged map that allows developers to provide
32033252
default values for keys, tags to be used in polymorphic
@@ -3208,6 +3257,9 @@ defmodule Kernel do
32083257
structs field. This macro is a convenience for doing such
32093258
function and a type `t` in one pass.
32103259
3260+
For more information about structs, please check
3261+
`Kernel.SpecialForms.%/2`.
3262+
32113263
## Examples
32123264
32133265
defmodule MyRange do

lib/elixir/lib/kernel/special_forms.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ defmodule Kernel.SpecialForms do
148148
argument is a struct, failing with `BadStructError` otherwise.
149149
150150
Check `Kernel.defprotocol/2` for more information on how structs
151-
can be used with protocols for polymorphic dispatch.
151+
can be used with protocols for polymorphic dispatch. Also,
152+
see `Kernel.struct/2` for examples on how to create and update
153+
structs dynamically.
152154
"""
153155
defmacro unquote(:%)(struct, map)
154156

lib/elixir/test/elixir/kernel_test.exs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,20 @@ defmodule KernelTest do
157157
refute binding() == [x: 2]
158158
end
159159

160+
defmodule User do
161+
defstruct name: "jose"
162+
end
163+
164+
test :struct do
165+
assert struct(User) == %User{name: "josé"}
166+
167+
user = struct(User, name: "eric")
168+
assert user == %User{ name: "eric" }
169+
170+
assert struct(user, unknown: "key") == user
171+
assert struct(user, name: "jose") == %User{ name: "josé" }
172+
end
173+
160174
defmodule Conversions do
161175
use ExUnit.Case, async: true
162176

0 commit comments

Comments
 (0)