Current section
Files
Jump to
Current section
Files
lib/tasks/xtra.ex
defmodule Mix.Tasks.Xtra do
use Mix.Task
@shortdoc "Transforms templates"
@moduledoc """
## Mix task to Transform EEx templates in the context of the `Extractly` module.
This tool serves two purposes.
1. A simple CLI to basicly `EEx.eval_file/2`
1. Access to the `Extractly` module (available as binding `xtra` too)
1. Access to the name of the rendered template with the `template` binding
The `Extractly` module gives easy access to Elixir metainformation of the application using
the `extractly` package, notably, _module_ and _function_ documentation.
This is BTW the raison d'ĂȘtre of this package, simple creation of a `README.md` file with very simple
access to the projects hex documentation.
Thusly hexdoc and Github will always be synchronized.
To see that in action just look at the [`README.md.eex`](README.md.eex) file of this package and compare
with what you are reading here.
Example Template:
Some text
<%= xtra.functiondoc("M.shiny_function/2") %>
<%= xtra.moduledoc("String") %>
More text
"""
@strict [
help: :boolean,
output: :string,
quiet: :boolean,
verbose: :boolean,
version: :boolean,
]
@aliases [
h: :help,
q: :quiet,
v: :version,
V: :verbose
]
@impl true
def run(args) do
Extractly.Messages.Agent.start_link
OptionParser.parse(args, strict: @strict, aliases: @aliases)
|> _mappify_options()
|> _run()
|> _output()
end
defp _mappify_options({options, args, errors}),
do: {options |> Enum.into(%{}), args, errors}
# run returns the options unless an error occurred
defp _output(opts_or_error)
defp _output(opts) when is_map(opts) do
opts
|> _severity()
|> Extractly.Messages.messages
|> Enum.each(&_output_message/1)
end
defp _output(_), do: nil
defp _output_message({status, message}), do: IO.puts(:stderr, "*#{status}* -- #{message}")
@help_text """
mix xtra
convert Eex template file to output with a shiny CLI and __Extra__ context from the `Extractly` module
For more detailed information use
mix xtra.help
"""
defp _process(options, template)
defp _process(%{help: true}, _), do: IO.puts(@help_text)
defp _process(%{version: true}, _), do: IO.puts(Extractly.version)
defp _process(options, template), do:
if File.exists?(template),
do: _process_template(options, template),
else: _puts_err("Template #{template} does not exist", options)
defp _process_template(options, template) do
try do
Mix.Task.run("compile")
rescue
UndefinedFunctionError -> nil
end
output_fn = Map.get(options, :output, String.replace(template, ~r{\.eex\z}, ""))
output = EEx.eval_file(template, [xtra: Extractly.Xtra, template: template, options: options])
case File.write(output_fn, output) do
:ok -> options
{:error, posix_reason} = x -> _puts_err("Cannot write to #{output_fn}, reason: #{:file.format_error(posix_reason)}", options)
x
end
end
defp _run(parsed)
defp _run({options, [], []}), do: _process(options, "README.md.eex")
defp _run({options, [template | args], []}) do
unless Enum.empty?(args) do
_puts_err("WARNING: Spourious templates #{inspect args} are ignored", options)
end
_process(options, template)
end
defp _run({options, _, errors}), do:
_puts_err("ERROR: Illegal arguments: #{inspect(errors)}\n\nTry `mix xtra.help` for, well, some help", options)
defp _puts_err(message, options)
defp _puts_err(_, %{quiet: true}), do: nil
defp _puts_err(message, _), do: IO.puts(:stderr, message)
defp _severity(opts)
defp _severity(%{verbose: true}), do: :debug
defp _severity(%{quiet: true}), do: :error
defp _severity(_), do: :info
end