I’m currently trying to figure out how to handle a send(self(), ...) call in another Module within THAT module?
Let me elaborate. My setup would be the following:
defmodule Module.A do
use Phoenix.LiveView # Could also be Phoenix.Socket since they have the same API, I believe
alias Module.B
def handle_event("do_stuff", socket) do
B.send_to_self(socket)
end
end
defmodule Module.B do
def send_to_self(socket) do
send(self(), :my_event)
{:no_reply, socket}
end
def handle_info(:my_event, socket) do
IO.puts("Event handled!")
{:no_reply, socket}
end
end
So, from Module.A I want to call a function of Module.B, which sends a Kernel message to the same process that Module.A is running in. However, instead of writing the handle_info/2 handler into the Module.A module, I want to keep everything in Module.B in order to make Module.B reusable. Unfortunately, when I run the setup, I receive the error: function Module.A.handle_info/2 is undefined or private. Do you know how to solve this Error, please?
Either you do this manually (you usually do not want to do not want to do this in “managed” processes), or if you have a managed process, it will eventually receive that message and call the registered callback for that case.
I do not use LiveView, but my assumption here is, that it is similar to a GenServer and therefore received the message you didn’t selectively took of the queue earlier. So it will try to call is associated modules handle_info/2 callback, which you haven’t defined. Obviously.
Remember though, that when you do a “manual” receive, your process gets blocked and wont do anything until it receives a message matching the pattern.
defmodule Module.A do
use Phoenix.LiveView # Could also be Phoenix.Socket since they have the same API, I believe
alias Module.B
def handle_event("do_stuff", socket) do
B.send_to_self(socket)
end
def handle_info(msg, state) do
B.handle_info(msg, state)
end
# or defdelegate handle_info(msg, socket), to: B
end
Phoenix.LiveView is the behaviour module - your Module.A is the callback module. All callbacks required by the behaviour module must be satisfied by the callback module - it follows that there can only be one callback module per behaviour implementation.
Therefore any “reusable” code has to be called explicitly from the callback module. To that end the defdelegate/2 syntax sugar could be helpful.
Thanks for the tip about receive. If possible, I’d like to use a managed process indeed since I don’t want to block the receiving process. I’ll look more into how Phoenix.Channels handles this.
Oh, I see! That makes sense! Thank you! I guess that for now, the only option would be to delegate the functions to Module.B with defdelegate/2 then. I’ll try that althought it isn’t really pretty unfortunately But thanks!
Not a recommended practice as with OTP behaviours the receive is typically handled by the OTP behaviour module. The implementor of the callback module should focus on implementing the callbacks.
Thanks all for your lightning fast responses! I ran into this problem while developing a State Management library for Phoenix LiveView. I made another topic about its first implementation here. Feel free to check my implementation there as well