wip: add igniter installer.
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing

This commit is contained in:
James Harton 2024-07-12 14:54:12 +12:00
parent 8c4e71614d
commit af9b20c2ca
Signed by: james
GPG key ID: 90E82DAA13F624F4
6 changed files with 276 additions and 5 deletions

View file

@ -3,6 +3,8 @@
"arities",
"Cardassia",
"recase",
"Slickback"
"Slickback",
"Sourceror",
"zipper"
]
}

View file

@ -0,0 +1,187 @@
defmodule Mix.Tasks.Smokestack.Gen.Factory do
@moduledoc """
Add a factory definition for a resource to your factory file.
Generates a factory DSL entry for all current public attributes of the
provided resource.
## Example
```bash
mix smokestack.gen.factory Helpdesk.Support.Ticket \
--variant default \
--domain Helpdesk \
--factory-module Helpdesk.Factory
```
## Options
* `--variant` - Specify a variant name to use for the factory. Optional.
Defaults to 'default'.
* `--domain` - The domain module used to access the resource. Optional. If not
specified it is inferred by removing the last segment from the resource
name.
* `--factory-module` - Specify the name of the factory module to generate.
Optional. Defaults to one generated by adding `.Factory` to your app module.
"""
@shortdoc "Generate a factory definition for a resource"
alias Ash.Resource
alias Sourceror.Zipper
use Igniter.Mix.Task
@impl Igniter.Mix.Task
def info(_argv, _parent) do
%Igniter.Mix.Task.Info{
schema: [
variant: :string,
domain: :string,
factory_module: :string
],
aliases: [
v: :variant,
d: :domain,
f: :factory_module
]
}
end
@impl Igniter.Mix.Task
def igniter(igniter, [resource | argv]) do
resource = Igniter.Code.Module.parse(resource)
options = options!(argv)
variant =
case options[:variant] do
nil -> :default
variant -> String.to_atom(variant)
end
domain =
case options[:domain] do
nil -> resource |> Module.split() |> List.delete_at(-1) |> Module.concat()
domain -> Igniter.Code.Module.parse(domain)
end
factory_module =
case options[:factory_module] do
nil -> Igniter.Code.Module.module_name("Factory")
module -> Igniter.Code.Module.parse(module)
end
igniter =
igniter
|> Igniter.compose_task("smokestack.gen.factory_module", [
inspect(factory_module),
"--ignore-if-exists"
])
with {:ok, igniter} <- remove_no_factories_comment(igniter, factory_module),
{:ok, igniter} <- add_factory(igniter, factory_module, resource, variant, domain) do
igniter
else
{:error, igniter} -> raise igniter
end
end
defp add_factory(igniter, factory_module, resource, variant, domain) do
Igniter.Code.Module.find_and_update_module(igniter, factory_module, fn zipper ->
with {:ok, zipper} <- maybe_move_past_existing_factories(zipper) do
{:ok, do_add_factory(zipper, resource, variant, domain)}
end
end)
end
defp do_add_factory(zipper, resource, :default, domain) do
Igniter.Code.Common.add_code(
zipper,
quote do
factory unquote(resource) do
# wat
end
end
)
end
defp do_add_factory(zipper, resource, variant, domain) do
Igniter.Code.Common.add_code(
zipper,
quote do
factory unquote(resource), unquote(variant) do
# wat
end
end
)
end
defp maybe_move_past_existing_factories(zipper) do
case Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, :factory, [1, 2]) do
{:ok, zipper} -> maybe_move_past_existing_factories(zipper)
:error -> {:ok, zipper}
end
end
defp remove_no_factories_comment(igniter, factory_module) do
with {:ok, {igniter, source, zipper}} <-
Igniter.Code.Module.find_module(igniter, factory_module) do
new_quoted =
zipper
|> Zipper.topmost()
|> Zipper.traverse_while(fn zipper ->
case maybe_replace_node(zipper) do
{:ok, zipper} -> {:halt, zipper}
:noop -> {:cont, zipper}
end
end)
|> Zipper.topmost()
|> Zipper.node()
new_source = Rewrite.Source.update(source, :quoted, new_quoted)
{:ok, %{igniter | rewrite: Rewrite.update!(igniter.rewrite, new_source)}}
end
end
defp maybe_replace_node(zipper) do
case Zipper.node(zipper) do
node when is_tuple(node) and is_list(elem(node, 1)) ->
case node_without_no_factories_comment(node) do
^node -> :noop
node -> {:ok, Zipper.replace(zipper, node)}
end
_ ->
:noop
end
end
defp node_without_no_factories_comment(node) do
node
|> maybe_remove_comments(:trailing_comments)
|> maybe_remove_comments(:leading_comments)
end
defp maybe_remove_comments(node, field) do
options = elem(node, 1)
comments =
options
|> Keyword.get(field, [])
|> Enum.reduce({[], nil}, fn
comment, {comments, last_change_line} when comment.line == last_change_line + 1 ->
# drop the comment line if it's subsequent to the one we already found
{comments, comment.line}
comment, {comments, nil} ->
if comment.text =~ ~r/Define your factories here:/ do
{comments, comment.line}
else
{[comment | comments], nil}
end
end)
|> elem(0)
|> Enum.reverse()
node
|> put_elem(1, Keyword.put(options, field, comments))
end
end

