0
votes

I have created 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

I have the same Genserver as user.ex the only difference between both 2 are the Attributes.

in camera its

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

and in user its

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

and all other calls are duplicated, where I am saving a user/camera or updating a user or camera.

how I can make generic module which I can use in User and Camera, and it will adopt the behaviour of that module and It will help to reduce the duplication. Thank you.

1

1 Answers

0
votes

There are two different approaches: one is to use defdelegate/2 instead of code duplication.


Another one would be to use metaprogramming.

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

and use it as

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