Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve types #375

Merged
merged 1 commit into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading