diff --git a/.formatter.exs b/.formatter.exs index 3392b8e..223c6af 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,5 +1,6 @@ # Used by "mix format" [ - import_deps: [:ash], - inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] + import_deps: [:ash, :phoenix], + plugins: [Phoenix.LiveView.HTMLFormatter], + inputs: ["{mix}.exs", "{config,lib,test}/**/*.{ex,exs,heex}"] ] diff --git a/lib/ash_phoenix/gen/live.ex b/lib/ash_phoenix/gen/live.ex index 8569cf9..4e3ac1c 100644 --- a/lib/ash_phoenix/gen/live.ex +++ b/lib/ash_phoenix/gen/live.ex @@ -1,17 +1,21 @@ defmodule AshPhoenix.Gen.Live do @moduledoc false - def generate_from_cli(argv) do - {domain, resource, opts, _rest} = AshPhoenix.Gen.parse_opts(argv) + def generate_from_cli(%Igniter{} = igniter, options) do + domain = Keyword.fetch!(options, :domain) |> String.to_existing_atom() + resource = Keyword.fetch!(options, :resource) |> String.to_existing_atom() + resource_plural = Keyword.fetch!(options, :resourceplural) + opts = [] generate( + igniter, domain, resource, - Keyword.put(opts, :interactive?, true) + Keyword.put(opts, :interactive?, true) |> Keyword.put(:resource_plural, resource_plural) ) end - def generate(domain, resource, opts \\ []) do + def generate(igniter, domain, resource, opts \\ []) do Code.ensure_compiled!(domain) Code.ensure_compiled!(resource) @@ -55,13 +59,13 @@ defmodule AshPhoenix.Gen.Live do [ domain: inspect(domain), resource: inspect(resource), - web_module: inspect(web_module()), + web_module: inspect(web_module(igniter)), actor: opts[:actor], actor_opt: actor_opt(opts) ] |> add_resource_assigns(resource, opts) - web_live = Path.join([web_path(), "live", "#{assigns[:resource_singular]}_live"]) + web_live = Path.join([web_path(igniter), "live", "#{assigns[:resource_singular]}_live"]) generate_opts = if opts[:interactive?] do @@ -70,40 +74,50 @@ defmodule AshPhoenix.Gen.Live do [force: true, quiet: true] end - write_formatted_template( - "ash_phoenix.gen.live/index.ex.eex", - "index.ex", - web_live, - assigns, - generate_opts - ) - - if assigns[:update_action] || assigns[:create_action] do + igniter = write_formatted_template( - "ash_phoenix.gen.live/form_component.ex.eex", - "form_component.ex", + igniter, + "ash_phoenix.gen.live/index.ex.eex", + "index.ex", web_live, assigns, generate_opts ) - end - write_formatted_template( - "ash_phoenix.gen.live/show.ex.eex", - "show.ex", - web_live, - assigns, - generate_opts - ) + igniter = + if assigns[:update_action] || assigns[:create_action] do + write_formatted_template( + igniter, + "ash_phoenix.gen.live/form_component.ex.eex", + "form_component.ex", + web_live, + assigns, + generate_opts + ) + else + igniter + end + + igniter = + write_formatted_template( + igniter, + "ash_phoenix.gen.live/show.ex.eex", + "show.ex", + web_live, + assigns, + generate_opts + ) if opts[:interactive?] do Mix.shell().info(""" - Add the live routes to your browser scope in #{web_path()}/router.ex: + Add the live routes to your browser scope in #{web_path(igniter)}/router.ex: #{for line <- live_route_instructions(assigns), do: " #{line}"} """) end + + igniter end defp live_route_instructions(assigns) do @@ -123,7 +137,7 @@ defmodule AshPhoenix.Gen.Live do |> Enum.reject(&is_nil/1) end - defp write_formatted_template(path, destination, web_live, assigns, generate_opts) do + defp write_formatted_template(igniter, path, destination, web_live, assigns, generate_opts) do destination_path = web_live |> Path.join(destination) @@ -137,7 +151,7 @@ defmodule AshPhoenix.Gen.Live do |> EEx.eval_file(assigns: assigns) |> formatter_function.() - Mix.Generator.create_file(destination_path, contents, generate_opts) + Igniter.create_new_file(igniter, destination_path, contents, generate_opts) end defp add_resource_assigns(assigns, resource, opts) do @@ -305,30 +319,15 @@ defmodule AshPhoenix.Gen.Live do end end - defp web_path do - web_module().module_info[:compile][:source] - |> Path.relative_to(root_path()) - |> Path.rootname() - end + defp web_path(igniter) do + web_module_path = Igniter.Project.Module.proper_location(igniter, web_module(igniter)) + lib_dir = Path.dirname(web_module_path) - defp root_path do - Mix.Project.get().module_info[:compile][:source] - |> Path.dirname() + Path.join([lib_dir, Path.basename(web_module_path, ".ex")]) end - defp web_module do - base = Mix.Phoenix.base() - - cond do - Mix.Phoenix.context_app() != Mix.Phoenix.otp_app() -> - Module.concat([base]) - - String.ends_with?(base, "Web") -> - Module.concat([base]) - - true -> - Module.concat(["#{base}Web"]) - end + defp web_module(igniter) do + Igniter.Libs.Phoenix.web_module(igniter) end defp template(path) do diff --git a/lib/mix/tasks/ash_phoenix.gen.live.ex b/lib/mix/tasks/ash_phoenix.gen.live.ex index 89b3f2c..a5f22b8 100644 --- a/lib/mix/tasks/ash_phoenix.gen.live.ex +++ b/lib/mix/tasks/ash_phoenix.gen.live.ex @@ -1,29 +1,55 @@ defmodule Mix.Tasks.AshPhoenix.Gen.Live do + use Igniter.Mix.Task + + @example "mix ash_phoenix.gen.live --domain ExistingDomainName --resource ExistingResourceName --resource-plural ExistingResourceNames" + + @shortdoc "Generates liveviews for a given domain and resource." + + # --domain + # --resource + # --resource-plural @moduledoc """ + #{@shortdoc} + Generates liveviews for a given domain and resource. The domain and resource must already exist, this task does not define them. - #{AshPhoenix.Gen.docs()} - - For example: + ## Example ```bash - mix ash_phoenix.gen.live ExistingDomainName ExistingResourceName + #{@example} ``` + + ## Options + + * `--domain` - Existing domain + * `--resource` - Existing resource + * `--resourceplural` - Plural resource name """ - use Mix.Task - @shortdoc "Generates liveviews for a resource" - def run(argv) do - Mix.Task.run("compile") + def info(_argv, _composing_task) do + %Igniter.Mix.Task.Info{ + # Groups allow for overlapping arguments for tasks by the same author + # See the generators guide for more. + group: :ash_phoenix, + example: @example, + schema: [domain: :string, resource: :string, resourceplural: :string], + # Default values for the options in the `schema`. + defaults: [], + # CLI aliases + aliases: [], + # A list of options in the schema that are required + required: [:domain, :resource, :resourceplural] + } + end - if Mix.Project.umbrella?() do - Mix.raise( - "mix phx.gen.live must be invoked from within your *_web application root directory" - ) - end + def igniter(igniter, argv) do + # extract options according to `schema` and `aliases` above + options = options!(argv) - AshPhoenix.Gen.Live.generate_from_cli(argv) + # Do your work here and return an updated igniter + igniter + |> AshPhoenix.Gen.Live.generate_from_cli(options) end end diff --git a/mix.exs b/mix.exs index 9717667..6e7e75b 100644 --- a/mix.exs +++ b/mix.exs @@ -140,7 +140,9 @@ defmodule AshPhoenix.MixProject do {:dialyxir, ">= 0.0.0", only: [:dev, :test], runtime: false}, {:sobelow, ">= 0.0.0", only: [:dev, :test], runtime: false}, {:mix_audit, ">= 0.0.0", only: [:dev, :test], runtime: false}, - {:mix_test_watch, "~> 1.0", only: [:dev, :test]} + {:mix_test_watch, "~> 1.0", only: [:dev, :test]}, + # Code Generators + {:igniter, "~> 0.4 and >= 0.4.3"} ] end diff --git a/mix.lock b/mix.lock index 9ca519b..385783a 100644 --- a/mix.lock +++ b/mix.lock @@ -14,8 +14,8 @@ "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, "git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"}, "git_ops": {:hex, :git_ops, "2.6.3", "38c6e381b8281b86e2911fa39bea4eab2d171c86d7428786566891efb73b68c3", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "a81cb6c6a2a026a4d48cb9a2e1dfca203f9283a3a70aa0c7bc171970c44f23f8"}, - "glob_ex": {:hex, :glob_ex, "0.1.9", "b97a25392f5339e49f587e5b24c468c6a4f38299febd5ec85c5f8bb2e42b5c1e", [:mix], [], "hexpm", "be72e584ad1d8776a4d134d4b6da1bac8b80b515cdadf0120e0920b9978d7f01"}, - "igniter": {:hex, :igniter, "0.3.63", "ac27c466e6f779cf5f39d200a7d433cd91c8d465277e001661dc9b4680ca9eb3", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "5e24f71479cfd3575f79a767db51de0b38a633f05107b05d94ef1a54fde9093f"}, + "glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"}, + "igniter": {:hex, :igniter, "0.4.5", "cdb5f5b9ff8b6cd7764613b5eb66332fbbbd9f4b345e59e4c8d6e5270b0f982b", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:rewrite, "~> 1.0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "b858319a36b6ebd12084a20844afcb1d64c8f20bc6790dedfad9d6dec38baa3f"}, "iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, @@ -35,15 +35,16 @@ "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, "reactor": {:hex, :reactor, "0.10.0", "1206113c21ba69b889e072b2c189c05a7aced523b9c3cb8dbe2dab7062cb699a", [:mix], [{:igniter, "~> 0.2", [hex: :igniter, repo: "hexpm", optional: false]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4003c33e4c8b10b38897badea395e404d74d59a31beb30469a220f2b1ffe6457"}, - "rewrite": {:hex, :rewrite, "0.10.5", "6afadeae0b9d843b27ac6225e88e165884875e0aed333ef4ad3bf36f9c101bed", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "51cc347a4269ad3a1e7a2c4122dbac9198302b082f5615964358b4635ebf3d4f"}, + "rewrite": {:hex, :rewrite, "1.0.1", "2a249d703e47c050ad251fa43a3d019d4c08159ead95ec30ef48357ba88af609", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "79869f0bdb22840cf233b99e0dc7b6682a35d7e4747bdf2e78d3bc156b2c7c14"}, "simple_sat": {:hex, :simple_sat, "0.1.3", "f650fc3c184a5fe741868b5ac56dc77fdbb428468f6dbf1978e14d0334497578", [:mix], [], "hexpm", "a54305066a356b7194dc81db2a89232bacdc0b3edaef68ed9aba28dcbc34887b"}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, - "sourceror": {:hex, :sourceror, "1.6.0", "9907884e1449a4bd7dbaabe95088ed4d9a09c3c791fb0103964e6316bc9448a7", [:mix], [], "hexpm", "e90aef8c82dacf32c89c8ef83d1416fc343cd3e5556773eeffd2c1e3f991f699"}, + "sourceror": {:hex, :sourceror, "1.7.1", "599d78f4cc2be7d55c9c4fd0a8d772fd0478e3a50e726697c20d13d02aa056d4", [:mix], [], "hexpm", "cd6f268fe29fa00afbc535e215158680a0662b357dc784646d7dff28ac65a0fc"}, "spark": {:hex, :spark, "2.2.34", "1f0a3bd86d37f86a1d26db4a34d6b0e5fb091940aee25cd40041dab1397c8ada", [:mix], [{:igniter, ">= 0.3.36 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "93f94b8f511a72f8764465ea32ff2e5376695f70e747884de2ce64bb6ac22a59"}, "spitfire": {:hex, :spitfire, "0.1.3", "7ea0f544005dfbe48e615ed90250c9a271bfe126914012023fd5e4b6b82b7ec7", [:mix], [], "hexpm", "d53b5107bcff526a05c5bb54c95e77b36834550affd5830c9f58760e8c543657"}, "splode": {:hex, :splode, "0.2.4", "71046334c39605095ca4bed5d008372e56454060997da14f9868534c17b84b53", [:mix], [], "hexpm", "ca3b95f0d8d4b482b5357954fec857abd0fa3ea509d623334c1328e7382044c2"}, "stream_data": {:hex, :stream_data, "1.1.2", "05499eaec0443349ff877aaabc6e194e82bda6799b9ce6aaa1aadac15a9fdb4d", [:mix], [], "hexpm", "129558d2c77cbc1eb2f4747acbbea79e181a5da51108457000020a906813a1a9"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, + "text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.6", "0437fe56e093fd4ac422de33bf8fc89f7bc1416a3f2d732d8b2c8fd54792fe60", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "e04378d26b0af627817ae84c92083b7e97aca3121196679b73c73b99d0d133ea"}, "yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"}, diff --git a/test/mix/tasks/ash_phoenix.gen.live_test.exs b/test/mix/tasks/ash_phoenix.gen.live_test.exs new file mode 100644 index 0000000..d3f30fa --- /dev/null +++ b/test/mix/tasks/ash_phoenix.gen.live_test.exs @@ -0,0 +1,307 @@ +defmodule Mix.Tasks.AshPhoenix.Gen.LiveTest do + use ExUnit.Case + import Igniter.Test + + setup do + current_shell = Mix.shell() + + :ok = Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(current_shell) + end) + end + + test "generate phoenix live views from resource" do + send(self(), {:mix_shell_input, :yes?, "n"}) + send(self(), {:mix_shell_input, :prompt, ""}) + + form_path = "lib/ash_phoenix_web/live/artist_live/form_component.ex" + + form_contents = + """ + defmodule AshPhoenixWeb.ArtistLive.FormComponent do + use AshPhoenixWeb, :live_component + + @impl true + def render(assigns) do + ~H\"\"\" +
+ <.header> + <%= @title %> + <:subtitle>Use this form to manage artist records in your database. + + + <.simple_form + for={@form} + id="artist-form" + phx-target={@myself} + phx-change="validate" + phx-submit="save" + > + + + <.input field={@form[:name]} type="text" label="Name" /> + + + <:actions> + <.button phx-disable-with="Saving...">Save Artist + + +
+ \"\"\" + end + + @impl true + def update(assigns, socket) do + {:ok, + socket + |> assign(assigns) + |> assign_form()} + end + + @impl true + def handle_event("validate", %{"artist" => artist_params}, socket) do + {:noreply, assign(socket, form: AshPhoenix.Form.validate(socket.assigns.form, artist_params))} + end + + def handle_event("save", %{"artist" => artist_params}, socket) do + case AshPhoenix.Form.submit(socket.assigns.form, params: artist_params) do + {:ok, artist} -> + notify_parent({:saved, artist}) + + socket = + socket + |> put_flash(:info, "Artist \#{socket.assigns.form.source.type}d successfully") + |> push_patch(to: socket.assigns.patch) + + {:noreply, socket} + + {:error, form} -> + {:noreply, assign(socket, form: form)} + end + end + + defp notify_parent(msg), do: send(self(), {__MODULE__, msg}) + + defp assign_form(%{assigns: %{artist: artist}} = socket) do + form = + if artist do + AshPhoenix.Form.for_update(artist, :update, + as: "artist", + actor: socket.assigns.current_user + ) + else + AshPhoenix.Form.for_create(AshPhoenix.Test.Artist, :create, + as: "artist", + actor: socket.assigns.current_user + ) + end + + assign(socket, form: to_form(form)) + end + end + """ + |> format_contents(form_path) + + index_path = "lib/ash_phoenix_web/live/artist_live/index.ex" + + index_contents = + """ + defmodule AshPhoenixWeb.ArtistLive.Index do + use AshPhoenixWeb, :live_view + + @impl true + def render(assigns) do + ~H\"\"\" + <.header> + Listing Artists + <:actions> + <.link patch={~p"/Artists/new"}> + <.button>New Artist + + + + + <.table + id="Artists" + rows={@streams.Artists} + row_click={fn {_id, artist} -> JS.navigate(~p"/Artists/\#{artist}") end} + > + <:col :let={{_id, artist}} label="Id"><%= artist.id %> + + <:col :let={{_id, artist}} label="Name"><%= artist.name %> + + <:action :let={{_id, artist}}> +
+ <.link navigate={~p"/Artists/\#{artist}"}>Show +
+ + <.link patch={~p"/Artists/\#{artist}/edit"}>Edit + + + <:action :let={{id, artist}}> + <.link + phx-click={JS.push("delete", value: %{id: artist.id}) |> hide("#\#{id}")} + data-confirm="Are you sure?" + > + Delete + + + + + <.modal + :if={@live_action in [:new, :edit]} + id="artist-modal" + show + on_cancel={JS.patch(~p"/Artists")} + > + <.live_component + module={AshPhoenixWeb.ArtistLive.FormComponent} + id={(@artist && @artist.id) || :new} + title={@page_title} + current_user={@current_user} + action={@live_action} + artist={@artist} + patch={~p"/Artists"} + /> + + \"\"\" + end + + @impl true + def mount(_params, _session, socket) do + {:ok, + socket + |> stream(:Artists, Ash.read!(AshPhoenix.Test.Artist, actor: socket.assigns[:current_user])) + |> assign_new(:current_user, fn -> nil end)} + end + + @impl true + def handle_params(params, _url, socket) do + {:noreply, apply_action(socket, socket.assigns.live_action, params)} + end + + defp apply_action(socket, :edit, %{"id" => id}) do + socket + |> assign(:page_title, "Edit Artist") + |> assign(:artist, Ash.get!(AshPhoenix.Test.Artist, id, actor: socket.assigns.current_user)) + end + + defp apply_action(socket, :new, _params) do + socket + |> assign(:page_title, "New Artist") + |> assign(:artist, nil) + end + + defp apply_action(socket, :index, _params) do + socket + |> assign(:page_title, "Listing Artists") + |> assign(:artist, nil) + end + + @impl true + def handle_info({AshPhoenixWeb.ArtistLive.FormComponent, {:saved, artist}}, socket) do + {:noreply, stream_insert(socket, :Artists, artist)} + end + + @impl true + def handle_event("delete", %{"id" => id}, socket) do + artist = Ash.get!(AshPhoenix.Test.Artist, id, actor: socket.assigns.current_user) + Ash.destroy!(artist, actor: socket.assigns.current_user) + + {:noreply, stream_delete(socket, :Artists, artist)} + end + end + """ + |> format_contents(index_path) + + show_path = "lib/ash_phoenix_web/live/artist_live/show.ex" + show_contents = """ + defmodule AshPhoenixWeb.ArtistLive.Show do + use AshPhoenixWeb, :live_view + + @impl true + def render(assigns) do + ~H\"\"\" + <.header> + Artist <%= @artist.id %> + <:subtitle>This is a artist record from your database. + + <:actions> + <.link patch={~p"/Artists/\#{@artist}/show/edit"} phx-click={JS.push_focus()}> + <.button>Edit artist + + + + + <.list> + <:item title="Id"><%= @artist.id %> + + <:item title="Name"><%= @artist.name %> + + + <.back navigate={~p"/Artists"}>Back to Artists + + <.modal + :if={@live_action == :edit} + id="artist-modal" + show + on_cancel={JS.patch(~p"/Artists/\#{@artist}")} + > + <.live_component + module={AshPhoenixWeb.ArtistLive.FormComponent} + id={@artist.id} + title={@page_title} + action={@live_action} + current_user={@current_user} + artist={@artist} + patch={~p"/Artists/\#{@artist}"} + /> + + \"\"\" + end + + @impl true + def mount(_params, _session, socket) do + {:ok, socket} + end + + @impl true + def handle_params(%{"id" => id}, _, socket) do + {:noreply, + socket + |> assign(:page_title, page_title(socket.assigns.live_action)) + |> assign(:artist, Ash.get!(AshPhoenix.Test.Artist, id, actor: socket.assigns.current_user))} + end + + defp page_title(:show), do: "Show Artist" + defp page_title(:edit), do: "Edit Artist" + end + """ + |> format_contents(show_path) + + assert Igniter.new() + |> Igniter.compose_task("ash_phoenix.gen.live", [ + "--domain", + "Elixir.AshPhoenix.Test.Domain", + "--resource", + "Elixir.AshPhoenix.Test.Artist", + "--resourceplural", + "Artists" + ]) + |> assert_creates( + form_path, + form_contents + ) + |> assert_creates(index_path, index_contents) + |> assert_creates(show_path, show_contents) + end + + defp format_contents(contents, path) do + {formatter_function, _options} = + Mix.Tasks.Format.formatter_for_file(path) + + formatter_function.(contents) + end +end