はじめに
- たとえばPhoenixでControllersを書く時に、
use HelloWeb, :controller
みたいなことを書きますが、use
しているものを自分で書くにはどうしたらいいのでしょうか -
useしているものを自分で書きたいという思いは、単なる憧れのようなものでした
- quoteとかみたらそっと閉じていました
- SlackのAPIをコールすることを通じてこの体験ができましたので書いておきます
- sapporo.beam #326というほぼ毎週水曜日に「思い思いの場所」で行われる勉強会での成果であります
Elixir 1.10.3
初期バージョン
- users.listとconversations.listをコールするプログラムを書いてみました
- Hexは、HTTPoisonとJasonを使っています
users.list
lib/awesome_app/slack/api/users/list.ex
defmodule AwesomeApp.Slack.Api.Users.List do
@token Application.get_env(:awesome_app, :slack_token)
def run do
get(url(), [])
end
def get(url, got_members) do
url
|> HTTPoison.get()
|> handle_response()
|> Jason.decode!()
|> next_action(got_members)
end
defp handle_response({:ok, %{body: body, status_code: 200}}), do: body
defp handle_response(_), do: ~s/{"members":[], "response_metadata":{"next_cursor":""}}/
defp next_action(
%{"members" => members, "response_metadata" => %{"next_cursor" => ""}},
got_members
) do
got_members ++ members
end
defp next_action(
%{"members" => members, "response_metadata" => %{"next_cursor" => next_cursor}},
got_members
) do
url(next_cursor)
|> get(got_members ++ members)
end
defp url do
"https://slack.com/api/users.list?token=#{@token}"
end
defp url(next_cursor) do
"#{url()}&cursor=#{next_cursor}"
end
end
conversations.list
lib/awesome_app/slack/api/conversations/list.ex
defmodule AwesomeApp.Slack.Api.Conversations.List do
@token Application.get_env(:awesome_app, :slack_token)
def run do
get(url(), [])
end
def get(url, got_channels) do
url
|> HTTPoison.get()
|> handle_response()
|> Jason.decode!()
|> next_action(got_channels)
end
defp handle_response({:ok, %{body: body, status_code: 200}}), do: body
defp handle_response(_), do: ~s/{"channels":[], "response_metadata":{"next_cursor":""}}/
defp next_action(
%{"channels" => channels, "response_metadata" => %{"next_cursor" => ""}},
got_channels
) do
got_channels ++ channels
end
defp next_action(
%{"channels" => channels, "response_metadata" => %{"next_cursor" => next_cursor}},
got_channels
) do
url(next_cursor)
|> get(got_channels ++ channels)
end
defp url do
"https://slack.com/api/conversations.list?token=#{@token}"
end
defp url(next_cursor) do
"#{url()}&cursor=#{next_cursor}"
end
end
- 構造は全く同じです
-
members
という字面とchannels
という字面の違いとAPIのパスが違うくらいです
改善
lib/awesome_app/slack/api.ex
defmodule AwesomeApp.Slack.Api do
defmacro __using__(opts) do
key = Keyword.get(opts, :key)
method = Keyword.get(opts, :method)
quote do
@token Application.get_env(:awesome_app, :slack_token)
def run do
get(url(), [])
end
def get(url, got_values) do
url
|> HTTPoison.get()
|> handle_response()
|> Jason.decode!()
|> next_action(got_values)
end
defp handle_response({:ok, %{body: body, status_code: 200}}), do: body
defp handle_response(_),
do: ~s/{"#{unquote(key)}":[], "response_metadata":{"next_cursor":""}}/
defp next_action(
%{unquote(key) => values, "response_metadata" => %{"next_cursor" => ""}},
got_values
) do
got_values ++ values
end
defp next_action(
%{unquote(key) => values, "response_metadata" => %{"next_cursor" => next_cursor}},
got_values
) do
url(next_cursor)
|> get(got_values ++ values)
end
defp url do
"https://slack.com/api/#{unquote(method)}?token=#{@token}"
end
defp url(next_cursor) do
"#{url()}&cursor=#{next_cursor}"
end
end
end
end
lib/awesome_app/slack/api/users/list.ex
defmodule AwesomeApp.Slack.Api.Users.List do
use AwesomeApp.Slack.Api, key: "members", method: "users.list"
end
lib/awesome_app/slack/api/conversations/list.ex
defmodule AwesomeApp.Slack.Api.Conversations.List do
use AwesomeApp.Slack.Api, key: "channels", method: "conversations.list"
end