Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions documentation/writing-installers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Writing Installers

Generate the install task:
```
mix igniter.gen.task my_dep.install
```
All of the work will be done in the igniter call:

```elixir
@impl Igniter.Mix.Task
def igniter(igniter) do
# Do your work here and return an updated igniter
igniter
|> Igniter.add_warning("mix routex.install is not yet implemented")
end
```

## Fetching Dependency

```elixir
if Igniter.Project.Deps.has_dep?(igniter, :my_dep) do
igniter
else
igniter
|> Igniter.Project.Deps.add_dep({:my_dep, "~> 1.0"})
|> Igniter.apply_and_fetch_dependencies(error_on_abort?: true, yes_to_deps: true)
end
```

Check does the dependency already exist and if not add it and apply it.


## Creating a module

```elixir
Igniter.Project.Module.create_module(igniter, module, """
use MyDep.Module
""")
```

Create a new module that uses some module from your application.

## Modifying a module

For example:

```elixir
defmodule SomeModule do
alias Some.Example

def some_function(a, b) do
Example.function(a, b)
end
end
```

Find that module and update it:

```elixir
Igniter.Project.Module.find_and_update_module!(igniter, SomeModule, fn zipper ->
{:ok, igniter}
end)
```

In the function block use [`within/2`](https://hexdocs.pm/igniter/Igniter.Code.Common.html#within/2) to do multiple modifications. Find the part you want to change and modify it.

For example:
- replace alias with an import:
```elixir
Igniter.Code.Common.within(fn zipper ->
pred = &match?(%Zipper{node: {:alias, _, _}}, &1)
zipper = Common.remove(zipper, pred)
line = "import Some.Example, only: [:function]"
{:ok, Common.add_code(zipper, line, placement: :before)}
end)
```

- replace function block:
```elixir
Igniter.Code.Common.within(fn zipper ->
{:ok, zipper} = move_to_function(zipper, :some_function)
{:ok, zipper} = Common.move_to_do_block(zipper)
line = "my_private_function!(function(a, b))"
{:ok, Igniter.Code.Common.replace_code(zipper, line)}
end)
```

- add a block:
```elixir
Igniter.Code.Common.within(fn zipper ->
block = """
defp private_function!({:ok, result}), do: result
defp private_function!(_), do: raise "Something went wrong!"
"""

{:ok, Common.add_code(zipper, block, placement: :after)}
end)
```

You can chain within blocks using `with`.
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ defmodule Igniter.MixProject do
extras: [
{"README.md", title: "Home"},
"documentation/writing-generators.md",
"documentation/writing-installers.md",
"documentation/configuring-igniter.md",
"documentation/upgrades.md",
"CHANGELOG.md"
Expand Down
82 changes: 82 additions & 0 deletions test/igniter/code/within_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
defmodule Igniter.Code.WithinTest do
alias Igniter.Code.Common
alias Sourceror.Zipper
use ExUnit.Case
import Igniter.Test

describe "with within" do
test "performs multiple changes to some module" do
test_project()
|> Igniter.create_new_file("lib/some_module.ex", """
defmodule SomeModule do
alias Some.Example

def some_function(a, b) do
Example.function(a, b)
end
end
""")
|> apply_igniter!()
|> Igniter.Project.Module.find_and_update_module!(SomeModule, fn zipper ->
zipper
|> within!(fn zipper ->
pred = &match?(%Zipper{node: {:alias, _, _}}, &1)
zipper = Common.remove(zipper, pred)
line = "import Some.Example, only: [:function]"
{:ok, Common.add_code(zipper, line, placement: :before)}
end)
|> within!(fn zipper ->
{:ok, zipper} = move_to_function(zipper, :some_function)
{:ok, zipper} = Common.move_to_do_block(zipper)
line = "my_private_function!(function(a, b))"
{:ok, Igniter.Code.Common.replace_code(zipper, line)}
end)
|> within!(fn zipper ->
block = """
defp private_function!({:ok, result}), do: result
defp private_function!(_), do: raise "Something went wrong!"
"""

{:ok, Common.add_code(zipper, block, placement: :after)}
end)
|> then(fn zipper -> {:ok, zipper} end)
end)
|> Igniter.Refactors.Rename.rename_function(
{SomeModule, :some_function},
{SomeModule, :some_function!},
arity: 2
)
|> assert_has_patch("lib/some_module.ex", """
|defmodule SomeModule do
- | alias Some.Example
+ | import Some.Example, only: [:function]
|
- | def some_function(a, b) do
- | Example.function(a, b)
+ | def some_function!(a, b) do
+ | my_private_function!(function(a, b))
| end
+ |
+ | defp private_function!({:ok, result}),
+ | do: result
+ |
+ | defp private_function!(_),
+ | do: raise("Something went wrong!")
|end
""")
end
end

defp within!(zipper, function) do
case Common.within(zipper, function) do
{:ok, zipper} -> zipper
:error -> raise "Error calling within"
end
end

defp move_to_function(zipper, function) do
Igniter.Code.Common.move_to(zipper, fn zipper ->
Igniter.Code.Function.function_call?(zipper, function)
end)
end
end