Last active
August 28, 2019 10:49
-
-
Save sreecodeslayer/e5983c6224bffc5387024c1ac771a392 to your computer and use it in GitHub Desktop.
Fake Synchronous calls using Asynchronous GenServers in Elixir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule MyAppWeb.AgentChannel do | |
use Phoenix.Channel | |
alias MyAppWeb.Endpoint | |
alias MyApp.AgentCommand | |
def push(topic, event, payload) do | |
Endpoint.broadcast("agent:#{topic}", event, payload) | |
end | |
def join("agent:" <> _topic, _message, socket) do | |
{:ok, socket} | |
end | |
@doc """ | |
This is a handler for incoming messages on the "command_reply" topic from agents | |
""" | |
def handle_in("command_reply", payload, socket) do | |
AgentCommand.handle_reply(payload["command_ref"], payload["payload"]) | |
{:noreply, socket} | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule MyApp.AgentCommand do | |
use GenServer | |
alias MyAppWeb.AgentChannel | |
alias MyApp.RandomStringGenerator | |
def start_link do | |
GenServer.start_link(__MODULE__, nil, name: __MODULE__) | |
end | |
@doc """ | |
Call this function to send a command to agent | |
""" | |
def send(agent, command, command_payload, timeout \\ 20_000) do | |
payload = %{ | |
command: command, | |
payload: command_payload | |
} | |
GenServer.call(__MODULE__, {:send, agent.topic, payload}, timeout) | |
end | |
@doc """ | |
Call this function where the agent response is available | |
""" | |
def handle_reply(command_ref, reply) do | |
GenServer.cast(__MODULE__, {:handle_reply, command_ref, reply}) | |
end | |
def init(_) do | |
{:ok, %{}} | |
end | |
def handle_call({:send, topic, payload}, from, state) do | |
{ref, state} = insert_with_unique_key(state, from) | |
payload = Map.put(payload, :command_ref, ref) | |
AgentChannel.push(topic, "command", payload) | |
{:noreply, state} | |
end | |
def handle_cast({:handle_reply, command_ref, reply}, state) do | |
{from, state} = Map.pop(state, command_ref) | |
GenServer.reply(from, reply) | |
{:noreply, state} | |
end | |
defp insert_with_unique_key(map, value) do | |
key = RandomStringGenerator.string_of_length(20) | |
if Map.has_key?(map, key) do | |
insert_with_unique_key(map, value) | |
else | |
{key, Map.put(map, key, value)} | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment