From b9abbb146de4bddae07af4ae58aaa893a5633a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Guti=C3=A9rrez?= Date: Sat, 16 Nov 2024 21:20:12 +0100 Subject: [PATCH 1/3] Ui --- .formatter.exs | 2 + lib/sleeky.ex | 5 +- lib/sleeky/job.ex | 6 +- lib/sleeky/template.ex | 30 ------- lib/sleeky/ui.ex | 5 +- lib/sleeky/ui/dsl.ex | 1 + lib/sleeky/ui/dsl/page.ex | 3 +- lib/sleeky/ui/dsl/ui.ex | 2 +- lib/sleeky/ui/generator/router.ex | 35 +++++--- lib/sleeky/ui/parser.ex | 35 ++++---- lib/sleeky/ui/view.ex | 11 +++ lib/sleeky/{ => ui}/view/dsl.ex | 4 +- lib/sleeky/ui/view/generator/data.ex | 13 +++ lib/sleeky/{ => ui}/view/generator/render.ex | 4 +- lib/sleeky/{ => ui}/view/generator/source.ex | 2 +- lib/sleeky/{ => ui}/view/parser.ex | 2 +- lib/sleeky/{ => ui}/view/render.ex | 4 +- lib/sleeky/ui/view/resolve.ex | 48 +++++++++++ lib/sleeky/view.ex | 10 --- lib/sleeky/view/resolve.ex | 84 -------------------- mix.lock | 2 +- test/sleeky/view_test.exs | 80 +++---------------- test/support/blogs/ui.ex | 2 +- test/support/blogs/ui/error.ex | 16 ++++ test/support/blogs/ui/index.ex | 6 +- test/support/blogs/ui/not_found.ex | 16 ++++ 26 files changed, 185 insertions(+), 243 deletions(-) delete mode 100644 lib/sleeky/template.ex create mode 100644 lib/sleeky/ui/view.ex rename lib/sleeky/{ => ui}/view/dsl.ex (97%) create mode 100644 lib/sleeky/ui/view/generator/data.ex rename lib/sleeky/{ => ui}/view/generator/render.ex (84%) rename lib/sleeky/{ => ui}/view/generator/source.ex (79%) rename lib/sleeky/{ => ui}/view/parser.ex (76%) rename lib/sleeky/{ => ui}/view/render.ex (93%) create mode 100644 lib/sleeky/ui/view/resolve.ex delete mode 100644 lib/sleeky/view.ex delete mode 100644 lib/sleeky/view/resolve.ex create mode 100644 test/support/blogs/ui/error.ex create mode 100644 test/support/blogs/ui/not_found.ex diff --git a/.formatter.exs b/.formatter.exs index 859834e..831b0bd 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -2,6 +2,7 @@ locals_without_parens = [ a: :*, abbr: :*, action: :*, + action: :*, address: :*, all: :*, all: :*, @@ -92,6 +93,7 @@ locals_without_parens = [ not_nil: :*, object: :*, ol: :*, + on: :*, one: :*, one: :*, optgroup: :*, diff --git a/lib/sleeky.ex b/lib/sleeky.ex index 16b691a..71b81b5 100644 --- a/lib/sleeky.ex +++ b/lib/sleeky.ex @@ -12,8 +12,9 @@ defmodule Sleeky do Sleeky.Context.Dsl, Sleeky.JsonApi.Dsl, Sleeky.Endpoint.Dsl, - Sleeky.View.Dsl, - Sleeky.Ui.Dsl + Sleeky.Ui.Dsl, + Sleeky.Ui.Action.Dsl, + Sleeky.Ui.View.Dsl ] @doc """ diff --git a/lib/sleeky/job.ex b/lib/sleeky/job.ex index 662a396..aa84631 100644 --- a/lib/sleeky/job.ex +++ b/lib/sleeky/job.ex @@ -82,7 +82,9 @@ defmodule Sleeky.Job do model: model, id: id, queue: job.queue, + job: job.id, reason: format_error(reason), + attempt: job.attempt, attempts_left: attempts_left ) end @@ -92,7 +94,9 @@ defmodule Sleeky.Job do task: task, model: model, id: id, - queue: job.queue + job: job.id, + queue: job.queue, + attempt: job.attempt ) end diff --git a/lib/sleeky/template.ex b/lib/sleeky/template.ex deleted file mode 100644 index eebc22e..0000000 --- a/lib/sleeky/template.ex +++ /dev/null @@ -1,30 +0,0 @@ -defmodule Sleeky.Template do - @moduledoc """ - Simple templating using Solid - """ - - def render!(text, vars, opts \\ []) - - def render!(text, vars, opts) when is_binary(text) do - text - |> Solid.parse!() - |> render!(vars, opts) - end - - def render!(template, vars, opts) do - vars = string_keys(vars) - - template |> Solid.render!(vars, opts) |> to_string() - end - - defp string_keys(map) when is_map(map) do - map - |> Enum.map(fn {key, value} -> - {to_string(key), string_keys(value)} - end) - |> Enum.into(%{}) - end - - defp string_keys(items) when is_list(items), do: Enum.map(items, &string_keys/1) - defp string_keys(other), do: other -end diff --git a/lib/sleeky/ui.ex b/lib/sleeky/ui.ex index 7e3e4d4..f8ca7d3 100644 --- a/lib/sleeky/ui.ex +++ b/lib/sleeky/ui.ex @@ -2,14 +2,15 @@ defmodule Sleeky.Ui do @moduledoc false use Diesel, otp_app: :sleeky, + dsl: Sleeky.Ui.Dsl, generators: [ Sleeky.Ui.Generator.Router ] - defstruct [:pages] + defstruct [:pages, :error_view, :not_found_view] defmodule Page do @moduledoc false - defstruct [:path, :module] + defstruct [:method, :path, :module, :runtime] end end diff --git a/lib/sleeky/ui/dsl.ex b/lib/sleeky/ui/dsl.ex index 4f3da2d..fd29961 100644 --- a/lib/sleeky/ui/dsl.ex +++ b/lib/sleeky/ui/dsl.ex @@ -2,6 +2,7 @@ defmodule Sleeky.Ui.Dsl do @moduledoc false use Diesel.Dsl, otp_app: :sleeky, + root: Sleeky.Ui.Dsl.Ui, tags: [ Sleeky.Ui.Dsl.Page ] diff --git a/lib/sleeky/ui/dsl/page.ex b/lib/sleeky/ui/dsl/page.ex index 4b4c66f..40386cb 100644 --- a/lib/sleeky/ui/dsl/page.ex +++ b/lib/sleeky/ui/dsl/page.ex @@ -3,6 +3,7 @@ defmodule Sleeky.Ui.Dsl.Page do use Diesel.Tag tag do - child kind: :module, min: 1, max: 1 + attribute :name, kind: :module + attribute :at, kind: :string end end diff --git a/lib/sleeky/ui/dsl/ui.ex b/lib/sleeky/ui/dsl/ui.ex index 136fe4a..857dc36 100644 --- a/lib/sleeky/ui/dsl/ui.ex +++ b/lib/sleeky/ui/dsl/ui.ex @@ -3,6 +3,6 @@ defmodule Sleeky.Ui.Dsl.Ui do use Diesel.Tag tag do - child :page, min: 0 + child :page, min: 1 end end diff --git a/lib/sleeky/ui/generator/router.ex b/lib/sleeky/ui/generator/router.ex index 4b3b314..334695a 100644 --- a/lib/sleeky/ui/generator/router.ex +++ b/lib/sleeky/ui/generator/router.ex @@ -11,16 +11,7 @@ defmodule Sleeky.Ui.Generator.Router do conn = var(:conn) - routes = - for {path, page} <- ui.pages do - html = page.render() - - quote do - get unquote(path) do - send_html(unquote(conn), unquote(html), 200) - end - end - end + routes = Enum.map(ui.pages, &route(ui, &1)) quote do defmodule unquote(module_name) do @@ -47,4 +38,28 @@ defmodule Sleeky.Ui.Generator.Router do end end end + + defp route(ui, page) do + conn = var(:conn) + not_found_view = ui.not_found_view + error_view = ui.error_view + + quote do + match unquote(page.path), via: unquote(page.method) do + case unquote(page.module).data(unquote(conn).params) do + {:ok, data} -> + html = unquote(page.module).render(data) + send_html(unquote(conn), html, 200) + + {:error, :not_found} -> + html = unquote(not_found_view).render(unquote(conn).params) + send_html(unquote(conn), html, 200) + + {:error, other} -> + html = unquote(error_view).render(unquote(conn).params) + send_html(unquote(conn), html, 200) + end + end + end + end end diff --git a/lib/sleeky/ui/parser.ex b/lib/sleeky/ui/parser.ex index 51b3d19..4a58449 100644 --- a/lib/sleeky/ui/parser.ex +++ b/lib/sleeky/ui/parser.ex @@ -3,31 +3,28 @@ defmodule Sleeky.Ui.Parser do @behaviour Diesel.Parser alias Sleeky.Ui + alias Sleeky.Ui.Page @impl true - def parse({_, _, children}, opts) do - caller = opts[:caller_module] + def parse({:ui, _, pages}, opts) do + ui = Keyword.fetch!(opts, :caller_module) pages = - for {:page, _, [module]} <- children, into: %{} do - {path(caller, module), module} - end - - %Ui{pages: pages} - end + for {:page, page, _} <- pages do + path = Keyword.fetch!(page, :at) + module = Keyword.fetch!(page, :name) + method = Keyword.get(page, :method, :get) - defp path(caller, module) do - caller = Module.split(caller) + %Page{ + method: method, + path: path, + module: module + } + end - path = - module - |> Module.split() - |> Kernel.--(caller) - |> Enum.map(&Inflex.parameterize/1) - |> Enum.reject(&(&1 == "index")) - |> Enum.join("/") - |> String.replace("//", "/") + error_view = Module.concat(ui, Error) + not_found_view = Module.concat(ui, NotFound) - "/" <> path + %Ui{pages: pages, error_view: error_view, not_found_view: not_found_view} end end diff --git a/lib/sleeky/ui/view.ex b/lib/sleeky/ui/view.ex new file mode 100644 index 0000000..f040a4c --- /dev/null +++ b/lib/sleeky/ui/view.ex @@ -0,0 +1,11 @@ +defmodule Sleeky.Ui.View do + @moduledoc false + use Diesel, + otp_app: :sleeky, + overrides: [div: 2], + generators: [ + Sleeky.Ui.View.Generator.Render, + Sleeky.Ui.View.Generator.Source, + Sleeky.Ui.View.Generator.Data + ] +end diff --git a/lib/sleeky/view/dsl.ex b/lib/sleeky/ui/view/dsl.ex similarity index 97% rename from lib/sleeky/view/dsl.ex rename to lib/sleeky/ui/view/dsl.ex index 21f39bd..8cd03b1 100644 --- a/lib/sleeky/view/dsl.ex +++ b/lib/sleeky/ui/view/dsl.ex @@ -1,4 +1,4 @@ -defmodule Sleeky.View.Dsl do +defmodule Sleeky.Ui.View.Dsl do @moduledoc false use Diesel.Dsl, otp_app: :sleeky, @@ -116,7 +116,7 @@ defmodule Sleeky.View.Dsl do :video, :wbr, # custom extensions - :using, + :layout, :slot, :each, :expand diff --git a/lib/sleeky/ui/view/generator/data.ex b/lib/sleeky/ui/view/generator/data.ex new file mode 100644 index 0000000..89da873 --- /dev/null +++ b/lib/sleeky/ui/view/generator/data.ex @@ -0,0 +1,13 @@ +defmodule Sleeky.Ui.View.Generator.Data do + @moduledoc false + @behaviour Diesel.Generator + + @impl true + def generate(_html, _opts) do + quote do + def data(params), do: {:ok, params} + + defoverridable data: 1 + end + end +end diff --git a/lib/sleeky/view/generator/render.ex b/lib/sleeky/ui/view/generator/render.ex similarity index 84% rename from lib/sleeky/view/generator/render.ex rename to lib/sleeky/ui/view/generator/render.ex index 811734a..248b45c 100644 --- a/lib/sleeky/view/generator/render.ex +++ b/lib/sleeky/ui/view/generator/render.ex @@ -1,8 +1,8 @@ -defmodule Sleeky.View.Generator.Render do +defmodule Sleeky.Ui.View.Generator.Render do @moduledoc false @behaviour Diesel.Generator - alias Sleeky.View + alias Sleeky.Ui.View @impl true def generate(html, _opts) do diff --git a/lib/sleeky/view/generator/source.ex b/lib/sleeky/ui/view/generator/source.ex similarity index 79% rename from lib/sleeky/view/generator/source.ex rename to lib/sleeky/ui/view/generator/source.ex index 90171db..376d0e7 100644 --- a/lib/sleeky/view/generator/source.ex +++ b/lib/sleeky/ui/view/generator/source.ex @@ -1,4 +1,4 @@ -defmodule Sleeky.View.Generator.Source do +defmodule Sleeky.Ui.View.Generator.Source do @moduledoc false @behaviour Diesel.Generator diff --git a/lib/sleeky/view/parser.ex b/lib/sleeky/ui/view/parser.ex similarity index 76% rename from lib/sleeky/view/parser.ex rename to lib/sleeky/ui/view/parser.ex index b800390..2d22d2f 100644 --- a/lib/sleeky/view/parser.ex +++ b/lib/sleeky/ui/view/parser.ex @@ -1,4 +1,4 @@ -defmodule Sleeky.View.Parser do +defmodule Sleeky.Ui.View.Parser do @moduledoc false @behaviour Diesel.Parser diff --git a/lib/sleeky/view/render.ex b/lib/sleeky/ui/view/render.ex similarity index 93% rename from lib/sleeky/view/render.ex rename to lib/sleeky/ui/view/render.ex index ec67f9e..08a0549 100644 --- a/lib/sleeky/view/render.ex +++ b/lib/sleeky/ui/view/render.ex @@ -1,4 +1,4 @@ -defmodule Sleeky.View.Render do +defmodule Sleeky.Ui.View.Render do @moduledoc false @self_closing_tags [ @@ -55,7 +55,7 @@ defmodule Sleeky.View.Render do " #{Enum.map_join(attrs, " ", &do_attr/1)}" end - defp do_attr({name, _}) when name in [:defer] do + defp do_attr({name, true}) do "#{name}" end diff --git a/lib/sleeky/ui/view/resolve.ex b/lib/sleeky/ui/view/resolve.ex new file mode 100644 index 0000000..041a5ef --- /dev/null +++ b/lib/sleeky/ui/view/resolve.ex @@ -0,0 +1,48 @@ +defmodule Sleeky.Ui.View.Resolve do + @moduledoc false + + def resolve({:layout, [name: layout], children}, slots) do + more_slots = + for {:slot, [name: name], value} <- children, into: %{} do + value = + case value do + [component] when is_atom(component) -> component + values when is_list(values) -> values + end + + {name, value} + end + + slots = Map.merge(slots, more_slots) + + layout.source() |> resolve(slots) + end + + def resolve({:slot, _, [name]}, slots) do + case Map.get(slots, name) do + nil -> + {:div, [], []} + + component when is_atom(component) -> + component.source() |> resolve(slots) + + nodes when is_list(nodes) -> + resolve(nodes, slots) + end + end + + def resolve({:each, attrs, [{_, _, _} = child]}, slots) do + child = resolve(child, slots) + {:each, attrs, [child]} + end + + def resolve({:each, attrs, [component]}, slots) when is_atom(component), + do: resolve({:each, attrs, [component.source()]}, slots) + + def resolve({tag, attrs, children}, slots), + do: {tag, attrs, Enum.map(children, &resolve(&1, slots))} + + def resolve(nodes, slots) when is_list(nodes), do: Enum.map(nodes, &resolve(&1, slots)) + def resolve(text, _slots) when is_binary(text), do: text + def resolve(value, _slots) when is_atom(value) or is_number(value), do: to_string(value) +end diff --git a/lib/sleeky/view.ex b/lib/sleeky/view.ex deleted file mode 100644 index 6bfefed..0000000 --- a/lib/sleeky/view.ex +++ /dev/null @@ -1,10 +0,0 @@ -defmodule Sleeky.View do - @moduledoc false - use Diesel, - otp_app: :sleeky, - overrides: [div: 2], - generators: [ - Sleeky.View.Generator.Render, - Sleeky.View.Generator.Source - ] -end diff --git a/lib/sleeky/view/resolve.ex b/lib/sleeky/view/resolve.ex deleted file mode 100644 index 887cb49..0000000 --- a/lib/sleeky/view/resolve.ex +++ /dev/null @@ -1,84 +0,0 @@ -defmodule Sleeky.View.Resolve do - @moduledoc false - - alias Sleeky.Template - - def resolve({:using, [name: layout], children}, slots) do - more_slots = - for {:slot, [name: name], value} <- children, into: %{} do - value = - case value do - [component] when is_atom(component) -> component - values when is_list(values) -> values - end - - {name, value} - end - - slots = Map.merge(slots, more_slots) - - layout.source() |> resolve(slots) - end - - def resolve({:slot, _, [name]}, slots) do - case Map.get(slots, name) do - nil -> - {:div, [], []} - - component -> - component.source() |> resolve(slots) - end - end - - def resolve({:each, attrs, [{_, _, _} = child]}, slots) do - child = resolve(child, slots) - {:each, attrs, [child]} - end - - def resolve({:each, attrs, [component]}, slots) when is_atom(component) do - resolve({:each, attrs, [component.source()]}, slots) - end - - def resolve({:expand, attrs, [{_, _, _} = child]}, slots) do - slot = Keyword.fetch!(attrs, :name) - alias = Keyword.fetch!(attrs, :as) - items = Map.get(slots, slot, []) - - for item <- items do - slots = - slots - |> Map.put(alias, Map.new(item)) - |> Map.put(:__template__, true) - - resolve(child, slots) - end - end - - def resolve({:expand, attrs, [component]}, slots) when is_atom(component) do - resolve({:expand, attrs, [component.source()]}, slots) - end - - def resolve({tag, attrs, children}, slots) do - {tag, resolve_attributes(attrs, slots), Enum.map(children, &resolve(&1, slots))} - end - - def resolve(text, slots) when is_binary(text) do - if Map.get(slots, :__template__) do - Template.render!(text, slots, strict_variables: true) - else - text - end - end - - def resolve(other, slots), do: other |> to_string() |> resolve(slots) - - defp resolve_attributes(attrs, slots) do - if Map.get(slots, :__template__) do - for {name, value} <- attrs do - {name, Template.render!(value, slots, strict_variables: true)} - end - else - attrs - end - end -end diff --git a/mix.lock b/mix.lock index b0fdd13..173190c 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, - "diesel": {:hex, :diesel, "0.7.1", "1be60b15e11dbeb07d6541cfe13418e6432994bbc3325821ee16843228aee778", [:mix], [], "hexpm", "96d2581d4e9282474850a6f058f9e5227d3d913b75f35274c6031ae1e1e06462"}, + "diesel": {:hex, :diesel, "0.7.2", "d3b9d7e0b89f2af8468f0a8d87d609b4608bf5834cbc7d7057ade09ad7e7ba76", [:mix], [], "hexpm", "08dab6a5d30b7f164426eefe6fef82d9a94ca59416bcd1614593875a48b3091c"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "ecto": {:hex, :ecto, "3.12.3", "1a9111560731f6c3606924c81c870a68a34c819f6d4f03822f370ea31a582208", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9efd91506ae722f95e48dc49e70d0cb632ede3b7a23896252a60a14ac6d59165"}, "ecto_sql": {:hex, :ecto_sql, "3.12.0", "73cea17edfa54bde76ee8561b30d29ea08f630959685006d9c6e7d1e59113b7d", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dc9e4d206f274f3947e96142a8fdc5f69a2a6a9abb4649ef5c882323b6d512f0"}, diff --git a/test/sleeky/view_test.exs b/test/sleeky/view_test.exs index 8995933..81f1b67 100644 --- a/test/sleeky/view_test.exs +++ b/test/sleeky/view_test.exs @@ -1,8 +1,8 @@ -defmodule Sleeky.ViewTest do +defmodule Sleeky.Ui.ViewTest do use ExUnit.Case defmodule Div do - use Sleeky.View + use Sleeky.Ui.View view do div id: "myDiv", class: "{{ myClass }}" do @@ -12,7 +12,7 @@ defmodule Sleeky.ViewTest do end defmodule Layout do - use Sleeky.View + use Sleeky.Ui.View view do header do @@ -22,10 +22,10 @@ defmodule Sleeky.ViewTest do end defmodule Page do - use Sleeky.View + use Sleeky.Ui.View view do - using Layout do + layout Layout do slot :header do Div end @@ -34,7 +34,7 @@ defmodule Sleeky.ViewTest do end defmodule Items do - use Sleeky.View + use Sleeky.Ui.View view do ul do @@ -48,7 +48,7 @@ defmodule Sleeky.ViewTest do end defmodule Item do - use Sleeky.View + use Sleeky.Ui.View view do li do @@ -58,7 +58,7 @@ defmodule Sleeky.ViewTest do end defmodule NamedItems do - use Sleeky.View + use Sleeky.Ui.View view do ul do @@ -70,7 +70,7 @@ defmodule Sleeky.ViewTest do end defmodule Link do - use Sleeky.View + use Sleeky.Ui.View view do a href: "{{ link.url }}" do @@ -79,60 +79,8 @@ defmodule Sleeky.ViewTest do end end - defmodule Nav do - use Sleeky.View - - view do - expand :links, as: :link do - a href: "{{ link.url }}" do - "{{ link.title }}" - end - end - end - end - - defmodule NamedNav do - use Sleeky.View - - view do - expand :links, as: :link do - Link - end - end - end - - defmodule Menu do - use Sleeky.View - - view do - nav do - using Nav do - slot :links do - [url: "/one", title: "one"] - [url: "/two", title: "two"] - end - end - end - end - end - - defmodule NamedMenu do - use Sleeky.View - - view do - nav do - using NamedNav do - slot :links do - [url: "/one", title: "one"] - [url: "/two", title: "two"] - end - end - end - end - end - defmodule Html do - use Sleeky.View + use Sleeky.Ui.View view do html do @@ -170,14 +118,6 @@ defmodule Sleeky.ViewTest do assert "" = NamedItems.render(params) end - test "expands data slots using inline html" do - assert "" = Menu.render() - end - - test "expands data slots using named views" do - assert "" = NamedMenu.render() - end - test "renders doctype for html pages" do assert "" = Html.render() end diff --git a/test/support/blogs/ui.ex b/test/support/blogs/ui.ex index 94ec242..828fb89 100644 --- a/test/support/blogs/ui.ex +++ b/test/support/blogs/ui.ex @@ -3,6 +3,6 @@ defmodule Blogs.Ui do use Sleeky.Ui ui do - page Blogs.Ui.Index + page Blogs.Ui.Index, at: "/" end end diff --git a/test/support/blogs/ui/error.ex b/test/support/blogs/ui/error.ex new file mode 100644 index 0000000..3c26e6b --- /dev/null +++ b/test/support/blogs/ui/error.ex @@ -0,0 +1,16 @@ +defmodule Blogs.Ui.Index do + @moduledoc false + use Sleeky.Ui.View + + view do + html do + head do + title "Blogs Index Page" + end + + body do + h1 "It works!" + end + end + end +end diff --git a/test/support/blogs/ui/index.ex b/test/support/blogs/ui/index.ex index 62f9d20..22332b0 100644 --- a/test/support/blogs/ui/index.ex +++ b/test/support/blogs/ui/index.ex @@ -1,6 +1,6 @@ -defmodule Blogs.Ui.Index do +defmodule Blogs.Ui.Error do @moduledoc false - use Sleeky.View + use Sleeky.Ui.View view do html do @@ -9,7 +9,7 @@ defmodule Blogs.Ui.Index do end body do - h1 "It works!" + h1 "Error Page" end end end diff --git a/test/support/blogs/ui/not_found.ex b/test/support/blogs/ui/not_found.ex new file mode 100644 index 0000000..2cc936d --- /dev/null +++ b/test/support/blogs/ui/not_found.ex @@ -0,0 +1,16 @@ +defmodule Blogs.Ui.NotFound do + @moduledoc false + use Sleeky.Ui.View + + view do + html do + head do + title "Blogs Index Page" + end + + body do + h1 "Not found" + end + end + end +end From ab3c550384c25797e67640f55f0a50e2be8825a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Guti=C3=A9rrez?= Date: Sun, 24 Nov 2024 23:00:20 +0100 Subject: [PATCH 2/3] UI redirects --- lib/sleeky/ui/dsl/page.ex | 6 ++++++ lib/sleeky/ui/generator/router.ex | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/lib/sleeky/ui/dsl/page.ex b/lib/sleeky/ui/dsl/page.ex index 40386cb..e9325fb 100644 --- a/lib/sleeky/ui/dsl/page.ex +++ b/lib/sleeky/ui/dsl/page.ex @@ -5,5 +5,11 @@ defmodule Sleeky.Ui.Dsl.Page do tag do attribute :name, kind: :module attribute :at, kind: :string + + attribute :method, + kind: :atom, + in: [:get, :post, :put, :delete], + default: :get, + required: false end end diff --git a/lib/sleeky/ui/generator/router.ex b/lib/sleeky/ui/generator/router.ex index 334695a..e6f7f66 100644 --- a/lib/sleeky/ui/generator/router.ex +++ b/lib/sleeky/ui/generator/router.ex @@ -51,6 +51,10 @@ defmodule Sleeky.Ui.Generator.Router do html = unquote(page.module).render(data) send_html(unquote(conn), html, 200) + {:ok, :redirect, path} -> + conn = put_resp_header(unquote(conn), "location", path) + send_resp(conn, 302, "") + {:error, :not_found} -> html = unquote(not_found_view).render(unquote(conn).params) send_html(unquote(conn), html, 200) From 3acc45a7cd77f8a46eee5c95c9d1aed93ed53983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Guti=C3=A9rrez?= Date: Sun, 24 Nov 2024 23:00:39 +0100 Subject: [PATCH 3/3] Bulk create context functions --- .../context/generator/create_actions.ex | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/sleeky/context/generator/create_actions.ex b/lib/sleeky/context/generator/create_actions.ex index 40dfe79..744e308 100644 --- a/lib/sleeky/context/generator/create_actions.ex +++ b/lib/sleeky/context/generator/create_actions.ex @@ -6,7 +6,8 @@ defmodule Sleeky.Context.Generator.CreateActions do @impl true def generate(context, _) do - create_funs(context) ++ do_create_funs(context) ++ create_children_funs(context) + create_funs(context) ++ + do_create_funs(context) ++ bulk_create_funs(context) ++ create_children_funs(context) end defp create_funs(context) do @@ -123,4 +124,33 @@ defmodule Sleeky.Context.Generator.CreateActions do end end end + + defp bulk_create_funs(context) do + for model <- context.models, %{name: :create} <- model.actions() do + single_fun_name = String.to_atom("create_#{model.name()}") + bulk_fun_name = String.to_atom("create_#{model.plural()}") + + quote do + def unquote(bulk_fun_name)(items, context \\ %{}) when is_list(items) do + repo = repo() + + with {:ok, :ok} <- + repo.transaction(fn -> + items + |> Enum.reduce_while(nil, fn item, _ -> + case unquote(single_fun_name)(item, context) do + {:ok, _} -> {:cont, :ok} + {:error, _} = error -> {:halt, error} + end + end) + |> then(fn + :ok -> :ok + {:error, reason} -> repo.rollback(reason) + end) + end), + do: :ok + end + end + end + end end