elixirerlang-otpgen-event

Launching multiple instances of the same event handler in Elixir


I have a simple event handler in elixir using GenEvent:

defmodule myHandler do
    use GenEvent
    #Callback
    def handle_event {:message, x}, state do
        IO.puts("Message value is #{x}")
        {:ok, [x|state]}
    end
end

I can start one handler and a manager in the usual way:

{:ok, mgr} = GenEvent.start_link

myServer.start_link(mgr)

GenEvent.add_handler(mgr,myHandler, [])

However, I would like to start a supervision tree where there are N handlers, each with a different id, using the same manager.

I tried:

Gen.Event.add_handler({mgr, :id1},myHandler, [])

, with no luck! In stead I get the following error:

** (Mix) Could not start application : exited in: myApp.start(:normal, [])
** (EXIT) no connection to :id1

I'm a newbie to Elixir and so am struggling with the documentation a bit. I'd be grateful if someone can show me how! Thanks.


Solution

  • You can always have a more complex state in MyHandler:

    defmodule MyHandler do
      use GenEvent
    
      def handle_event({:message, id, message}, {id, messages}) do
        IO.puts "[ID: #{inspect id}] Message value is #{inspect message}."
        {:ok, {id, [message | messages]}}
      end
    
      def handle_event(_, state) do
        {:ok, state}
      end
    end
    

    To filter messages by id, I would change the message structure to:

    {:message, id, message}
    

    If you don't do this, every handler will print the same message. I guess that's why you want the ID.

    Then having an id, you could do something like:

    {:ok, manager} = GenEvent.start_link
    MyServer.start_link manager
    GenEvent.add_handler manager, MyHandler, {id, []}
    

    As you can see the new state is {id :: atom, messages :: list} instead of a simple list of messages.

    Then its just a matter of sending the message:

    GenServer.sync_notify manager, {:message, id, message}
    

    Example:

    Initialize the manager:

    iex(1)>  {:ok, manager} = GenEvent.start_link
    {:ok, #PID<0.75.0>}
    

    Add the handler:

    iex(2)> GenEvent.add_handler manager, MyHandler, {:id0, []}
    :ok
    

    Test a message with ID :id0 and prints the message:

    iex(3)> GenEvent.sync_notify manager, {:message, :id0, "Hello"} 
    [ID: :id0] Message value is "Hello".
    :ok
    

    Test a message with the inexistent ID :id1 and it doesn't print anything:

    iex(4)> GenEvent.sync_notify manager, {:message, :id1, "Hello"}
    :ok
    

    There you go. I hope this helps :)

    P.S: If your state is too complex, you can always use a map:

    %{id: id, messages: []}