10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2024

Day 9

ElixirでPAY.JPのライブラリ作成シリーズ⑤ config.exの作成

Last updated at Posted at 2024-12-08

こちらの続きです。

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

つまり、この設定により、アプリケーションとして正しく動作するための基本的な構造が確立されます。

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?