View Source Writing Generators
In Igniter
, generators are done as a wrapper around Mix.Task
, allowing them to be called individually or composed as part of a task.
Since an example is worth a thousand words, lets take a look at an example that generates a file and ensures a configuration is set in the user's config.exs
.
An igniter for igniters?!
Run mix igniter.gen.task your_app.task.name
to generate a new, fully configured igniter task!
# lib/mix/tasks/your_lib.gen.your_thing.ex
defmodule Mix.Tasks.YourLib.Gen.YourThing do
use Igniter.Mix.Task
@impl Igniter.Mix.Task
def igniter(igniter) do
[module_name | _] = igniter.args.argv
module_name = Igniter.Code.Module.parse(module_name)
path = Igniter.Code.Module.proper_location(module_name)
app_name = Igniter.Project.Application.app_name(igniter)
igniter
|> Igniter.create_new_elixir_file(path, """
defmodule #{inspect(module_name)} do
use YourLib.Thing
...some_code
end
""")
|> Igniter.Project.Config.configure(
"config.exs",
app_name,
[:list_of_things],
[module_name],
&Igniter.Code.List.prepend_new_to_list(&1, module_name)
)
end
end
Now, your users can run
mix your_lib.gen.your_thing MyApp.MyModuleName
and it will present them with a diff, creating a new file and updating their config.exs
.
Additionally, other generators can "compose" this generator using Igniter.compose_task/3
igniter
|> Igniter.compose_task(Mix.Tasks.YourLib.Gen.YourThing, ["MyApp.MyModuleName"])
|> Igniter.compose_task(Mix.Tasks.YourLib.Gen.YourThing, ["MyApp.SomeOtherName"])
Writing a library installer
Igniter will look for a mix task called your_library.install
when a user runs mix igniter.install your_library
. As long as it has the correct name, it will be run automatically as part of installation!
Task Groups
Igniter allows for composing tasks, which means that many igniter tasks can be run in tandem. This happens automatically when using mix igniter.install
, for example:
mix igniter.install package1 package2
. You can also do this manually by using Igniter.compose_task/3
. See the example above.
However, composing tasks means that sometimes a flag from one task may conflict with a flag from another task. Igniter will alert users when this happens, and ask them to prefix the option with your task name. For example, the user may see an error like this:
Ambiguous flag provided `--option`.
The task or task groups `package1, package2` all define the flag `--option`.
To disambiguate, provide the arg as `--<prefix>.option`,
where `<prefix>` is the task or task group name.
For example:
`--package1.option`
It is not possible to prevent this from happening for all combinations of invocations of your task, but you can help by using a group
.
%Igniter.Mix.Task.Info{
group: :your_package,
...
}
Setting this group performs two functions:
- any tasks that share a group with each other will be assumed that the same flag has the same meaning. That way,
users don't have to disambiguate when calling
mix igniter.install yourthing1 yourthing2 --option
, because it is assumed to have the same meaning. - it can provide a shorter/semantic name to type, i.e instead of
--ash-authentication-phoenix.install.domain
it could be just--ash.domain
.
By default the group name is the full task name. We suggest setting a group for all of your tasks. You should not use a group name that is used by someone else, just like you should not use a module prefix used by someone else in general.
Navigating the Igniter Codebase
A large part of writing generators with igniter is leveraging our built-in suite of tools for working with zippers and AST, as well as our off-the-shelf patchers for making project modifications. The codebase is split up into four primary divisions:
Igniter.Project.*
- project-level, off-the-shelf patchersIgniter.Code.*
- working with zippers and manipulating source codeIgniter.Mix.*
- mix tasks, tools for writing igniter mix tasksIgniter.Util.*
- various utilities and helpers