Using SQL Aliases in Ecto

In some occasions, you might need to use alias in your Ecto query. For example, while developing TIL, I need to use both date_trunc (PostgreSQL) together with group_by.

In SQL, it can be written as:

select date_trunc('day', inserted_at) as date 
from "posts" 
group by date;

In Ecto, you can use fragment to call database function. Hence, to use date_trunc, we can just:

from q in Post,
select: [fragment("date_trunc('day', ?) as date", q.inserted_at],
group_by: :date

However, this will raise an error mentioning that date column is not available for Post table. Hence, we need a way to use the SQL aliases date in the query.

To use SQL alias in a group_by, we can simply use fragment again.

from q in Post,
select: [fragment("date_trunc('day', ?) as date", q.inserted_at],
group_by: fragment("date")

I was able to solve this thanks to this Stack Overflow question.

Compress file into tar.gz in Elixir

In Elixir, we can use the Erlang module erl_tar to compress a file into tar.gz. It is straightforward to create a tar file with erl_tar. However, there is a caveat.

Unexpected Error

Assuming that we have a file named test.md and run the following code:

# :erl_tar.create(TarFilename, [Filename], [Options])
:erl_tar.create("tarfilename.tar.gz", 
                ["test.md"], 
                [:write, :compressed])

Surprisingly, you will receive a FunctionClauseError which stated that there are no function clause matching in :erl_tar.add/4. (as for Elixir 1.8.1)

Solution

This is because the expected file name type in erl_tar seems to be charlists. It is mentioned in Elixir Official Guide that charlists are used mostly when interfacing with Erlang where some old libraries do not accept binaries as arguments. (The double quoted “string” is UTF-8 encoded binary)

To resolve the error, we need to use the single quoted file name, 'test.md'.

:erl_tar.create("tarfilename.tar.gz", 
                ['test.md'], 
                [:write, :compressed])

That’s all. Hopefully it helps!


You can also pass in a list of tuple into the second argument in the format of [{filenameInArchive, filename or binary}]. For more on :erl_tar.create refer to the documentation.

Tags: elixir

Verbose Git Commit

After staging your files, doing git commit will launch your editor, typically Vim, for you to enter the commit message.


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
#   modified:   vimrc
#

However, if you do git commit -v, you will get to see the diff for the commit you are about to make as well. This is a useful reminder for when you are writing the commit message.


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
#   modified:   vimrc
#
# ------------------------ >8 ------------------------
# Do not touch the line above.
# Everything below will be removed.
diff --git a/vimrc b/vimrc
index cc63644..fb3832c 100644
--- a/vimrc
+++ b/vimrc
@@ -439,6 +439,11 @@ nmap <C-f>f <Plug>CtrlSFPrompt
 nmap <C-f>w <Plug>CtrlSFCwordPath
 nnoremap <C-f>t :CtrlSFToggle<cr>
 
+" -----------------------------------------------------------------------------
+" emmet.snippets
+" -----------------------------------------------------------------------------
+imap <C-e>, <esc>dawae<tab><esc>pa<tab>
+
 " -----------------------------------------------------------------------------
 " ultisnips
 " -----------------------------------------------------------------------------

To make this the default behavior of git commit, you can do git config --global commit.verbose true. This will add the following entry into .gitconfig.

[commit]
  verbose = true

Tags: git

More from Less

The less command actually accepts a lot of configuration options. For instance, less -F <file> will make the pager quit if the file can be displayed within one screen. We can also do export LESS='-F' so specific options apply to all less commands. That way, other commands that use less like git and man will also benefit from the environment variable. Use man less to read more about all the different kind of options you can use to make less more. Here is a nice article that covers some interesting options.

Send temporary file in Phoenix controller

TLDR: Use Phoenix.Controller.send_download/3 to send binary as download.


There will be some occasions when you need to send a file to user in your applications. In Phoenix, there are a couple ways of doing it. The most straightforward one is to programmatically create a file and send it to user using Plug.Conn.send_file/5.

For example:

def export(conn, _params) do
  # Create file
  filename = "test.md"
  File.write(filename, "Hello World")
  
  # Send file
  conn
  |> put_resp_header("content-disposition", ~s(attachment; filename="#{filename}"))
  |> send_file(200, filename)
end

However, this approach create a file locally in your production server and require some clean up after that. (I don’t really know how to delete a file only after the request succeed)

Luckily, there is another approach, which is using Phoenix.Controller.send_download/3. This function allow us to send binary directly as a download. Hence, file creation is not needed.

For example:

def export(conn, _params) do
  filename = "test.md"
  
  # Send file
  conn
  |> send_download({:binary, "Hello World"}, filename: filename)
end

For more, refer to the send_file/5 and send_download/3 documentation.

Tags: phoenix