Skip to content

Commit

Permalink
Add Ecto.embedded_load/3 function (#3270)
Browse files Browse the repository at this point in the history
  • Loading branch information
narrowtux committed Apr 6, 2020
1 parent 1e0b077 commit c97822b
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 0 deletions.
35 changes: 35 additions & 0 deletions lib/ecto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -601,4 +601,39 @@ defmodule Ecto do
true
end
end

@doc """
Loads previously dumped `data` in the given `format` into a schema.
The first argument can be a an embedded schema module, or a map (of types) and
determines the return value: a struct or a map, respectively.
The second argument `data` specifies fields and values that are to be loaded.
It can be a map, a keyword list, or a `{fields, values}` tuple. Fields can be
atoms or strings.
The third argument `format` is the format the data has been dumped as. For
example, databases may dump embedded to `:json`, this function allows such
dumped data to be put back into the schemas.
Fields that are not present in the schema (or `types` map) are ignored.
If any of the values has invalid type, an error is raised.
Note that if you want to load data into a non-embedded schema that was
directly persisted into a given repository, then use `c:Ecto.Repo.load/3`.
## Examples
iex> result = Ecto.Adapters.SQL.query!(MyRepo, "SELECT users.settings FROM users", [])
iex> Enum.map(result.rows, fn [settings] -> Ecto.embedded_load(Setting, Jason.decode!(settings), :json) end)
[%Setting{...}, ...]
"""
@spec embedded_load(
module_or_map :: module | map(),
data :: map() | Keyword.t(),
format :: atom()
) :: Ecto.Schema.t() | map()
def embedded_load(schema_or_types, data, format) do
Ecto.Repo.Schema.embedded_load(schema_or_types, data, format)
end
end
2 changes: 2 additions & 0 deletions lib/ecto/repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,8 @@ defmodule Ecto.Repo do
Fields that are not present in the schema (or `types` map) are ignored.
If any of the values has invalid type, an error is raised.
To load data from non-database sources, use `Ecto.embedded_load/3`.
## Examples
iex> MyRepo.load(User, %{name: "Alice", age: 25})
Expand Down
4 changes: 4 additions & 0 deletions lib/ecto/repo/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,10 @@ defmodule Ecto.Repo.Schema do
do_load(schema_or_types, data, &Ecto.Type.adapter_load(adapter, &1, &2))
end

def embedded_load(schema_or_types, data, format) do
do_load(schema_or_types, data, &Ecto.Type.embedded_load(&1, &2, format))
end

defp do_load(schema, data, loader) when is_list(data),
do: do_load(schema, Map.new(data), loader)
defp do_load(schema, {fields, values}, loader) when is_list(fields) and is_list(values),
Expand Down
27 changes: 27 additions & 0 deletions test/ecto/embedded_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ defmodule Ecto.EmbeddedTest do
end
end

defmodule MySchemaWithUuid do
use Ecto.Schema

schema "my_schema" do
field :uuid, Ecto.UUID
end
end

test "__schema__" do
assert Author.__schema__(:embeds) ==
[:profile, :post, :posts]
Expand All @@ -25,4 +33,23 @@ defmodule Ecto.EmbeddedTest do
assert Author.__schema__(:embed, :posts) ==
%Embedded{field: :posts, cardinality: :many, owner: Author, on_replace: :delete, related: Post}
end

test "embedded_load/3" do
uuid = Ecto.UUID.generate()

assert %MySchemaWithUuid{uuid: ^uuid} =
Ecto.embedded_load(MySchemaWithUuid, %{"uuid" => uuid}, :json)

assert %MySchemaWithUuid{uuid: ^uuid} =
Ecto.embedded_load(MySchemaWithUuid, %{uuid: uuid}, :json)

assert %MySchemaWithUuid{uuid: nil} =
Ecto.embedded_load(MySchemaWithUuid, %{"uuid" => nil}, :json)

assert_raise ArgumentError,
~s[cannot load `"ABC"` as type Ecto.UUID for field `uuid` in schema Ecto.EmbeddedTest.MySchemaWithUuid],
fn ->
Ecto.embedded_load(MySchemaWithUuid, %{"uuid" => "ABC"}, :json)
end
end
end

0 comments on commit c97822b

Please sign in to comment.