Introduction
I was recently asked by a friend, whom I converted to functional programming and Elixir, how to include a package located on the local file system. To be honest, I knew it was very possible, but I wasn’t able to give a straight answer. This post is this answer. We’ll define a dummy library and we’ll see the different ways to make it available to another applications.
The dummy library
As an example of a library, we’ll write a calculator package that will
be able to perform the four basic arithmetic operations, add()
,
sub()
, mult()
and div()
.
A library is not different from a normal application, except for few details that we’ll explain later. So the first step is to create the application:
1mix new dummy_calc
Then we replace the content of lib/dummy_calc.ex
with:
1defmodule DummyCalc do
2 @moduledoc """
3 This module provides functions for basic arithmetic
4 operations such as addition, substraction, multiplication and
5 division.
6 """
7
8 def add(x,y), do: x+y
9 def sub(x,y), do: x-y
10 def mult(x,y), do: x*y
11 def div(x,y) when y != 0, do: x/y
12
13end
We can check if it works by starting the REPL with iex -S mix
and
by typing DummyCalc.add(1, 9)
which should reply 10
.
The last thing is to create a git
repository
The ciam
command is my shortcut for commit -a -m
. The last line
renames the master
branch into main
as both Github and Gitlab use
main
as the default brach name.
And basically we are done for the library. In the following sections,
we’ll only change some content of the mix.exs
file, when needed.
The client of the library
To demonstrate how we import and use the dummy_calc
library, we need
a client application that will use dummy_calc
as it
dependency. Let’s call it client
.
Let’s create it with mix new client
. We then modify the file
lib/client.ex
so that it require
s DummyCalc
and uses its
functions:
1defmodule Client do
2 @moduledoc false
3
4 require DummyCalc
5
6 def main do
7 IO.puts("#{3}+#{1} -> #{DummyCalc.add(3, 1)}")
8 IO.puts("#{3}-#{1} -> #{DummyCalc.sub(3, 1)}")
9 IO.puts("#{3}*#{2} -> #{DummyCalc.mult(3, 2)}")
10 IO.puts("#{4}/#{2} -> #{DummyCalc.div(4, 2)}")
11 end
12end
This is sufficient. We will now see different ways to import DummyCalc
as a dependency of Client
.
Importing the library
We’ll consider three ways to import the library: from the local file
system, from a git
repository on the internet and for Hex.pm, the
official repository for Elixir’s packages.
Local file system
If you are writing a library, chances are good that you also have a test application and that both are located on your local file system. Let’s imagine a configuration like this:
To use dummy_calc
, client
need to declare the dependency like
this:
It’s always a good thing to mix deps.get
after modifying mix.exs
even if in this case we don’t need to. Then we enter the REPL (iex -S mix
) and test by executing Client.main
.
Using a repository on the internet
A good habit, even if you are a solo developer, is to save the source
code on an external repository. Personally, I’m using Gitlab, but of
course Github is fine, and probably any other that proposes git
as
the management tool.
First, we need to create a new repository on Gitlab. Then, as we
already created a local git
repository, we need to add the
information about the origin being on Gitlab and to push the local
repository:
Then we add the dependency to mix.exs
:
1defp deps do
2 [
3 # {:dummy_calc, path: "../dummy_calc" },
4 {:dummy_calc, git: "https://gitlab.com/mszmurlo/dummy_calc.git"}
5 ]
6 end
To make sure there is no any remaining artifact from the previous test
and that we start with a fresh environment in the client
directory,
we’ll clean up the directory with:
1mix clean --deps && rm -rf deps/ _build/ mix.lock
Then we get the dependencies:
and we can test:
1iex -S mix
2... Erlang header
3... Compilation log
4... Interactive Elixir header
5iex(1)> Client.main
63+1 -> 4
73-1 -> 2
83*2 -> 6
94/2 -> 2.0
Looks like it works!
Publishing the library on Hex
Once you consider your library is ready to be used by others, you can publish it on Hex.pm. To do this we need to get back to our package and prepare it to be published.
The documentation is mandatory
One of the pillar of Elixir’s success is its documentation. Hex.pm
requests all package have a dependency to produce the
documentation. To do so, include ex_docs
in the dependency list.
Don’t forget to import this dependency with mix deps.get
.
Add “package” properties
Make Hex aware this is a package by defining a package:
property in the project
list. First add the following:
Then define the package()
function to return a keyword list of
properties describing the package:
1defp package do
2 [
3 files: ["lib", "mix.exs", "README.md"],
4 maintainers: ["Maurycy Szmurlo"],
5 licenses: ["MIT"],
6 links: %{"GitLab" => "https://gitlab.com/mszmurlo/dummy_calc.git"},
7 description: """
8 Dummy library defining a calculator as a demo for how to create
9 and distrute a package
10 """,
11 ]
12end
Register on Hex.pm as a user
Before you publish your first package, you need to register on
Hex.pm. The operation is quite straightforward. Type mix hex.register
and fill in the requested information
Publish the package
This operation is also straightforward. Type mix hex.publish
; you’ll
be asked for the local password you have defined during the
registration and if you have provided all mandatory properties in the
package()
function, you should be OK. Otherwise, correct or add what
is missing and re-publish.
Notice that while the package is published on Hex.pm, its documentation is generated and published on hexdocs.pm.
Get the library from Hex.pm
Now we are ready to include our dummy_calc
library from the official
Elixir’s package repository, Hex.pm. This is definitely not rocket
science: just add the following tuple to the dependency list:
1defp deps do
2 [
3 # {:dummy_calc, path: "../dummy_calc" },
4 # {:dummy_calc, git: "https://gitlab.com/mszmurlo/dummy_calc.git"}
5 {:dummy_calc, "~> 0.1"}, # << This one
6 ]
7end
To test, remove all previous artifacts (mix clean --deps && rm -rf deps/ _build/ mix.lock
) and get the dependencies (mix deps.get
)
then start the REPL:
1iex -S mix
2... Erlang OTP header
3
4==> dummy_calc
5Compiling 1 file (.ex)
6Generated dummy_calc app
7==> client
8Compiling 1 file (.ex)
9Generated client app
10... IEx Interactive Elixir header
11
12iex(1)> DummyCalc.add 4, 8
1312
Isn’t that neat ?
Removing the library from Hex
If for any reason you want to remove a package from Hep.pm, you… can’t. Well, actually you can with following constraints as directly taken from the Hex documentation: A new package can be reverted or updated within 24 hours of it’s initial publish, a new version of an existing package can be reverted or updated within one hour. Documentation have no limitations on when it can be updated.
As a consequence, do not use Hex.pm as a code repository during development and only publish definitive versions.
As our dummy_calc
package is just an example and we don’t want to
pollute Hex.pm, we have time to remove it with:
Conclusion
Do not forget that software has evolved in a kind of Cambrian explosion and that we are able to have fun for free with some fantastic applications, languages, OSes, etc. because back in the 70’s or 80’s some people realized that sharing and opensourcing was important. So share your libraries, share your knowledge and if you are an Elixir user (well, if you have come so far in reading, I believe you are), I hope this short post clarified how to do so.
And a very important last thing: don’t forget to have fun!
References
- The official Hex documentation on Hexdoc.pm.
- The official documentation on publishing packages on Hex.pm.
dummy_calc
Gitlab repository