Current section

Files

Jump to
phoenix_custom_generators lib mix tasks phoenix_custom_generators.gen.schema.ex
Raw

lib/mix/tasks/phoenix_custom_generators.gen.schema.ex

defmodule Mix.Tasks.PhoenixCustomGenerators.Gen.Schema do
@shortdoc "Generates an Ecto schema and migration file"
use Mix.Task
alias Mix.PhoenixCustomGenerators.Schema
@switches [migration: :boolean, binary_id: :boolean, table: :string,
web: :string, ex_machina_module: :string, ex_machina_path: :string]
@doc false
def run(args) do
if Mix.Project.umbrella?() do
Mix.raise "mix phx.gen.schema can only be run inside an application directory"
end
schema = build(args, [])
paths = Mix.PhoenixCustomGenerators.generator_paths()
prompt_for_conflicts(schema)
schema
|> copy_new_files(paths, schema: schema)
|> print_shell_instructions()
end
defp prompt_for_conflicts(schema) do
schema
|> files_to_be_generated()
|> Mix.PhoenixCustomGenerators.prompt_for_conflicts()
end
@doc false
def build(args, parent_opts, help \\ __MODULE__) do
{schema_opts, parsed, _} = OptionParser.parse(args, switches: @switches)
[schema_name, plural | attrs] = validate_args!(parsed, help)
opts = Keyword.merge(parent_opts, schema_opts)
schema = Schema.new(schema_name, plural, attrs, opts)
schema
end
@doc false
def files_to_be_generated(%Schema{} = schema) do
[{:eex, "schema.ex", schema.file}]
end
@doc false
def copy_new_files(%Schema{context_app: ctx_app} = schema, paths, binding) do
files = files_to_be_generated(schema)
Mix.PhoenixCustomGenerators.copy_from(paths,"priv/templates/phoenix_custom_generators.gen.schema", binding, files)
case Keyword.fetch(schema.opts, :ex_machina_path) do
{:ok, ex_machina_path} -> inject_ex_machina_factory(paths, ex_machina_path, binding)
:error -> nil
end
if schema.migration? do
migration_path = Mix.PhoenixCustomGenerators.context_app_path(ctx_app, "priv/repo/migrations/#{timestamp()}_create_#{schema.table}.exs")
Mix.PhoenixCustomGenerators.copy_from paths, "priv/templates/phoenix_custom_generators.gen.schema", binding, [
{:eex, "migration.exs", migration_path},
]
end
schema
end
defp inject_ex_machina_factory(paths, ex_machina_path, binding) do
unless File.exists?(ex_machina_path) do
Mix.Generator.create_file(ex_machina_path, Mix.PhoenixCustomGenerators.eval_from(paths, "priv/templates/phoenix_custom_generators.gen.schema/ex_machina.ex", binding))
end
content_to_inject = Mix.PhoenixCustomGenerators.eval_from(paths, "priv/templates/phoenix_custom_generators.gen.schema/ex_machina_factory.ex", binding)
file = File.read!(ex_machina_path)
if String.contains?(file, content_to_inject) do
:ok
else
Mix.shell.info([:green, "* injecting ", :reset, Path.relative_to_cwd(ex_machina_path)])
file
|> String.trim_trailing()
|> String.trim_trailing("end")
|> EEx.eval_string(binding)
|> Kernel.<>(content_to_inject)
|> Kernel.<>("end\n")
|> write_file(ex_machina_path)
end
end
defp write_file(content, file) do
File.write!(file, content)
end
@doc false
def print_shell_instructions(%Schema{} = schema) do
if schema.migration? do
Mix.shell.info """
Remember to update your repository by running migrations:
$ mix ecto.migrate
"""
end
end
@doc false
def validate_args!([schema, plural | _] = args, help) do
cond do
not Schema.valid?(schema) ->
help.raise_with_help "Expected the schema argument, #{inspect schema}, to be a valid module name"
String.contains?(plural, ":") or plural != Phoenix.Naming.underscore(plural) ->
help.raise_with_help "Expected the plural argument, #{inspect plural}, to be all lowercase using snake_case convention"
true ->
args
end
end
def validate_args!(_, help) do
help.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 phx.gen.schema expects both a module name and
the plural of the generated resource followed by
any number of attributes:
mix phx.gen.schema Blog.Post blog_posts title:string
"""
end
defp timestamp do
{{y, m, d}, {hh, mm, ss}} = :calendar.universal_time()
"#{y}#{pad(m)}#{pad(d)}#{pad(hh)}#{pad(mm)}#{pad(ss)}"
end
defp pad(i) when i < 10, do: << ?0, ?0 + i >>
defp pad(i), do: to_string(i)
end