こちらの続きです。
lib/payjpex/config.ex
defmodule Payjpex.Config do
@moduledoc """
PAY.JP APIの設定を管理するモジュール。
このモジュールはGenServerとして実装され、以下の機能を提供します:
- API設定の保存と取得
- 設定値の動的な更新
- デフォルト値の管理
- 設定のリセット機能
## 設定可能な項目
* `api_key` - PAY.JPのAPIキー
* `api_base` - APIのベースURL
* `api_version` - 使用するAPIバージョン
* `open_timeout` - 接続タイムアウト時間(ミリ秒)
* `read_timeout` - 読み取りタイムアウト時間(ミリ秒)
* `max_retry` - リトライ最大回数
* `retry_initial_delay` - 初回リトライまでの待機時間(ミリ秒)
* `retry_max_delay` - 最大リトライ待機時間(ミリ秒)
## Examples
# 設定の取得
api_key = Payjpex.Config.get(:api_key)
# 設定の更新
Payjpex.Config.put(:api_key, "sk_test_xxxxx")
# 全設定の取得
all_config = Payjpex.Config.get_all()
"""
use GenServer
@doc """
設定管理用GenServerを起動します。
アプリケーション起動時にスーパーバイザーによって自動的に呼び出されます。
プロセスは名前付きプロセスとして登録され、アプリケーション全体で単一のインスタンスとして動作します。
## パラメータ
* `_opts` - 起動オプション(現在は使用していません)
## 戻り値
* `{:ok, pid}` - プロセスが正常に起動した場合
* `{:error, term}` - 起動に失敗した場合
"""
def start_link(_opts) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
@doc """
指定されたキーの設定値を取得します。
## パラメータ
* `key` - 取得したい設定のキー
## 戻り値
設定値。キーが存在しない場合はnil
## Examples
iex> Payjpex.Config.get(:api_base)
"https://api.pay.jp"
"""
def get(key) do
GenServer.call(__MODULE__, {:get, key})
end
@doc """
指定されたキーに値を設定します。
## パラメータ
* `key` - 設定するキー
* `value` - 設定する値
## 戻り値
* `:ok` - 設定の更新に成功した場合
## Examples
iex> Payjpex.Config.put(:api_key, "sk_test_xxxxx")
:ok
"""
def put(key, value) do
GenServer.call(__MODULE__, {:put, key, value})
end
@doc """
全ての設定値をマップとして取得します。
## 戻り値
現在の全設定値を含むマップ
## Examples
iex> Payjpex.Config.get_all()
%{
api_key: nil,
api_base: "https://api.pay.jp",
api_version: nil,
open_timeout: 30_000,
read_timeout: 90_000,
max_retry: 0,
retry_initial_delay: 2_000,
retry_max_delay: 32_000
}
"""
def get_all do
GenServer.call(__MODULE__, :get_all)
end
@doc """
全ての設定をデフォルト値にリセットします。
## 戻り値
* `:ok` - リセットに成功した場合
## Examples
iex> Payjpex.Config.reset()
:ok
"""
def reset do
GenServer.call(__MODULE__, :reset)
end
# サーバーコールバック
@doc false # プライベートコールバック
@impl true
def init(:ok) do
initial_state = %{
api_key: nil,
api_base: Application.get_env(:payjpex, :api_base, "https://api.pay.jp"),
api_version: Application.get_env(:payjpex, :api_version, nil),
open_timeout: Application.get_env(:payjpex, :open_timeout, 30_000),
read_timeout: Application.get_env(:payjpex, :read_timeout, 90_000),
max_retry: Application.get_env(:payjpex, :max_retry, 0),
retry_initial_delay: Application.get_env(:payjpex, :retry_initial_delay, 2_000),
retry_max_delay: Application.get_env(:payjpex, :retry_max_delay, 32_000)
}
{:ok, initial_state}
end
@doc false # プライベートコールバック
@impl true
def handle_call({:get, key}, _from, state) do
{:reply, Map.get(state, key), state}
end
@doc false # プライベートコールバック
@impl true
def handle_call({:put, key, value}, _from, state) do
{:reply, :ok, Map.put(state, key, value)}
end
@doc false # プライベートコールバック
@impl true
def handle_call(:get_all, _from, state) do
{:reply, state, state}
end
@doc false # プライベートコールバック
@impl true
def handle_call(:reset, _from, _state) do
new_state = init(:ok) |> elem(1)
{:reply, :ok, new_state}
end
end
では、テストも書いていきましょう。
test/config_test.exs
defmodule Payjpex.ConfigTest do
use ExUnit.Case
doctest Payjpex.Config
setup do
# 各テストの前に設定をリセット
Payjpex.Config.reset()
:ok
end
describe "設定の管理" do
test "デフォルト値が正しく設定されていること" do
assert Payjpex.Config.get(:api_base) == "https://api.pay.jp"
assert Payjpex.Config.get(:api_key) == nil
assert Payjpex.Config.get(:open_timeout) == 30_000
end
test "値を更新できること" do
Payjpex.Config.put(:api_key, "sk_test_123")
assert Payjpex.Config.get(:api_key) == "sk_test_123"
end
test "全ての設定を取得できること" do
config = Payjpex.Config.get_all()
assert is_map(config)
assert Map.has_key?(config, :api_base)
assert Map.has_key?(config, :api_key)
end
test "設定をリセットできること" do
Payjpex.Config.put(:api_key, "sk_test_123")
Payjpex.Config.reset()
assert Payjpex.Config.get(:api_key) == nil
end
end
end
さて、この辺りでそろそろ、テスト実行して動作確認したいですが、Apiclientも作っておく必要があるので、中身の無い簡易的なものを作成しておきます。
lib/payjpex/api_client.ex
defmodule Payjpex.ApiClient do
@moduledoc """
PAY.JP APIとの通信を処理するモジュール。
"""
use GenServer
def start_link(_opts) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
def init(:ok) do
{:ok, %{}}
end
end
ApiClientはまた後ほどじっくり作っていくとして、まずはここまでの実装をテストできるように準備します。テストコードは書いてあるのであとは設定を追加するだけです。
ルートディレクトリにconfigフォルダを追加して、test用の設定ファイルを追加します。
config/test.exs
import Config
config :payjpex,
api_key: "sk_test_123",
api_base: "https://api.pay.jp",
api_version: nil,
open_timeout: 30_000,
read_timeout: 90_000
続いて、applicationに mod: の設定を追加します。
mix.exs
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger],
mod: {Payjpex, []} # アプリケーションのエントリーポイントを指定
]
end
mod: {Payjpex, []}
これは、なぜ追加するかというと、payjpex.ex
で、use Application
を利用している為、アプリケーション起動時に自動的にスーパーバイザーが起動し、設定された子プロセスが自動的に開始して、アプリケーションのライフサイクル管理にするためで、自動的にstart関数を呼び出すようにする為です。
lib/payjpex.ex
defmodule Payjpex do
use Application # これと関連している
def start(_type, _args) do # この関数が自動的に呼ばれる
children = [
{Payjpex.Config, []},
{Payjpex.ApiClient, []}
]
Supervisor.start_link(children, opts)
end
end
つまり、この設定により、アプリケーションとして正しく動作するための基本的な構造が確立されます。