Posted at

ElixirでHTTPリクエスト

fukuoka.exのkoyoです

最近Goばっかり書いているのでそろそろElixirするかということで書いてみます!


お題

Github API を使って特定のリポジトリのcloseされたPR一覧を出してみる


プロジェクトの作成

mix new sample_ex


必要なライブラリの追加

今回はHTTPリクエストにHTTPoison, レスポンスボディのパースにPoisonを使います


mix.exs

defp deps do

[
{:httpoison, "~> 1.5"},
{:poison, "~> 4.0"}
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
]
end


実装

対象リポジトリは昔作ったグラフ付きQiita検索アプリ

https://github.com/koyo-miyamura/vue_api_spa


sample_ex.ex

defmodule SampleEx do

@moduledoc """
Documentation for SampleEx.
"""

@spec main :: :ok
def main() do
pr_list() |> Enum.each(fn x -> IO.puts(x) end)
end

@spec pr_list :: charlist()
def pr_list() do
build_url("https://api.github.com/repos/koyo-miyamura/vue_api_spa/pulls", %{
"state" => "closed"
})
|> HTTPoison.get!()
|> Map.get(:body)
|> Poison.decode!()
|> Enum.map(fn x -> format(x) end)
end

@doc """
build url.

## Examples

iex> SampleEx.build_url("http://example.com", %{"foo" => "bar"})
"http://example.com?foo=bar"

"""
@spec build_url(String.t(), map()) :: String.t()
def build_url(path, query) do
URI.parse(path)
|> Map.put(:query, URI.encode_query(query))
|> URI.to_string()
end

@doc """
format map

## Examples

iex> SampleEx.format(%{"title" => "title", "created_at" => "2018-10-06T23:46:53Z"})
"title 2018-10-06"
"""
@spec format(map()) :: String.t()
def format(map) do
map_title = Map.get(map, "title")

map_date =
Map.get(map, "created_at")
|> NaiveDateTime.from_iso8601!()
|> NaiveDateTime.to_date()
|> Date.to_string()

map_title <> " " <> map_date
end
end


Elixirで時刻扱うのはちょっとクセありますがこんな感じで

https://hexdocs.pm/elixir/NaiveDateTime.html#content

またURI文字列を作成する部分はURIモジュールを使えば良いですが、クエリパラメータを結合する関数は用意されていなかったので以下を参考に自前で作ってみました

https://stackoverflow.com/questions/49508509/idiom-to-construct-a-uri-with-a-query-string-in-elixir


実行結果


キタ━(゚∀゚)━! $ iex -S mix
Erlang/OTP 21 [erts-10.3.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> SampleEx.main
ユーザランキング 2018-10-06
vuexの導入 2018-10-06
チャート機能の実装 2018-10-06
Tag search 2018-10-05
Fix design 2018-10-04
Use vue cli 2018-09-30
vue-routerの導入 2018-09-29
Use component 2018-09-26
formから検索ワード変更する機能の追加 2018-09-25
見た目の調整 2018-09-24

できたぜ!!


テストについて

できる部分はdoctestしましたが、HTTPリクエストを行うpr_listのテストはどうするのか分からず断念

Goだとnet/http使ってモックするやり方が考えられますが、Elixirはモックを推奨していない様子

探してみると自前でサーバ立てるやり方が書いてました

んーちょっと大変?もっといいやり方募集中です

https://medium.com/flatiron-labs/rolling-your-own-mock-server-for-testing-in-elixir-2cdb5ccdd1a0


ちなみにHTMLをパースしたい場合

Flokiというライブラリがメジャーみたいです

mix.exs{:floki, "~> 0.21.0"}

すると使えます


感想

最近Goばっかり書いてたんですが「GoでやるアレElixirでどうするんだろう?」という題材で学習してみると学びが深まりました

iex使って動かしながら作れるので楽しい

皆さんも普段使っている言語でやっていることをElixirで実現してみると面白いですよ!