View file

@ -0,0 +1,63 @@
defmodule Mix.Tasks.Smokestack.Gen.FactoryModule do
@moduledoc """
Generate a starting factory module.
## Example
```bash
mix smokestack.gen.factory_module factory_module
```
## Options
* `--ignore-if-exists` - Do nothing if the module file already exists.
"""
@shortdoc "Generate a starting factory module"
use Igniter.Mix.Task
@doc false
@impl Igniter.Mix.Task
def info(_argv, _parent) do
%Igniter.Mix.Task.Info{
schema: [
ignore_if_exists: :boolean
]
}
end
def igniter(igniter, [factory_module | argv]) do
factory = Igniter.Code.Module.parse(factory_module)
factory_file = Igniter.Code.Module.proper_test_support_location(factory)
options = options!(argv)
if options[:ignore_if_exists] && Igniter.exists?(igniter, factory_file) do
igniter
else
Igniter.create_new_elixir_file(igniter, factory_file, ~s'''
defmodule #{inspect(factory)} do
@moduledoc """
Test factories generated by Smokestack.
In this file you can define factories for your Ash resources which can be
used in your tests.
As well as defining your factories, this module will contain automatic
implementations of the `c:Smokestack.insert/2`, `c:Smokestack.insert!/2`,
`c:Smokestack.params/2` and `c:Smokestack.params!/2` callbacks.
See https://hexdocs.pm/smokestack/ for more information.
"""
use Smokestack
# Define your factories here:
#
# factory MyResource do
# attribute :name, constant("Marty McFly")
# end
end
''')
end
end
end

View file

@ -0,0 +1,18 @@
defmodule Mix.Tasks.Smokestack.Install do
@moduledoc "Installs smokestack. Should be run with `mix igniter.install smokestack`."
@shortdoc @moduledoc
alias Igniter.{Code.Common, Code.Module, Mix.Task, Project.Formatter}
require Common
use Task
def igniter(igniter, _argv) do
factory = Module.module_name("Factory")
igniter
|> Formatter.import_dep(:smokestack)
|> Igniter.compose_task("smokestack.gen.factory_module", [
inspect(factory),
"--ignore-if-exists"
])
end
end

View file

@ -102,6 +102,7 @@ defmodule Smokestack.MixProject do
{:ex_doc, ">= 0.0.0", opts},
{:faker, "~> 0.18", opts},
{:git_ops, "~> 2.6", opts},
{:igniter, "~> 0.2"},
{:mix_audit, "~> 2.1", opts},
{:recase, "~> 0.8"},
{:spark, "~> 2.1"}

View file

@ -1,7 +1,7 @@
%{
"ash": {:hex, :ash, "3.1.3", "c5c65e18107247df4857951fa546e720c6e2ef0afde07ad0c2f523725a751eb2", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, "~> 0.2.12", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, ">= 0.8.1 and < 1.0.0-0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.7 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "530c04f32b2562352e48c92fab50bc837819c6cd3453c4fa9c9842b2e9d8483b"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
"credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
@ -20,7 +20,7 @@
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
"git_ops": {:hex, :git_ops, "2.6.1", "cc7799a68c26cf814d6d1a5121415b4f5bf813de200908f930b27a2f1fe9dad5", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "ce62d07e41fe993ec22c35d5edb11cf333a21ddaead6f5d9868fcb607d42039e"},
"glob_ex": {:hex, :glob_ex, "0.1.7", "eae6b6377147fb712ac45b360e6dbba00346689a87f996672fe07e97d70597b1", [:mix], [], "hexpm", "decc1c21c0c73df3c9c994412716345c1692477b9470e337f628a7e08da0da6a"},
"hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"},
"hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"},
"igniter": {:hex, :igniter, "0.2.12", "e2e8fbb15effecb433f4096edbb0754282553544c75c3130d06ca09bdaa1fb13", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:req, "~> 0.4", [hex: :req, 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", "51f3487a13441cd3e6e0d559689f8b0ba2c716834f86802e8a6760fdd1a2e579"},
"jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"},
"libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"},
@ -28,7 +28,7 @@
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
"mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
"mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"},
"mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"},
"mix_audit": {:hex, :mix_audit, "2.1.3", "c70983d5cab5dca923f9a6efe559abfb4ec3f8e87762f02bab00fa4106d17eda", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.9", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "8c3987100b23099aea2f2df0af4d296701efd031affb08d0746b2be9e35988ec"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
@ -36,7 +36,7 @@
"reactor": {:hex, :reactor, "0.8.5", "7a621e0392a5975ed97938a4ddbbc92a6a31157fbd87446bc8bc6b1a0f49e56a", [:mix], [{:igniter, "~> 0.2", [hex: :igniter, 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", "17b1976b9d333e55382dc108779078d5bbdbcd2c3d4033ea6dd52437339fe469"},
"recase": {:hex, :recase, "0.8.1", "ab98cd35857a86fa5ca99036f575241d71d77d9c2ab0c39aacf1c9b61f6f7d1d", [:mix], [], "hexpm", "9fd8d63e7e43bd9ea385b12364e305778b2bbd92537e95c4b2e26fc507d5e4c2"},
"req": {:hex, :req, "0.5.2", "70b4976e5fbefe84e5a57fd3eea49d4e9aa0ac015301275490eafeaec380f97f", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0c63539ab4c2d6ced6114d2684276cef18ac185ee00674ee9af4b1febba1f986"},
"rewrite": {:hex, :rewrite, "0.10.5", "6afadeae0b9d843b27ac6225e88e165884875e0aed333ef4ad3bf36f9c101bed", [], [{: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, "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"},
"sourceror": {:hex, :sourceror, "1.4.0", "be87319b1579191e25464005d465713079b3fd7124a3938a1e6cf4def39735a9", [:mix], [], "hexpm", "16751ca55e3895f2228938b703ad399b0b27acfe288eff6c0e629ed3e6ec0358"},
"spark": {:hex, :spark, "2.2.7", "96113e09a52a2a95fd696e06f310950132aabfacf5c7b34e0666d26ce4a7b7a7", [:mix], [{:igniter, "~> 0.2.6", [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", "e192add56a260382d4d270e1490401786f96545b86d67b466544cecb48c3f9a4"},
"spitfire": {:hex, :spitfire, "0.1.3", "7ea0f544005dfbe48e615ed90250c9a271bfe126914012023fd5e4b6b82b7ec7", [:mix], [], "hexpm", "d53b5107bcff526a05c5bb54c95e77b36834550affd5830c9f58760e8c543657"},