Skip to content

Commit

Permalink
Improve types (#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
leandrocp authored Nov 9, 2023
1 parent 51b157a commit afeda28
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 27 deletions.
2 changes: 1 addition & 1 deletion lib/beacon/content/page.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ defmodule Beacon.Content.Page do
field :description, :string
field :template, :string
field :meta_tags, {:array, :map}, default: []
field :raw_schema, {:array, :map}, default: []
field :raw_schema, Beacon.Types.JsonArrayMap, default: []
field :order, :integer, default: 1
field :format, Beacon.Types.Atom, default: :heex
field :extra, :map, default: %{}
Expand Down
15 changes: 7 additions & 8 deletions lib/beacon/types/atom.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ defmodule Beacon.Types.Atom do

def type, do: :atom

def cast(:any, site) when is_binary(site), do: {:ok, String.to_existing_atom(site)}
def cast(:any, site) when is_atom(site), do: {:ok, site}
def cast(:any, _), do: :error

def cast(site) when is_binary(site), do: {:ok, String.to_existing_atom(site)}
def cast(site) when is_atom(site), do: {:ok, site}
def cast(_), do: :error

def load(site) when is_binary(site), do: {:ok, String.to_existing_atom(site)}
def cast(site), do: {:error, message: "invalid site #{inspect(site)}"}

def dump(site) when is_binary(site), do: {:ok, site}
def dump(site) when is_atom(site), do: {:ok, Atom.to_string(site)}
def dump(_), do: :error
def dump(_site), do: :error

def equal?(site1, site2), do: site1 === site2

def load(site) when is_binary(site), do: {:ok, String.to_existing_atom(site)}
def load(_site), do: :error
end
14 changes: 6 additions & 8 deletions lib/beacon/types/binary.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ defmodule Beacon.Types.Binary do

def type, do: :binary

def cast(:any, term), do: {:ok, term}
def cast(term), do: {:ok, term}
def cast(term) when is_binary(term), do: {:ok, term}
def cast(term), do: {:ok, :erlang.term_to_binary(term)}

def load(binary) when is_binary(binary) do
{:ok, :erlang.binary_to_term(binary)}
end
def dump(term) when is_binary(term), do: {:ok, term}
def dump(term), do: {:ok, :erlang.term_to_binary(term)}

def dump(term) do
{:ok, :erlang.term_to_binary(term)}
end
def load(binary) when is_binary(binary), do: {:ok, :erlang.binary_to_term(binary)}
def load(_binary), do: :error
end
57 changes: 57 additions & 0 deletions lib/beacon/types/json_array_map.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule Beacon.Types.JsonArrayMap do
@moduledoc """
Convert between json and map enforcing the data shape as array of objects/maps.
"""

use Ecto.Type

def type, do: :mine

def cast(term) when is_map(term), do: {:ok, [term]}

def cast(term) when is_list(term) do
case validate(term) do
{true, list} ->
{:ok, list}

{false, list} ->
{:error, message: "expected a list of map or a map, got: #{inspect(list)}"}
end
end

def cast(term) do
{:error, message: "expected a list of map or a map, got: #{inspect(term)}"}
end

def dump(term) when is_map(term), do: {:ok, [term]}

def dump(term) when is_list(term) do
case validate(term) do
{true, list} ->
{:ok, list}

{false, _list} ->
:error
end
end

def dump(_site), do: :error

def load(term) when is_list(term), do: {:ok, term}
def load(_term), do: :error

defp validate(term) when is_list(term) do
{valid, list} =
Enum.reduce_while(term, {true, []}, fn
t, {_valid, list} when is_map(t) ->
{:cont, {true, [t | list]}}

t, {_, list} ->
{:halt, {false, [t | list]}}
end)

{valid, Enum.reverse(list)}
end

defp validate(term), do: {false, term}
end
16 changes: 6 additions & 10 deletions lib/beacon/types/site.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,17 @@ defmodule Beacon.Types.Site do
@doc false
def type, do: :atom

@doc false
def cast(:any, site) when is_binary(site), do: {:ok, String.to_existing_atom(site)}
def cast(:any, site) when is_atom(site), do: {:ok, site}
def cast(:any, _), do: :error

@doc false
def cast(site) when is_binary(site), do: {:ok, String.to_existing_atom(site)}
def cast(site) when is_atom(site), do: {:ok, site}
def cast(_), do: :error

@doc false
def load(site) when is_binary(site), do: {:ok, String.to_existing_atom(site)}
def cast(site), do: {:error, message: "invalid site #{inspect(site)}"}

@doc false
def dump(site) when is_binary(site), do: {:ok, site}
def dump(site) when is_atom(site), do: {:ok, Atom.to_string(site)}
def dump(_), do: :error
def dump(_site), do: :error

@doc false
def load(site) when is_binary(site), do: {:ok, String.to_existing_atom(site)}
def load(_site), do: :error
end
13 changes: 13 additions & 0 deletions test/beacon/content_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,19 @@ defmodule Beacon.ContentTest do
assert_receive :lifecycle_after_create_page
assert_receive :lifecycle_after_publish_page
end

test "save raw_schema" do
layout = layout_fixture(site: :raw_schema_test)

assert %Page{raw_schema: [%{"foo" => "bar"}]} =
Content.create_page!(%{
site: "my_site",
path: "/",
template: "<p>page</p>",
layout_id: layout.id,
raw_schema: [%{"foo" => "bar"}]
})
end
end

describe "snippets" do
Expand Down
24 changes: 24 additions & 0 deletions test/beacon/types/atom_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule Beacon.Types.AtomTest do
use ExUnit.Case, async: true

alias Beacon.Types.Atom

_ = :site

test "cast" do
assert Atom.cast("site") == {:ok, :site}
assert Atom.cast(:site) == {:ok, :site}
assert Atom.cast(0) == {:error, [message: "invalid site 0"]}
end

test "dump" do
assert Atom.dump("site") == {:ok, "site"}
assert Atom.dump(:site) == {:ok, "site"}
assert Atom.dump(0) == :error
end

test "load" do
assert Atom.load("site") == {:ok, :site}
assert Atom.load(0) == :error
end
end
23 changes: 23 additions & 0 deletions test/beacon/types/binary_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule Beacon.Types.BinaryTest do
use ExUnit.Case, async: true

alias Beacon.Types.Binary

@term %{"foo" => :bar}
@binary :erlang.term_to_binary(@term)

test "cast" do
assert Binary.cast(@binary) == {:ok, @binary}
assert Binary.cast(@term) == {:ok, @binary}
end

test "dump" do
assert Binary.dump(@binary) == {:ok, @binary}
assert Binary.dump(@term) == {:ok, @binary}
end

test "load" do
assert Binary.load(@binary) == {:ok, @term}
assert Binary.load(@term) == :error
end
end
28 changes: 28 additions & 0 deletions test/beacon/types/json_array_map_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule Beacon.Types.JsonArrayMapTest do
use ExUnit.Case, async: true

alias Beacon.Types.JsonArrayMap

@map %{"foo" => :bar}

test "cast" do
assert JsonArrayMap.cast(@map) == {:ok, [@map]}
assert JsonArrayMap.cast([@map]) == {:ok, [@map]}
assert JsonArrayMap.cast([]) == {:ok, []}
assert JsonArrayMap.cast(nil) == {:error, [{:message, "expected a list of map or a map, got: nil"}]}
assert JsonArrayMap.cast([1]) == {:error, [{:message, "expected a list of map or a map, got: [1]"}]}
end

test "dump" do
assert JsonArrayMap.dump(@map) == {:ok, [@map]}
assert JsonArrayMap.dump([@map]) == {:ok, [@map]}
assert JsonArrayMap.dump([]) == {:ok, []}
assert JsonArrayMap.dump(nil) == :error
assert JsonArrayMap.dump([1]) == :error
end

test "load" do
assert JsonArrayMap.load([@map]) == {:ok, [@map]}
assert JsonArrayMap.load(@map) == :error
end
end
27 changes: 27 additions & 0 deletions test/beacon/types/site_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule Beacon.Types.SiteTest do
use ExUnit.Case, async: true

alias Beacon.Types.Site
import Beacon.Types.Site, only: [valid?: 1]

doctest Site, only: [valid?: 1]

_ = :site

test "cast" do
assert Site.cast("site") == {:ok, :site}
assert Site.cast(:site) == {:ok, :site}
assert Site.cast(0) == {:error, [message: "invalid site 0"]}
end

test "dump" do
assert Site.dump("site") == {:ok, "site"}
assert Site.dump(:site) == {:ok, "site"}
assert Site.dump(0) == :error
end

test "load" do
assert Site.load("site") == {:ok, :site}
assert Site.load(0) == :error
end
end

0 comments on commit afeda28

Please sign in to comment.