Skip to content

Instantly share code, notes, and snippets.

@trbngr
Last active June 18, 2021 20:12
Show Gist options
  • Save trbngr/12e45b26749f1438bf641c3e7ac7a6b5 to your computer and use it in GitHub Desktop.
Save trbngr/12e45b26749f1438bf641c3e7ac7a6b5 to your computer and use it in GitHub Desktop.
Phoenix Socket Macros
[
locals_without_parens: [handle_message: 2, handle_message: 3],
import_deps: [:ecto, :phoenix, :cqrs_tools],
inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"],
subdirectories: ["priv/*/migrations"]
]
defmodule SocketMacro.Commands.DoSomething do
use Cqrs.Command
# More info: https://hexdocs.pm/cqrs_tools/Cqrs.Command.html
alias Phoenix.Socket
field :msg, :string
field :player, :integer
option :socket, Socket, default: nil
option :send_message, Function, default: nil
@impl true
def handle_dispatch(%{player: player_id} = state, opts) do
send_message(%{my: "player #{player_id} has done it now."}, opts)
{:ok, Map.from_struct(state)}
end
defp send_message(message, opts) do
with %Socket{} = socket <- Keyword.fetch!(opts, :socket),
send_message when is_function(send_message, 2) <- Keyword.fetch!(opts, :send_message) do
send_message.(socket, message)
end
end
end
defmodule SocketMacro.MixProject do
use Mix.Project
...
defp deps do
[
...,
# CQRS Tools are macros to make your life managable.
{:cqrs_tools, "~> 0.3"}
]
end
end
defmodule Cqrs.Phoenix.Socket do
defmacro __using__(_) do
quote do
use Phoenix.Socket
import Cqrs.Phoenix.Socket, only: [handle_message: 2, handle_message: 3]
@before_compile Cqrs.Phoenix.Socket
end
end
defmacro __before_compile__(_env) do
quote do
# Catch-all handler
def handle_msg(_socket, _state, _payload), do: {:disconnect}
end
end
defmacro handle_message(pattern, command_module, opts \\ []) do
quote do
Cqrs.Guards.ensure_is_command!(unquote(command_module))
def handle_msg(socket, state, payload = unquote(pattern)) do
{auth, state} = Map.pop(state, :auth, %{})
# This allows you to define any fields from payload, auth, or state in your command and validate them as needed.
attrs = payload |> Map.merge(auth) |> Map.merge(state)
opts = Keyword.put(unquote(opts), :socket, socket)
Cqrs.BoundedContext.__dispatch_command__!(unquote(command_module), attrs, opts)
end
end
end
end
defmodule SocketMacroWeb.UserSocket do
# use Phoenix.Socket
# Changed to this
use Cqrs.Phoenix.Socket
alias SocketMacro.Commands.DoSomething
defp on_message(_socket, message) do
# send_msg(socket, message)
IO.puts("sending #{inspect(message)}")
end
# send_message would probably just be "send_message: &send_msg/2" but this is for illustration that it works.
handle_message %{"msg" => "some_action"}, DoSomething, send_message: &on_message/2
@impl true
def connect(_params, socket, _connect_info) do
{:ok, socket}
end
@impl true
def id(_socket), do: nil
end
@trbngr
Copy link
Author

trbngr commented Jun 18, 2021

image

@trbngr
Copy link
Author

trbngr commented Jun 18, 2021

Command option signatures are only for documentation.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment