Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LiveResource generator #494

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions lib/mix/backpex/live_resource.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
defmodule Mix.Backpex.LiveResource do
@moduledoc false

alias Mix.Backpex.LiveResource

defstruct module: nil,
file: nil,
repo: nil,
layout: nil,
schema: nil,
pubsub: nil,
topic: nil,
event_prefix: nil,
singular_name: nil,
plural_name: nil,
fields: []

@switches [
integer: Backpex.Fields.Number,
boolean: Backpex.Fields.Boolean,
utc_datetime: Backpex.Fields.DateTime,
naive_datetime: Backpex.Fields.DateTime,
string: Backpex.Fields.Text
]
def new(schema_name, opts) do
otp_app = Mix.Phoenix.otp_app()
project_module = otp_app |> Atom.to_string() |> Phoenix.Naming.camelize()
otp_web_module = (project_module <> "_web") |> Phoenix.Naming.camelize()
module = Module.concat([otp_web_module, Phoenix.Naming.camelize(opts[:file])])
repo = opts[:repo] || Module.concat([project_module, "Repo"])
file = opts[:file] <> ".ex"
layout = {Module.concat([project_module, "Layouts"]), :admin}
pubsub = Module.concat([project_module, "PubSub"])
schema = ("Elixir." <> schema_name) |> String.to_atom()
singular = String.split(schema_name, ".") |> List.last()
plural = singular <> "s"
event_prefix = Phoenix.Naming.underscore(singular) <> "_"
topic = Phoenix.Naming.underscore(plural)
fields = fields(schema)

%LiveResource{
module: module,
file: file,
repo: repo,
layout: layout,
schema: schema,
pubsub: pubsub,
topic: topic,
event_prefix: event_prefix,
singular_name: singular,
plural_name: plural,
fields: fields
}
end

def fields(schema) do
fields = schema.__schema__(:fields) |> Enum.filter(fn field -> field != :id end)

Enum.map(fields, fn field ->
{field, field(field, schema.__schema__(:type, field))}
end)
end

defp field(field, type) when is_atom(type) do
%{module: @switches[type], label: Atom.to_string(field) |> Phoenix.Naming.camelize()}
end

defp field(field, _type) do
%{module: Backpex.Fields.Text, label: Atom.to_string(field) |> Phoenix.Naming.camelize()}
end
end
72 changes: 72 additions & 0 deletions lib/mix/tasks/bpx.gen.live.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
defmodule Mix.Tasks.Bpx.Gen.Live do
@shortdoc "Generates a Backpex.LiveResource for an existing ecto schema"

@moduledoc """
Generates a Backpex.LiveResource for an existing ecto schema.

mix bpx.gen.live Demo.User --file "user_live"

The first argument is the ecto schema module.
It takes the field names and types of this schema to generate a Backpex.LiveResource.
It has the minimum required code to display the `:index`, `:show`, `:new` and `:edit`.

The second argument defines the name for the LiveView file and its module name.
It will be copied to your projects `projectname_web/live` folder.


This generator will add the following file:

* a live_resource in `lib/app_web/live/user_live.ex`

After file generation is complete, there will be output regarding required
updates to the `lib/app_web/router.ex` file.

Add the live routes to your browser scope in lib/app_web/router.ex:

backpex_routes()

live_session :default, on_mount: [Backpex.InitAssigns] do
live_resources "/users", UserLive
end
"""

use Mix.Task

alias Mix.Backpex.LiveResource

@doc false
def run(args) do
if Mix.Project.umbrella?() do
Mix.raise("mix bpx.gen.live must be invoked from within your *_web application root directory")
end

Mix.Task.run("compile")
live_resource = build(args)

paths = [".", :backpex]

live_resource
|> copy_new_files(paths, live_resource: live_resource)
end

def build(args) do
{opts, [schema], _} = OptionParser.parse(args, strict: [file: :string])

LiveResource.new(schema, opts)
end

@doc false
defp files_to_be_generated(file) do
web_prefix = Mix.Phoenix.web_path(Mix.Phoenix.otp_app())
web_live = Path.join([web_prefix, "live", file])
[{:eex, "live_resource.ex", web_live}]
end

defp copy_new_files(%LiveResource{} = live_resource, paths, binding) do
files = files_to_be_generated(live_resource.file)

Mix.Phoenix.copy_from(paths, "priv/templates/bpx.gen.live", binding, files)

live_resource
end
end
22 changes: 22 additions & 0 deletions priv/templates/bpx.gen.live/live_resource.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule <%= inspect live_resource.module %> do
use Backpex.LiveResource,
layout: <%= inspect live_resource.layout %>,
schema: <%= live_resource.schema %>,
repo: <%= inspect live_resource.repo %>,
update_changeset: &<%= inspect live_resource.schema %>.changeset/3,
create_changeset: &<%= inspect live_resource.schema %>.changeset/3,
pubsub: <%= inspect live_resource.pubsub %>,
topic: <%= inspect live_resource.topic %>,
event_prefix: <%= inspect live_resource.event_prefix %>

@impl Backpex.LiveResource
def singular_name, do: <%= inspect live_resource.singular_name %>

@impl Backpex.LiveResource
def plural_name, do: <%= inspect live_resource.plural_name %>

@impl Backpex.LiveResource
def fields do
<%= inspect live_resource.fields %>
end
end
Loading