Reading Time: 4 mins
While writing a custom Mix task in Elixir, I ran into minor challenges for running / executing an Ecto query to update a database table column from within my mix task. It was not straightforward initially thus I thought to share here. In this short article, we will also see how to pass arguments to our mix task to run dynamic query.
First, what is Mix
? Elixir documentation says:
Mix is a build tool that ships with Elixir that provides tasks for creating, compiling, testing your application, managing its dependencies and much more.
Mix build tool is like rake
Ruby utility and make
Unix utility but with ecosystem specific differences.
Its pretty common need to extend our Elixir application by adding our own custom Mix tasks. We already use Mix tasks by default in our project when we run mix phoenix.new my_application
to create new web application. This mix task has a recipe to generate entire project structure for our application along with necessary packages and utility.
To create our own custom mix task, we just need to add a new file in folder lib/mix/tasks
. Say for example, we will create a mix task as lib/mix/tasks/set_all_products_to_active_state.ex
to set all products to active state.
# run as => mix UpdateAllProducts.SetActive
defmodule Mix.Tasks.UpdateAllProducts.SetActive do
use Mix.Task
alias Store.{Repo, Product}
@shortdoc "Sets all products to active state"
def run(_) do
[:postgrex, :ecto]
|> Enum.each(&Application.ensure_all_started/1)
Repo.start_link
Repo.update_all Product, set: [is_active: true]
IO.puts "Task completed successfully."
end
end
In snippet alias Store.{Repo, Product}
above .... Store
is our project name and Product
is one of the model in our project. To run Ecto queries with postgres
from within this mix task, we just needed to include following 3 lines.
[:postgrex, :ecto]
|> Enum.each(&Application.ensure_all_started/1)
Repo.start_link
This mix task will set all products to active state, i.e. it will set is_active
field to true
for all products (in our products table).
To run this mix task, we just need to execute this command mix UpdateAllProducts.SetActive
. Run command mix help
now and you'll notice that our mix task is also in the list of available tasks with short document we added.
We can also pass true
or false
as argument to our mix task as follows
# run => mix UpdateAllProducts.SetState true
# run => mix UpdateAllProducts.SetState false
defmodule Mix.Tasks.UpdateAllProducts.SetState do
use Mix.Task
alias Store.{Repo, Product}
@shortdoc "Sets all products to active or inactive state based on argument value."
def run(args) do
state = Enum.at(args, 0) |> String.to_existing_atom
[:postgrex, :ecto]
|> Enum.each(&Application.ensure_all_started/1)
Repo.start_link
Repo.update_all Product, set: [is_active: state]
IO.puts "Task completed successfully."
end
end
To run this mix task with argument i.e. state as true
, we need to execute this command mix UpdateAllProducts.SetState true
Try this on your terminal mix help new
and you'll notice nice documentation by mix help for new
command.
Reference & related help on mix tasks:
https://stackoverflow.com/questions/38225406/how-to-get-data-from-ecto-in-a-custom-mix-task
http://nhu313.github.io/elixir/2015/03/22/elixir-mix-task.html