Creating my first erlang application
Hello! I want to share my first experience of programming in erlang. This is very interesting programming language and a little clipping from main page:
Erlang is a programming language used to build massively scalable soft real-time systems with requirements on high availability. Some of its uses are in telecoms, banking, e-commerce, computer telephony and instant messaging. Erlang's runtime system has built-in support for concurrency, distribution and fault tolerance.
My first application will not be ‘Hello, World!’ as always. I’m going to create simple application allowing extract some information related to specified github user. In two words, user enters needed github username, program make request to github through http and shows some information for specified user. Let’s get it started!
After little bit of searching I had found package manager for erlang - hex, where pointed following in ‘Using with Erlang’ section:
Download rebar3, put it in your PATH and give it executable permissions. Now you can specify Hex dependencies in your rebar.config like {deps, [hackney]}
I followed this step, after installing rebar3 I change PATH environment variable appending ~/bin to it where rebar3 was installed to then start new app with:
That command created boilerplate for new application with several files (Readme.md, rebar.config and so on), and src folder with files for app sources.
Our file for writing source code is coper_app.erl. It was already consist some code with documentation. And here we goin on!
Firstly we should figure out how make request to github api to extract data. It is very easy, let say we want extract information for chadhietala’s account, we make request to https://api.github.com/users/chadhietala and get json data as follows:
{ "login": "chadhietala", "id": 183799, "avatar_url": "https://avatars2.githubusercontent.com/u/183799?v=3", "gravatar_id": "", "url": "https://api.github.com/users/chadhietala", "html_url": "https://github.com/chadhietala", "followers_url": "https://api.github.com/users/chadhietala/followers", "following_url": "https://api.github.com/users/chadhietala/following{/other_user}", "gists_url": "https://api.github.com/users/chadhietala/gists{/gist_id}", "starred_url": "https://api.github.com/users/chadhietala/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/chadhietala/subscriptions", "organizations_url": "https://api.github.com/users/chadhietala/orgs", "repos_url": "https://api.github.com/users/chadhietala/repos", "events_url": "https://api.github.com/users/chadhietala/events{/privacy}", "received_events_url": "https://api.github.com/users/chadhietala/received_events", "type": "User", "site_admin": false, "name": "Chad Hietala", "company": "LinkedIn", "blog": "http://twitter.com/chadhietala", "location": "San Francisco, CA", "email": null, "hireable": true, "bio": null, "public_repos": 191, "public_gists": 119, "followers": 77, "following": 16, "created_at": "2010-01-16T23:18:15Z", "updated_at": "2017-03-16T21:04:51Z" }
Now we should have tool for decoding json string. After some searching I found cool jsx library. I had added it to deps in rebars.config:
{erl_opts, [debug_info]}.
{deps, [
{jsx, "2.8.0"}
]}.
Then we create function called get_props_for_github_user, that accepts one argument - username (for github). Now we should implement code that does work. After some googling I found needed erlang’s module for performing http requests - httpc. Firstly I had trying plain request just with url. But calling that gave me error that the User-Agent header should be specified from github api. So I specified header with tuple {"User-Agent", "Mozilla/5.0"}. After that we’ve got response body when successful request.
Next step we decode source body using jsx:decode preliminary converting list to binary. We can work with our list of tuples now. In order to extract tuple by key from list of tuples we can use lists:keyfind/3 function that returns found tuple or false. Let say we want to get id, created_at and type fields. We can create new variables for every type that may consist neither tuple for found key or false. Now we want to display info about every field when key was found. For that we can create function output_if_exists/1, that would be display info with key and value, or show that key was not found. An argument in that function accepts tuple or boolean, we can simply check whether passing argument tuple (using is_tuple/1 function) or not and output corresponding info. For properly format tuple in output string we can convert tuple to list firstly, and use lists:map/2 for iterating for every value and converting binary to list (string in erlang representated as list) if value is binary. We can create function to convert binary to list called convert_bin_to_list that does that work. Inside this function we can test erlang’s case expression.
And our source functions looks loke this:
get_props_for_github_user(Username) -> {ok, {{_, 200, _}, _, Body}} = httpc:request(get, {"https://api.github.com/users/" ++ Username, [ {"User-Agent", "Mozilla/5.0"} ]}, [], []), PropsList = jsx:decode(list_to_binary(Body)), IdTuple = lists:keyfind(<<"id">>, 1, PropsList), CreatedAtTuple = lists:keyfind(<<"created_at">>, 1, PropsList),
TypeTuple = lists:keyfind(<<"type">>, 1, PropsList), output_if_exists(IdTuple), output_if_exists(CreatedAtTuple), output_if_exists(TypeTuple). output_if_exists(TupleOrBool) -> if is_tuple(TupleOrBool) -> io:format(" ~p -> ~p.~n", lists:map(fun convert_bin_to_list/1, tuple_to_list(TupleOrBool))); true -> io:format("Key not present.~n") end. convert_bin_to_list(Item) -> case is_binary(Item) of true -> binary_to_list(Item); _ -> Item end.
We should also export our get_props_for_github_user/1 function with
-export([get_props_for_github_user/1]).
Yo! Our first program is ready. We can compile and test it. Run command:
If everything is ok, we should see erlang shell and ready to use it!
For example for user mnishiguchi we got:
coper_app:get_props_for_github_user("mnishiguchi").
"id" -> 7563926.
"created_at" -> "2014-05-13T00:55:37Z".
"type" -> "User".
coper_app:get_props_for_github_user("emberjs").
"id" -> 1253363.
"created_at" -> "2011-12-10T00:25:10Z".
"type" -> "Organization".