Now that we've learned about dependencies, we can try it out ourselves. I'm going to create a project and write some simple code that uses a dependency. You're welcome to follow along by doing the same thing yourself if you like.
Creating the Project
I created a new Elixir project called "dep_project", which I created by running mix new dep_project
.
You can also find the completed project in the the "lwm 59 - Using Dependencies in our Own Project" folder in the code examples in the Learn With Me: Elixir repository on Github.
Adding a Dependency
I'm going to be installing and using the "poison" dependency, which despite its odd name, is a library for handling JSON.
Now I'm going to go to the hex website at https://hex.pm, and search for "poison". It's a popular library, so it comes up as the top search result. Clicking on the title takes me to the package page.
This page gives us some useful information like where to find the project documentation and source code, the license, how many downloads it has, the version history, who published it, its dependencies (there are none), which packages are using it (a lot of them), and what to put into your mix.exs file in order to use it. At the time of this writing, the latest version is 4.0.1.
Note that you don't absolutely have to use the latest version of package: all versions are in the hex archive. If your project has only been tested against 3.1.0, then specify that version number as your dependency. You can always upgrade and do regression testing at some later date.
Let's start by copying the dependency information into the mix.exs file. Here's what my deps/0
function in mix.exs looks like now.
defp deps do
[
{:poison, "~> 4.0"}
]
end
This indicates that the project will retrieve the latest 4.* package.
Now that I've added the dependency to mix.exs, I can look at the dependencies by running mix deps
.
> mix deps
* poison (Hex package) (mix)
the dependency is not locked. To generate the "mix.lock" file run "mix deps.get"
Next, I'll retrieve the dependencies by running mix deps.get
.
> mix deps.get
Resolving Hex dependencies...
Dependency resolution completed:
New:
poison 4.0.1
All dependencies are up to date
Now the project has downloaded the poison package and stored it in the project directory. I'm going to build the project and it's dependencies by running mix
on the command line.
> mix
==> poison
Compiling 4 files (.ex)
Generated poison app
==> dep_project
Compiling 1 file (.ex)
Generated dep_project app
Now the poison package is ready to to use.
Using Poison
Before I use the poison library, I'm going to read the poison documentation on Github. The API documentation that was generated from the source code contains mostly the same information, but it gives us a little better idea of what the functions look like.
This library looks fairly easy to use. I can convert between JSON and a map data structure and I can even convert between JSON and a struct. The functions I'll be using are Poison.decode/2
and Poison.encode.2
.
Creating the Project Code
I'm going to now create a "JsonConvert" module that will convert between JSON and maps by calling the functions in the poison library. I'm not really adding any value here, but I am getting some practice in using dependencies.
defmodule JsonConvert do
def convert_to_json(data) do
Poison.encode(data)
end
def parse_json(json) do
Poison.decode(json)
end
end
These functions just call the equivalent poison function and return the result. I use the non-exception-throwing versions of the poison functions. There are also functions that will throw an exception when an error is encountered.
Now let's build the project again.
> mix
Compiling 1 file (.ex)
Generated dep_project app
It builds, so that's promising.
Running the Project
I'm going to now load the project into IEx and try running the functions.
λ iex -S mix
Interactive Elixir (1.8.0) - press Ctrl+C to exit (type h() ENTER for help)
#Create some map data
iex> data = %{name: "Bob", age: 32, status: :napping}
%{age: 32, name: "Bob", status: :napping}
#Convert the map to JSON
iex> JsonConvert.convert_to_json(data)
{:ok, "{\"status\":\"napping\",\"name\":\"Bob\",\"age\":32}"}
#Create some JSON
iex> json = "{\"name\": \"Gertrude\", \"age\": 5, \"favorite_color\": \"burnt sienna\"}"
"{\"name\": \"Gertrude\", \"age\": 5, \"favorite_color\": \"burnt sienna\"}"
#Convert it to a map
iex> JsonConvert.parse_json(json)
{:ok, %{"age" => 5, "favorite_color" => "burnt sienna", "name" => "Gertrude"}}
#Create some invalid JSON (item2 key needs to be surrounded in quotes, no ending curly brace)
iex> bad_json = "{\"item1\": \"blueberries\", item2: 56.4"
"{\"item1\": \"blueberries\", item2: 56.4"
#Attempt to parse the invalid JSON
iex> JsonConvert.parse_json(bad_json)
{:error, %Poison.ParseError{pos: 25, rest: nil, value: "i"}}
Success! I've added a dependency and used it in my code. It was a modest goal, but you have to take baby steps before you can start running.
I did not mess with the default unit test, so it will fail if I try to run it.
Conclusion
That's it for today. I encourage you to try this out for yourself. You can also get the code from Github as well.