Skip to content

Instantly share code, notes, and snippets.

@rranelli
Created March 3, 2017 18:02
Show Gist options
  • Save rranelli/e0377d88af8370be6d9e38c15678ee8a to your computer and use it in GitHub Desktop.
Save rranelli/e0377d88af8370be6d9e38c15678ee8a to your computer and use it in GitHub Desktop.
defmodule Protocolz do
defmacro defimplz(protocolo, [for: modulo_do_struct], [do: block]) do
quote do
defmodule (Module.concat([unquote(protocolo), unquote(modulo_do_struct)])) do
unquote(block)
end
end
end
defmacro defprotocolz(protocolo, [do: block]) do
corpo_do_protocolo = Macro.postwalk block, [], fn
ast = {:def, _, [{nome_da_funcao, _, argumentos}]}, acc ->
quoted_def =
quote do
def unquote(nome_da_funcao)(unquote_splicing(argumentos)) do
Protocolz.__dispatch__(unquote(protocolo),
unquote(nome_da_funcao),
unquote_splicing(argumentos)
)
end
end
{ast, [quoted_def | acc]}
ast, acc ->
{ast, acc}
end
quote do
defmodule unquote(protocolo) do
unquote(corpo_do_protocolo)
end
end
end
def __dispatch__(protocolo, nome_da_funcao, dado) do
modulo_do_struct = dado.__struct__
modulo_da_implementacao =
Module.concat(protocolo, modulo_do_struct)
:erlang.apply(modulo_da_implementacao, nome_da_funcao, [dado])
end
end
defmodule App do
import Protocolz, only: [defimplz: 3, defprotocolz: 2]
defmodule SecretMessage, do: (defstruct [:password, :username, :wathever])
defmodule PublicMessage, do: (defstruct [:username, :wathevis])
defprotocolz MyLogger do
def log(message)
end
defimplz MyLogger, for: SecretMessage do
def log(message) do
"password=SECRET username=#{message.username} wathever=#{message.wathever}"
end
end
defimplz MyLogger, for: PublicMessage do
def log(message) do
"username=#{message.username} wathever=#{message.wathevis}"
end
end
def main do
public = %PublicMessage{username: "mimi"}
IO.puts MyLogger.log(public)
secret = %SecretMessage{password: "eita", username: "mimi"}
IO.puts MyLogger.log(secret)
end
end
App.main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment