首页 > 解决方案 > 如何在一个模块 Elixir 中获取所有重复的 genserver 调用

问题描述

我创建了 2 个 genservers。

camera.ex

defmodule Camera do
  use GenServer

  defmodule Attributes do
    defstruct ~w|name url password port username id owner_id status|a
  end

  def start_link(state \\ %Attributes{} ) do
    GenServer.start_link(__MODULE__, %Attributes{state | id: make_ref()})
  end

  def init(state) do
    {:ok, state}
  end

  def details(pid) do
    GenServer.call(pid, :get)
  end

  def update(pid, map) do
    GenServer.cast(pid, {:update, map})
  end

  def handle_call(:get, _from, state) do
    {:reply, state, state}
  end

  def handle_cast({:update, map}, state) do
    {:noreply, Map.merge(map, state)}
  end
end

我有相同的 Genserver,因为user.ex两者之间的唯一区别是属性。

在相机中

  defmodule Attributes do
    defstruct ~w|name url password port username id owner_id status|a
  end

在用户中

  defmodule Attributes do
    defstruct ~w|id username|a
  end

并且所有其他调用都是重复的,我正在保存用户/相机或更新用户或相机。

我如何制作可以在用户和相机中使用的通用模块,它将采用该模块的行为,这将有助于减少重复。谢谢你。

标签: elixir

解决方案


有两种不同的做法:一种是使用defdelegate/2代码复制代替。


另一种是使用元编程。

defmodule Scaffold do
  @moduledoc false

  defmacro __using__(opts) do
    quote generated: true, location: :keep do
      use GenServer

      defmodule Attributes, do: defstruct unquote(opts)

      def start_link(state \\ %Attributes{}) do
        GenServer.start_link(__MODULE__,
          %Attributes{state | id: make_ref()})
      end

      def init(state), do: {:ok, state}

      def details(pid),
        do: GenServer.call(pid, :get)

      def update(pid, map),
        do: GenServer.cast(pid, {:update, map})

      def handle_call(:get, _from, state),
        do: {:reply, state, state}

      def handle_cast({:update, map}, state),
        do: {:noreply, Map.merge(map, state)}
    end
  end
end

并将其用作

defmodule Camera do
  use Scaffold, [:name, :url, ...]
end

推荐阅读