Current section
Files
Jump to
Current section
Files
lib/mix/tasks/phoenix_custom_generators.gen.context.ex
defmodule Mix.Tasks.PhoenixCustomGenerators.Gen.Context do
@shortdoc "Generates a context with functions around an Ecto schema"
use Mix.Task
alias Mix.PhoenixCustomGenerators.{Context, Schema}
alias Mix.Tasks.PhoenixCustomGenerators.Gen
@switches [
binary_id: :boolean,
table: :string,
web: :string,
schema: :boolean,
context: :boolean,
context_app: :string,
ex_machina_module: :string,
ex_machina_path: :string
]
@default_opts [schema: true, context: true]
@doc false
def run(args) do
if Mix.Project.umbrella? do
Mix.raise "mix phx.gen.context can only be run inside an application directory"
end
{context, schema} = build(args)
binding = [context: context, schema: schema]
paths = Mix.PhoenixCustomGenerators.generator_paths()
prompt_for_conflicts(context)
context
|> copy_new_files(paths, binding)
|> print_shell_instructions()
end
defp prompt_for_conflicts(context) do
context
|> files_to_be_generated()
|> Mix.PhoenixCustomGenerators.prompt_for_conflicts()
end
@doc false
def build(args) do
{opts, parsed, _} = parse_opts(args)
[context_name, schema_name, plural | schema_args] = validate_args!(parsed)
schema_module = inspect(Module.concat(context_name, schema_name))
schema = Gen.Schema.build([schema_module, plural | schema_args], opts, __MODULE__)
context = Context.new(context_name, schema, opts)
{context, schema}
end
defp parse_opts(args) do
{opts, parsed, invalid} = OptionParser.parse(args, switches: @switches)
merged_opts =
@default_opts
|> Keyword.merge(opts)
|> put_context_app(opts[:context_app])
{merged_opts, parsed, invalid}
end
defp put_context_app(opts, nil), do: opts
defp put_context_app(opts, string) do
Keyword.put(opts, :context_app, String.to_atom(string))
end
@doc false
def files_to_be_generated(%Context{schema: schema}) do
if schema.generate? do
Gen.Schema.files_to_be_generated(schema)
else
[]
end
end
@doc false
def copy_new_files(%Context{schema: schema} = context, paths, binding) do
if schema.generate?, do: Gen.Schema.copy_new_files(schema, paths, binding)
inject_schema_access(context, paths, binding)
inject_tests(context, paths, binding)
context
end
defp inject_schema_access(%Context{file: file} = context, paths, binding) do
unless Context.pre_existing?(context) do
Mix.Generator.create_file(file, Mix.PhoenixCustomGenerators.eval_from(paths, "priv/templates/phoenix_custom_generators.gen.context/context.ex", binding))
end
paths
|> Mix.PhoenixCustomGenerators.eval_from("priv/templates/phoenix_custom_generators.gen.context/#{schema_access_template(context)}", binding)
|> inject_eex_before_final_end(file, binding)
end
defp write_file(content, file) do
File.write!(file, content)
end
defp inject_tests(%Context{test_file: test_file} = context, paths, binding) do
unless Context.pre_existing_tests?(context) do
Mix.Generator.create_file(test_file, Mix.PhoenixCustomGenerators.eval_from(paths, "priv/templates/phoenix_custom_generators.gen.context/context_test.exs", binding))
end
paths
|> Mix.PhoenixCustomGenerators.eval_from("priv/templates/phoenix_custom_generators.gen.context/test_cases.exs", binding)
|> inject_eex_before_final_end(test_file, binding)
end
defp inject_eex_before_final_end(content_to_inject, file_path, binding) do
file = File.read!(file_path)
if String.contains?(file, content_to_inject) do
:ok
else
Mix.shell.info([:green, "* injecting ", :reset, Path.relative_to_cwd(file_path)])
file
|> String.trim_trailing()
|> String.trim_trailing("end")
|> EEx.eval_string(binding)
|> Kernel.<>(content_to_inject)
|> Kernel.<>("end\n")
|> write_file(file_path)
end
end
@doc false
def print_shell_instructions(%Context{schema: schema}) do
if schema.generate? do
Gen.Schema.print_shell_instructions(schema)
else
:ok
end
end
defp schema_access_template(%Context{schema: schema}) do
if schema.generate? do
"schema_access.ex"
else
"access_no_schema.ex"
end
end
defp validate_args!([context, schema, _plural | _] = args) do
cond do
not Context.valid?(context) ->
raise_with_help "Expected the context, #{inspect context}, to be a valid module name"
not Schema.valid?(schema) ->
raise_with_help "Expected the schema, #{inspect schema}, to be a valid module name"
context == schema ->
raise_with_help "The context and schema should have different names"
true ->
args
end
end
defp validate_args!(_) do
raise_with_help "Invalid arguments"
end
@doc false
@spec raise_with_help(String.t) :: no_return()
def raise_with_help(msg) do
Mix.raise """
#{msg}
mix phoenix_custom_generators.gen.html, phoenix_custom_generators.gen.json
and phoenix_custom_generators.gen.context expect a
context module name, followed by singular and plural names of
the generated resource, ending with any number of attributes.
For example:
mix phoenix_custom_generators.gen.html Accounts User users name:string
mix phoenix_custom_generators.gen.json Accounts User users name:string
mix phoenix_custom_generators.gen.context Accounts User users name:string
The context serves as the API boundary for the given resource.
Multiple resources may belong to a context and a resource may be
split over distinct contexts (such as Accounts.User and Payments.User).
"""
end
end