Packages
ash_phoenix
2.1.14
2.3.24
2.3.23
2.3.22
2.3.21
2.3.20
2.3.19
2.3.18
2.3.17
2.3.16
2.3.15
2.3.14
2.3.13
2.3.12
2.3.11
2.3.10
2.3.9
2.3.8
2.3.7
2.3.6
2.3.5
2.3.4
2.3.3
2.3.2
2.3.1
2.3.0
2.2.0
2.1.26
2.1.25
2.1.24
2.1.23
2.1.22
2.1.21
2.1.20
2.1.19
2.1.18
2.1.17
2.1.16
2.1.15
2.1.14
2.1.13
2.1.12
2.1.11
2.1.10
2.1.9
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.8
2.0.0-rc.7
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
1.3.7
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.26
1.2.25
1.2.24
1.2.23
1.2.22
1.2.21
1.2.20
1.2.19
1.2.18
1.2.17
1.2.16
1.2.15
1.2.14
1.2.13
1.2.12
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.1.0-rc.3
1.1.0-rc.2
1.1.0-rc.1
1.1.0-rc.0
1.0.0-rc.1
1.0.0-rc.0
1.0.0-pre.2
1.0.0-pre.1
1.0.0-pre.0
0.7.7
0.7.6-rc.0
0.7.5
0.7.4
0.7.3
0.7.2-rc.2
0.7.2-rc.1
0.7.2-rc.0
0.7.1
0.7.0
0.6.0-rc.7
0.6.0-rc.6
0.6.0-rc.5
0.6.0-rc.4
0.6.0-rc.3
0.6.0-rc.2
0.6.0-rc.1
0.6.0-rc.0
0.5.19-rc.2
0.5.19-rc.1
0.5.19-rc.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.24
0.4.23
0.4.23-rc.1
0.4.23-rc.0
0.4.22-rc2
0.4.22-rc1
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Utilities for integrating Ash and Phoenix
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/ash_phoenix.gen.html.ex
defmodule Mix.Tasks.AshPhoenix.Gen.Html do
use Mix.Task
@shortdoc "Generates a controller and HTML views for an existing Ash resource."
@moduledoc """
This task renders .ex and .heex templates and copies them to specified directories.
#{AshPhoenix.Gen.docs()}
mix ash_phoenix.gen.html MyApp.Shop MyApp.Shop.Product --plural-name products
"""
def run([]) do
not_umbrella!()
Mix.shell().info("""
#{Mix.Task.shortdoc(__MODULE__)}
#{Mix.Task.moduledoc(__MODULE__)}
""")
end
def run(args) do
not_umbrella!()
Mix.Task.run("compile")
{domain, resource, opts, _} = AshPhoenix.Gen.parse_opts(args)
singular = to_string(Ash.Resource.Info.short_name(resource))
opts = %{
resource: List.last(Module.split(resource)),
full_resource: resource,
full_domain: domain,
singular: singular,
plural: opts[:resource_plural]
}
if Code.ensure_loaded?(resource) do
source_path = Application.app_dir(:ash_phoenix, "priv/templates/ash_phoenix.gen.html")
resource_html_dir = to_string(opts[:singular]) <> "_html"
template_files(resource_html_dir, opts)
|> generate_files(
assigns(
[:domain, :full_resource, :full_domain, :resource, :singular, :plural],
resource,
opts
),
source_path
)
print_shell_instructions(opts)
else
Mix.shell().info(
"The resource #{inspect(opts[:domain])}.#{inspect(opts[:resource])} does not exist."
)
end
end
defp not_umbrella! do
if Mix.Project.umbrella?() do
Mix.raise(
"mix phx.gen.html must be invoked from within your *_web application root directory"
)
end
end
defp assigns(keys, resource, opts) do
binding = Enum.map(keys, fn key -> {key, opts[key]} end)
binding = [{:route_prefix, to_string(opts[:plural])} | binding]
binding = [{:app_name, app_name()} | binding]
binding = [{:attributes, attributes(resource)} | binding]
binding = [{:update_attributes, update_attributes(resource)} | binding]
binding = [{:create_attributes, create_attributes(resource)} | binding]
Enum.into(binding, %{})
end
defp template_files(resource_html_dir, opts) do
app_web_path = "lib/#{app_name_underscore()}_web"
%{
"index.html.heex.eex" => "#{app_web_path}/controllers/#{resource_html_dir}/index.html.heex",
"show.html.heex.eex" => "#{app_web_path}/controllers/#{resource_html_dir}/show.html.heex",
"resource_form.html.heex.eex" =>
"#{app_web_path}/controllers/#{resource_html_dir}/#{opts[:singular]}_form.html.heex",
"new.html.heex.eex" => "#{app_web_path}/controllers/#{resource_html_dir}/new.html.heex",
"edit.html.heex.eex" => "#{app_web_path}/controllers/#{resource_html_dir}/edit.html.heex",
"controller.ex.eex" => "#{app_web_path}/controllers/#{opts[:singular]}_controller.ex",
"html.ex.eex" => "#{app_web_path}/controllers/#{opts[:singular]}_html.ex"
}
end
defp generate_files(template_files, assigns, source_path) do
Enum.each(template_files, fn {source_file, dest_file} ->
Mix.Generator.create_file(
dest_file,
EEx.eval_file("#{source_path}/#{source_file}", assigns: assigns)
)
end)
end
defp app_name_underscore do
Mix.Project.config()[:app]
end
defp app_name do
app_name_atom = Mix.Project.config()[:app]
Macro.camelize(Atom.to_string(app_name_atom))
end
defp print_shell_instructions(opts) do
Mix.shell().info("""
Add the resource to your browser scope in lib/#{opts[:singular]}_web/router.ex:
resources "/#{opts[:plural]}", #{opts[:resource]}Controller
""")
end
defp attributes(resource) do
resource
|> Ash.Resource.Info.public_attributes()
|> Enum.reject(&(&1.type == Ash.Type.UUID))
|> Enum.map(&attribute_map/1)
end
defp create_attributes(resource) do
create_action = Ash.Resource.Info.primary_action!(resource, :create)
attrs =
create_action.accept
|> Enum.map(&Ash.Resource.Info.attribute(resource, &1))
|> Enum.filter(& &1.writable?)
create_action.arguments
|> Enum.concat(attrs)
|> Enum.map(&attribute_map/1)
end
defp update_attributes(resource) do
update_action = Ash.Resource.Info.primary_action!(resource, :update)
attrs =
update_action.accept
|> Enum.map(&Ash.Resource.Info.attribute(resource, &1))
|> Enum.filter(& &1.writable?)
update_action.arguments
|> Enum.concat(attrs)
|> Enum.map(&attribute_map/1)
end
defp attribute_map(attr) do
%{
name: attr.name,
type: attr.type,
writable?: Map.get(attr, :writable?, true),
public?: attr.public?
}
end
end