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 b05b99b..d6253e8 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/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..1bd81b8 --- /dev/null +++ b/test/mix/tasks/ash_phoenix.gen.live_test.exs @@ -0,0 +1,309 @@ +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\"\"\" +