Writing Mix Task in Elixir Phoenix

Search for a command to run...

No comments yet. Be the first to comment.
Motivation Recently, I tried to learn some low-level system programming stuff. I am a Mac user, and I thought that everything that works on Linux should also work on Mac. After all, Mac is a Unix-based system 😊. I guess we all heard this. Oh boy! I ...

Problem Statement While working on a Nestjs project, I encountered a weird problem related to the database column. I was trying to insert a record into a MySQL table using TypeORM. The error I was experiencing stated that a specific column “cannot be...

Recently, I was exploring design patterns courses on my LinkedIn Learning subscription. I came across a course, Node.js: Design Patterns by Alex Banks. It is a wonderful, easy-to-understand course. I started with the Builder Pattern, and the explanat...

Suppose you are working on a table in a LiveView project. This table has limited static data of not more than one page (you can avoid questions about pagination in the comment section 😊). From a user's point of view, it becomes hard to look into the...

Recently, while working on one of my personal Elixir projects. I came across a scenario where I needed to make some changes to the database schema.The changes mainly revolve around adding and removing indexes due to changes in the business requiremen...

Imagine you are working on an application that has multiple models (Tables) and each table has some columns. Your manager came up with a requirement to add a new column in one table, say User table. The new column you have asked to add is user_type of string type. You will write the migration to add the new column and as per the new column, you added this new field in form as well. Everything works fine, but what about the records that already exist in the database on Production or another environment. That might can give unexpected results. The solution to this problem is task in Phoenix. It is somewhat inspired by the rails rake task. But the question arises we can directly run SQL queries on the database as soon as we run the migration before deploying other changes that are going to change new data. Why there was a need to come with a new concept like task to change the data.
There are few benefits of using the Elixir task:
Task code is nothing but pure Elixir code with which you can use to write Ecto Queries (which is more readable and expressive) to do changes on a database.Task you can write a Test case and be 100% sure that your query is going to work on the production.user_type column example I explained in the introduction. We will add that column to the User struct.def change do
alter table(:user) do
add :user_type, :string
end
end
task.lib/mix/tasks/add_user_type.ex.add_user_type.ex file we will add the following code. defmodule Mix.Tasks.AddUserType do
use Mix.Task
def run(_args) do
# Write your task logic here
end
end
Mix.Tasks. So, we will name our module Mix.Tasks.AddUserType. use Mix.Task macro, which adds task capability to this file.mix help in the terminal of the project. You will get the list of the tasks but you'll not find the task that we created.It is because in order to list our task with a description we need to add the @shortdoc module attribute. We will add one.
defmodule Mix.Tasks.AddUserType do
use Mix.Task
@shortdoc "Add user_type value to existing user data"
def run(_args) do
# Write your task logic here
end
end
mix compile and mix help again you can see the task we created (see the second line in the image below).
run function.run function which takes one parameter args. We are going to ignore it because we don't need one.We will alias User, Repo and import Ecto.Query to write some SQL.
defmodule Mix.Tasks.AddUserType do
use Mix.Task
@shortdoc "Add user_type value to existing user data"
alias MyProject.{ Repo, User }
import Ecto.Query
def run(_args) do
# Logic to change existing data.
from(u in User, where: is_nil(u.user_type))
|> Repo.update_all(set: [some_condition])
end
end
mix task_name and in our case it will be mix add_user_type. (RuntimeError) could not lookup MyProject.Repo because it was not started or it does not exist.Repo to Mix.EctoSQL.ensure_started function and also start the Task inside the run function. Mix.Task.run("app.start", [])
Mix.EctoSQL.ensure_started(MyProject.Repo, [])
mix add_user_typ it will run the task without any issue.Refer below complete running code
defmodule Mix.Tasks.AddUserType do
use Mix.Task
@shortdoc "Add user_type value to existing user data"
alias MyProject.{ Repo, User }
import Ecto.Query
def run(_args) do
Mix.Task.run("app.start", [])
Mix.EctoSQL.ensure_started(MyProject.Repo, [])
from(u in User, where: is_nil(u.user_type))
|> Repo.update_all(set: [some_condition])
end
end
At the start of the blog while explaining the benefits of using task for writing the query. I emphasized writing the test to make sure our query is working fine. For writing the test you can add a test file in the folder test/mix/task/add_user_type_test.ex file and you can write the test for your task. We can cover about writing test for mix task in some other post.
I hope you like this post. If you have any questions then please comment below. Thanks for reading 😊.