在__using__宏中添加默认的handle_info

Hai*_*ito 3 macros metaprogramming elixir

我试图围绕ExIrc做一个小包装,但我遇到了一些问题.__using__宏将ast附加到模块的开头,我想在默认情况下将函数定义附加到它的最后handle_info.我可以在使用该包装器的每个模块中手动完成它,但我更确定在某些时候我会忘记它.

我当前的包装器实现:

defmodule Cgas.IrcServer do
  defmacro __using__(opts) do
    quote do
      use GenServer
      alias ExIrc.Client
      require Logger
      defmodule State do
        defstruct host: "irc.chat.twitch.tv",
          port: 6667,
          pass: unquote(Keyword.get(opts, :password, "password")),
          nick: unquote(Keyword.get(opts, :nick, "uname")),
          client: nil,
          handlers: [],
          channel: "#cohhcarnage"
      end
      def start_link(client, state \\ %State{}) do
        GenServer.start_link(__MODULE__, [%{state | client: client}])
      end
      def init([state]) do
        ExIrc.Client.add_handler state.client, self
        ExIrc.Client.connect! state.client, state.host, state.port
        {:ok, state}
      end
      def handle_info({:connected, server, port}, state) do
        Logger.debug(state.nick <> " " <> state.pass)
        Client.logon(state.client, state.pass, state.nick, state.nick, state.nick)
        {:noreply, state}
      end
      def handle_info(:logged_in, config) do
        Client.join(config.client, config.channel)
        {:noreply, config}
      end
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

以及使用它的示例模块:

defmodule Cgas.GiveAwayMonitor do
  use Cgas.IrcServer,
    nick: "twitchsniperbot",
    password: "token"
  require Logger
  def handle_info({_type, msg, %ExIrc.SenderInfo{user: "cohhilitionbot"} , _channel}, state) do
    if String.downcase(msg) |> String.contains?("giveaway") do
      IO.inspect msg
    end
    {:noreply, state}
  end
end
Run Code Online (Sandbox Code Playgroud)

在它的当前状态下,它迟早会因为我不关心的IRC随机消息而崩溃.

我需要在文件末尾追加一些东西以处理所有随机情况:

def handle_info(_msg, state) do
  {:noreply, state}
end
Run Code Online (Sandbox Code Playgroud)

Dog*_*ert 5

你可以handle_info@before_compile钩子中注入catch-all :

@before_compile

在编译模块之前将调用的钩子.

接受模块或元组{<module>, <function/macro atom>}.函数/宏必须采用一个参数:模块环境.如果它是一个宏,它的返回值将在编译开始之前在模块定义的末尾注入.

仅提供模块时,假定函数/宏为 __before_compile__/1.

注意:不同于@after_compile,回调函数/宏必须放置在一个单独的模块(因为被调用的回调时,当前模块尚不存在).

defmodule A do
  defmacro __before_compile__(_env) do
    quote do
      def hello, do: "world"
    end
  end
end

defmodule B do
  @before_compile A
end
Run Code Online (Sandbox Code Playgroud)

资源

例:

defmodule MyGenServer do
  defmacro __using__(_) do
    quote do
      use GenServer
      @before_compile MyGenServer

      def start_link do
        GenServer.start_link(__MODULE__, [])
      end
    end
  end

  defmacro __before_compile__(_) do
    quote do
      def handle_info(message, state) do
        IO.inspect {:unknown_message, message}
        {:noreply, state}
      end
    end
  end
end

defmodule MyServer do
  use MyGenServer

  def handle_info(:hi, state) do
    IO.inspect {:got, :hi}
    {:noreply, state}
  end
end

{:ok, pid} = MyServer.start_link
send(pid, :hi)
send(pid, :hello)
:timer.sleep(100)
Run Code Online (Sandbox Code Playgroud)

输出:

{:got, :hi}
{:unknown_message, :hello}
Run Code Online (Sandbox Code Playgroud)