From 3e3f4480faa838b45b5759aa8c38627524b555bb Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 8 Jan 2020 05:44:39 +0100 Subject: [PATCH] Ensure optional Phoenix is optional (#39) --- lib/mix/tasks/temple.gen.html.ex | 386 +++++++++++++++-------------- lib/mix/tasks/temple.gen.layout.ex | 46 ++-- 2 files changed, 221 insertions(+), 211 deletions(-) diff --git a/lib/mix/tasks/temple.gen.html.ex b/lib/mix/tasks/temple.gen.html.ex index 988c364..9576c2b 100644 --- a/lib/mix/tasks/temple.gen.html.ex +++ b/lib/mix/tasks/temple.gen.html.ex @@ -1,255 +1,263 @@ -defmodule Mix.Tasks.Temple.Gen.Html do - @shortdoc "Generates controller, views, and context for an HTML resource in Temple" +if Code.ensure_loaded?(Mix.Phoenix) do + defmodule Mix.Tasks.Temple.Gen.Html do + @shortdoc "Generates controller, views, and context for an HTML resource in Temple" - @moduledoc """ - Generates controller, views, and context for an HTML resource in Temple. + @moduledoc """ + Generates controller, views, and context for an HTML resource in Temple. - mix temple.gen.html Accounts User users name:string age:integer + mix temple.gen.html Accounts User users name:string age:integer - The first argument is the context module followed by the schema module - and its plural name (used as the schema table name). + The first argument is the context module followed by the schema module + and its plural name (used as the schema table name). - The context is an Elixir module that serves as an API boundary for - the given resource. A context often holds many related resources. - Therefore, if the context already exists, it will be augmented with - functions for the given resource. + The context is an Elixir module that serves as an API boundary for + the given resource. A context often holds many related resources. + Therefore, if the context already exists, it will be augmented with + functions for the given resource. - > Note: A resource may also be split - > over distinct contexts (such as `Accounts.User` and `Payments.User`). + > Note: A resource may also be split + > over distinct contexts (such as `Accounts.User` and `Payments.User`). - The schema is responsible for mapping the database fields into an - Elixir struct. + The schema is responsible for mapping the database fields into an + Elixir struct. - Overall, this generator will add the following files to `lib/`: + Overall, this generator will add the following files to `lib/`: - * a context module in `lib/app/accounts.ex` for the accounts API - * a schema in `lib/app/accounts/user.ex`, with an `users` table - * a view in `lib/app_web/views/user_view.ex` - * a controller in `lib/app_web/controllers/user_controller.ex` - * default CRUD templates in `lib/app_web/templates/user` + * a context module in `lib/app/accounts.ex` for the accounts API + * a schema in `lib/app/accounts/user.ex`, with an `users` table + * a view in `lib/app_web/views/user_view.ex` + * a controller in `lib/app_web/controllers/user_controller.ex` + * default CRUD templates in `lib/app_web/templates/user` - A migration file for the repository and test files for the context and - controller features will also be generated. + A migration file for the repository and test files for the context and + controller features will also be generated. - The location of the web files (controllers, views, templates, etc) in an - umbrella application will vary based on the `:context_app` config located - in your applications `:generators` configuration. When set, the Phoenix - generators will generate web files directly in your lib and test folders - since the application is assumed to be isolated to web specific functionality. - If `:context_app` is not set, the generators will place web related lib - and test files in a `web/` directory since the application is assumed - to be handling both web and domain specific functionality. - Example configuration: + The location of the web files (controllers, views, templates, etc) in an + umbrella application will vary based on the `:context_app` config located + in your applications `:generators` configuration. When set, the Phoenix + generators will generate web files directly in your lib and test folders + since the application is assumed to be isolated to web specific functionality. + If `:context_app` is not set, the generators will place web related lib + and test files in a `web/` directory since the application is assumed + to be handling both web and domain specific functionality. + Example configuration: - config :my_app_web, :generators, context_app: :my_app + config :my_app_web, :generators, context_app: :my_app - Alternatively, the `--context-app` option may be supplied to the generator: + Alternatively, the `--context-app` option may be supplied to the generator: - mix phx.gen.html Sales User users --context-app warehouse + mix phx.gen.html Sales User users --context-app warehouse - ## Web namespace + ## Web namespace - By default, the controller and view will be namespaced by the schema name. - You can customize the web module namespace by passing the `--web` flag with a - module name, for example: + By default, the controller and view will be namespaced by the schema name. + You can customize the web module namespace by passing the `--web` flag with a + module name, for example: - mix phx.gen.html.temple Sales User users --web Sales + mix phx.gen.html.temple Sales User users --web Sales - Which would generate a `lib/app_web/controllers/sales/user_controller.ex` and - `lib/app_web/views/sales/user_view.ex`. + Which would generate a `lib/app_web/controllers/sales/user_controller.ex` and + `lib/app_web/views/sales/user_view.ex`. - ## Generating without a schema or context file + ## Generating without a schema or context file - In some cases, you may wish to bootstrap HTML templates, controllers, and - controller tests, but leave internal implementation of the context or schema - to yourself. You can use the `--no-context` and `--no-schema` flags for - file generation control. + In some cases, you may wish to bootstrap HTML templates, controllers, and + controller tests, but leave internal implementation of the context or schema + to yourself. You can use the `--no-context` and `--no-schema` flags for + file generation control. - ## table + ## table - By default, the table name for the migration and schema will be - the plural name provided for the resource. To customize this value, - a `--table` option may be provided. For example: + By default, the table name for the migration and schema will be + the plural name provided for the resource. To customize this value, + a `--table` option may be provided. For example: - mix phx.gen.html.temple Accounts User users --table cms_users + mix phx.gen.html.temple Accounts User users --table cms_users - ## binary_id + ## binary_id - Generated migration can use `binary_id` for schema's primary key - and its references with option `--binary-id`. + Generated migration can use `binary_id` for schema's primary key + and its references with option `--binary-id`. - ## Default options + ## Default options - This generator uses default options provided in the `:generators` - configuration of your application. These are the defaults: + This generator uses default options provided in the `:generators` + configuration of your application. These are the defaults: - config :your_app, :generators, - migration: true, - binary_id: false, - sample_binary_id: "11111111-1111-1111-1111-111111111111" + config :your_app, :generators, + migration: true, + binary_id: false, + sample_binary_id: "11111111-1111-1111-1111-111111111111" - You can override those options per invocation by providing corresponding - switches, e.g. `--no-binary-id` to use normal ids despite the default - configuration or `--migration` to force generation of the migration. + You can override those options per invocation by providing corresponding + switches, e.g. `--no-binary-id` to use normal ids despite the default + configuration or `--migration` to force generation of the migration. - Read the documentation for `phx.gen.schema` for more information on - attributes. - """ - use Mix.Task + Read the documentation for `phx.gen.schema` for more information on + attributes. + """ + use Mix.Task - alias Mix.Phoenix.{Context, Schema} - alias Mix.Tasks.Phx.Gen + alias Mix.Phoenix.{Context, Schema} + alias Mix.Tasks.Phx.Gen - @doc false - def run(args) do - if Mix.Project.umbrella?() do - Mix.raise("mix temple.gen.html can only be run inside an application directory") + @doc false + def run(args) do + if Mix.Project.umbrella?() do + Mix.raise("mix temple.gen.html can only be run inside an application directory") + end + + {context, schema} = Gen.Context.build(args) + Gen.Context.prompt_for_code_injection(context) + + binding = [context: context, schema: schema, inputs: inputs(schema)] + paths = [".", :temple] + + prompt_for_conflicts(context) + + context + |> copy_new_files(paths, binding) + |> print_shell_instructions() end - {context, schema} = Gen.Context.build(args) - Gen.Context.prompt_for_code_injection(context) + defp prompt_for_conflicts(context) do + context + |> files_to_be_generated() + |> Kernel.++(context_files(context)) + |> Mix.Phoenix.prompt_for_conflicts() + end - binding = [context: context, schema: schema, inputs: inputs(schema)] - paths = [".", :temple] + defp context_files(%Context{generate?: true} = context) do + Gen.Context.files_to_be_generated(context) + end - prompt_for_conflicts(context) + defp context_files(%Context{generate?: false}) do + [] + end - context - |> copy_new_files(paths, binding) - |> print_shell_instructions() - end + @doc false + def files_to_be_generated(%Context{schema: schema, context_app: context_app}) do + web_prefix = Mix.Phoenix.web_path(context_app) + test_prefix = Mix.Phoenix.web_test_path(context_app) + web_path = to_string(schema.web_path) - defp prompt_for_conflicts(context) do - context - |> files_to_be_generated() - |> Kernel.++(context_files(context)) - |> Mix.Phoenix.prompt_for_conflicts() - end + [ + {:eex, "controller.ex", + Path.join([web_prefix, "controllers", web_path, "#{schema.singular}_controller.ex"])}, + {:eex, "edit.html.exs", + Path.join([web_prefix, "templates", web_path, schema.singular, "edit.html.exs"])}, + {:eex, "form.html.exs", + Path.join([web_prefix, "templates", web_path, schema.singular, "form.html.exs"])}, + {:eex, "index.html.exs", + Path.join([web_prefix, "templates", web_path, schema.singular, "index.html.exs"])}, + {:eex, "new.html.exs", + Path.join([web_prefix, "templates", web_path, schema.singular, "new.html.exs"])}, + {:eex, "show.html.exs", + Path.join([web_prefix, "templates", web_path, schema.singular, "show.html.exs"])}, + {:eex, "view.ex", + Path.join([web_prefix, "views", web_path, "#{schema.singular}_view.ex"])}, + {:eex, "controller_test.exs", + Path.join([ + test_prefix, + "controllers", + web_path, + "#{schema.singular}_controller_test.exs" + ])} + ] + end - defp context_files(%Context{generate?: true} = context) do - Gen.Context.files_to_be_generated(context) - end + @doc false + def copy_new_files(%Context{} = context, paths, binding) do + files = files_to_be_generated(context) - defp context_files(%Context{generate?: false}) do - [] - end + Mix.Phoenix.copy_from(paths, "priv/templates/temple.gen.html", binding, files) - @doc false - def files_to_be_generated(%Context{schema: schema, context_app: context_app}) do - web_prefix = Mix.Phoenix.web_path(context_app) - test_prefix = Mix.Phoenix.web_test_path(context_app) - web_path = to_string(schema.web_path) + if context.generate?, + do: Gen.Context.copy_new_files(context, Mix.Phoenix.generator_paths(), binding) - [ - {:eex, "controller.ex", - Path.join([web_prefix, "controllers", web_path, "#{schema.singular}_controller.ex"])}, - {:eex, "edit.html.exs", - Path.join([web_prefix, "templates", web_path, schema.singular, "edit.html.exs"])}, - {:eex, "form.html.exs", - Path.join([web_prefix, "templates", web_path, schema.singular, "form.html.exs"])}, - {:eex, "index.html.exs", - Path.join([web_prefix, "templates", web_path, schema.singular, "index.html.exs"])}, - {:eex, "new.html.exs", - Path.join([web_prefix, "templates", web_path, schema.singular, "new.html.exs"])}, - {:eex, "show.html.exs", - Path.join([web_prefix, "templates", web_path, schema.singular, "show.html.exs"])}, - {:eex, "view.ex", Path.join([web_prefix, "views", web_path, "#{schema.singular}_view.ex"])}, - {:eex, "controller_test.exs", - Path.join([test_prefix, "controllers", web_path, "#{schema.singular}_controller_test.exs"])} - ] - end + context + end - @doc false - def copy_new_files(%Context{} = context, paths, binding) do - files = files_to_be_generated(context) + @doc false + def print_shell_instructions(%Context{schema: schema, context_app: ctx_app} = context) do + if schema.web_namespace do + Mix.shell().info(""" - Mix.Phoenix.copy_from(paths, "priv/templates/temple.gen.html", binding, files) + Add the resource to your #{schema.web_namespace} :browser scope in #{ + Mix.Phoenix.web_path(ctx_app) + }/router.ex: - if context.generate?, - do: Gen.Context.copy_new_files(context, Mix.Phoenix.generator_paths(), binding) + scope "/#{schema.web_path}", #{ + inspect(Module.concat(context.web_module, schema.web_namespace)) + }, as: :#{schema.web_path} do + pipe_through :browser + ... + resources "/#{schema.plural}", #{inspect(schema.alias)}Controller + end + """) + else + Mix.shell().info(""" - context - end + Add the resource to your browser scope in #{Mix.Phoenix.web_path(ctx_app)}/router.ex: - @doc false - def print_shell_instructions(%Context{schema: schema, context_app: ctx_app} = context) do - if schema.web_namespace do - Mix.shell().info(""" - - Add the resource to your #{schema.web_namespace} :browser scope in #{ - Mix.Phoenix.web_path(ctx_app) - }/router.ex: - - scope "/#{schema.web_path}", #{ - inspect(Module.concat(context.web_module, schema.web_namespace)) - }, as: :#{schema.web_path} do - pipe_through :browser - ... resources "/#{schema.plural}", #{inspect(schema.alias)}Controller - end - """) - else - Mix.shell().info(""" + """) + end - Add the resource to your browser scope in #{Mix.Phoenix.web_path(ctx_app)}/router.ex: - - resources "/#{schema.plural}", #{inspect(schema.alias)}Controller - """) + if context.generate?, do: Gen.Context.print_shell_instructions(context) end - if context.generate?, do: Gen.Context.print_shell_instructions(context) - end + defp inputs(%Schema{} = schema) do + Enum.map(schema.attrs, fn + {_, {:references, _}} -> + {nil, nil, nil} - defp inputs(%Schema{} = schema) do - Enum.map(schema.attrs, fn - {_, {:references, _}} -> - {nil, nil, nil} + {key, :integer} -> + {label(key), ~s(number_input form, #{inspect(key)}), error(key)} - {key, :integer} -> - {label(key), ~s(number_input form, #{inspect(key)}), error(key)} + {key, :float} -> + {label(key), ~s(number_input form, #{inspect(key)}, step: "any"), error(key)} - {key, :float} -> - {label(key), ~s(number_input form, #{inspect(key)}, step: "any"), error(key)} + {key, :decimal} -> + {label(key), ~s(number_input form, #{inspect(key)}, step: "any"), error(key)} - {key, :decimal} -> - {label(key), ~s(number_input form, #{inspect(key)}, step: "any"), error(key)} + {key, :boolean} -> + {label(key), ~s(checkbox form, #{inspect(key)}), error(key)} - {key, :boolean} -> - {label(key), ~s(checkbox form, #{inspect(key)}), error(key)} + {key, :text} -> + {label(key), ~s(textarea form, #{inspect(key)}), error(key)} - {key, :text} -> - {label(key), ~s(textarea form, #{inspect(key)}), error(key)} + {key, :date} -> + {label(key), ~s(date_select form, #{inspect(key)}), error(key)} - {key, :date} -> - {label(key), ~s(date_select form, #{inspect(key)}), error(key)} + {key, :time} -> + {label(key), ~s(time_select form, #{inspect(key)}), error(key)} - {key, :time} -> - {label(key), ~s(time_select form, #{inspect(key)}), error(key)} + {key, :utc_datetime} -> + {label(key), ~s(datetime_select form, #{inspect(key)}), error(key)} - {key, :utc_datetime} -> - {label(key), ~s(datetime_select form, #{inspect(key)}), error(key)} + {key, :naive_datetime} -> + {label(key), ~s(datetime_select form, #{inspect(key)}), error(key)} - {key, :naive_datetime} -> - {label(key), ~s(datetime_select form, #{inspect(key)}), error(key)} + {key, {:array, :integer}} -> + {label(key), ~s(multiple_select form, #{inspect(key)}, ["1": 1, "2": 2]), error(key)} - {key, {:array, :integer}} -> - {label(key), ~s(multiple_select form, #{inspect(key)}, ["1": 1, "2": 2]), error(key)} + {key, {:array, _}} -> + {label(key), + ~s(multiple_select form, #{inspect(key)}, ["Option 1": "option1", "Option 2": "option2"]), + error(key)} - {key, {:array, _}} -> - {label(key), - ~s(multiple_select form, #{inspect(key)}, ["Option 1": "option1", "Option 2": "option2"]), - error(key)} + {key, _} -> + {label(key), ~s(text_input form, #{inspect(key)}), error(key)} + end) + end - {key, _} -> - {label(key), ~s(text_input form, #{inspect(key)}), error(key)} - end) - end + defp label(key) do + ~s(phx_label form, #{inspect(key)}) + end - defp label(key) do - ~s(phx_label form, #{inspect(key)}) - end - - defp error(field) do - ~s{partial error_tag(form, #{inspect(field)})} + defp error(field) do + ~s{partial error_tag(form, #{inspect(field)})} + end end end diff --git a/lib/mix/tasks/temple.gen.layout.ex b/lib/mix/tasks/temple.gen.layout.ex index 3646f52..4c4a0a6 100644 --- a/lib/mix/tasks/temple.gen.layout.ex +++ b/lib/mix/tasks/temple.gen.layout.ex @@ -1,29 +1,31 @@ -defmodule Mix.Tasks.Temple.Gen.Layout do - use Mix.Task +if Code.ensure_loaded?(Mix.Phoenix) do + defmodule Mix.Tasks.Temple.Gen.Layout do + use Mix.Task - @shortdoc "Generates a default Phoenix layout file in Temple" + @shortdoc "Generates a default Phoenix layout file in Temple" - @moduledoc """ - Generates a Phoenix layout file in Temple. - mix temple.gen.layout - """ - def run(_args) do - context_app = Mix.Phoenix.context_app() - web_prefix = Mix.Phoenix.web_path(context_app) - binding = [application_module: Mix.Phoenix.base()] - - Mix.Phoenix.copy_from(temple_paths(), "priv/templates/temple.gen.layout", binding, [ - {:eex, "app.html.eex", "#{web_prefix}/templates/layout/app.html.exs"} - ]) - - instructions = """ - A new #{web_prefix}/templates/layout/app.html.exs file was generated. + @moduledoc """ + Generates a Phoenix layout file in Temple. + mix temple.gen.layout """ + def run(_args) do + context_app = Mix.Phoenix.context_app() + web_prefix = Mix.Phoenix.web_path(context_app) + binding = [application_module: Mix.Phoenix.base()] - Mix.shell().info(instructions) - end + Mix.Phoenix.copy_from(temple_paths(), "priv/templates/temple.gen.layout", binding, [ + {:eex, "app.html.eex", "#{web_prefix}/templates/layout/app.html.exs"} + ]) - defp temple_paths do - [".", :temple] + instructions = """ + A new #{web_prefix}/templates/layout/app.html.exs file was generated. + """ + + Mix.shell().info(instructions) + end + + defp temple_paths do + [".", :temple] + end end end