Hello everyone,
I’m a new in elexir and functional language. I’m trying to implement Websocket interraction with server.
On first layer I implement module based on GenServer with functionality like WebSockex and use ninenines/gun as Websocket client. This module name egun, below are the code snippets:
defmodule Egun do
use GenServer
@callback handle_connect(state :: term) :: {:ok, new_state :: term}
@callback handle_message(msg :: term, state :: term) :: {:ok, new_state} | ...
@callback handle_send(msg :: term, state :: term) :: {:ok, new_state} | ...
...
defmacro __using__(_opts) do
quote location: :keep do
@behaviour Egun
@doc false
def handle_connect(state) do
{:ok, state}
end
@doc false
def handle_message(message, _state) do
raise "No handle_message/2 clause in #{__MODULE__} provided for #{inspect(message)}"
end
@doc false
def handle_send(message, state) do
{:reply, {:text, message}, state}
end
defoverridable handle_connect: 1,
handle_message: 2,
handle_send: 2
end
def start_link(conn = %Conn{}) do
GenServer.start_link(__MODULE__, %__MODULE__{
conn: conn,
})
end
def init(state) do
...
send(self(), :ws_connected)
{:ok, state}
end
def send_message(client, message) do
GenServer.call(client, {:ws_syncsend, message})
end
def handle_call({:ws_syncsend, message}, sender, state) do
message = compose_msg(message, state.conn)
...
{:reply, message, state}
end
def handle_info(:ws_connected, state) do
common_handle(:handle_connect, state)
end
def handle_info(
{:gun_ws, _gun_pid, _ws_stream, {:text, message}}, state
) do
case Jason.decode(message, keys: :atoms) do
{:ok, msg} ->
common_handle({:handle_message, msg}, state)
error ->
{:stop, error, state}
end
end
defp common_handle({function, msg}, state) when is_atom(function) do
result = apply(state.module, function, [msg, state])
common_handle_ret(result, {function, msg}, state)
end
defp common_handle(function, state) when is_atom(function) do
result = apply(state.module, function, [state])
common_handle_ret(result, {function, nil}, state)
end
defp common_handle_ret(result, {function, msg}, state) do
case result do
{:ok, new_state} ->
{:noreply, new_state}
{:reply, frame, new_state} ->
:gun.ws_send(new_state.gun_pid, frame_encode(frame))
{:noreply, new_state}
....
end
end
This implementation provides messaging through an open Websocket. Next I need extend functionality egun. And create module session which adds business logic methods:
defmodule Session do
@moduledoc nil
use Egun
def create(client) when is_pid(client) do
tr = Egun.send_message(client, %{
session: "create",
transaction: "yes"
})
GenServer.call(client, {:push_transaction, tr, client})
end
@impl true
def handle_call({:push_transaction, transaction, callback_pid}, _from, state) do
trs = Map.put(state.transactions, transaction, callback_pid)
{:noreply, %{state | transactions: trs}}
end
@impl Egun
def handle_message(message, state) do
IO.puts("Handle message: #{inspect(message)}")
IO.puts("State: #{inspect(state)}")
{:ok, state}
end
end
After compile I get warning message:
warning: got "@impl Egun" for function handle_call/3 but this behaviour does not specify such callback. The known callbacks are:
* Egun.handle_connect/1 (function)
* Egun.handle_message/2 (function)
* Egun.handle_send/2 (function)
How can I modify my modules so that the Session module can implement the behavior of the GenServer and Egan without using an additional GenServer. On object oriented programming language Session can get access to all methods from GenServer and Egan?
?






















