Create Phoenix Channel in a runtime

Hi folks,
is it possible to create a Channel dynamically, in a runtime, instead of defining it in the compile-time in Socket?

2 Likes

What are you hoping to accomplish? Let’s start there and see if what you are trying to do is feasible.

1 Like

Hi @chrismccord,
I am building an extension library to Phoenix (Drab), which creates its own Socket and Channel to communicate with the browser. Drab depends on Phoenix, can’t live standalone. There is an idea (by @OvermindDL1, here, to allow users to add their own channels in the Drab Socket, because some weird corpo proxies don’t allow to have multiple sockets.

So far, I’ve tried to accomplish it with the configuration, so in the Phoenix Application which uses Drab, you can do:

config :drab, 
  additional_channels: [{"mychannel:*", MyApp.Channel}]

And Drab.Socket read it and add users channel in a compile time:

Application.get_env(:drab, :additional_channels, []) |> Enum.map(fn {name, module} ->
  channel name, module
end)

This does work, but obviously does not follow the changes in config.exs: after each change I must force recompilation of Drab.Socket.

This is why I am thinking about adding the Channel dynamically.

Also, I’ve been thinking about using @external_resouce (the idea I learned from your book) - as setting it in the Drab.Socket, so it would be recompiled after any change of the configs in parent application. It works when I explicitly use the full path to the config.exs. But of course there are more config files, the user can put my config in dev.exs or so. May I set the @external_resource to a list of files? And how to get a list of config files with the full paths? Mix.Project.config_files shows some of them as a relative:

iex(6)> Mix.Project.config_files              
["/Users/grych/Dropbox/elixir/phoenix/drabrella/apps/drab_poc/_build/dev/lib/drab_poc/.compile.lock",
 "/Users/grych/Dropbox/elixir/phoenix/drabrella/apps/drab_poc/mix.exs",
 "config/config.exs", "config/dev.exs", "config/prod.exs",
 "config/prod.secret.exs", "config/test.exs"]
1 Like

I think it would be better and easier to go the other way. Allow users to add Drab to their own socket(s) as needed, For example:

defmodule MyApp.UserSocket do

  channel "my:whatever", MyApp.WhateverChannel
  channel "drab:*", Drab.Channel

  def connect(%{"drab_return" => _} = drab_params, socket) do
    Drab.Socket.connect(drab_params)
  end

  def connect(params, socket) do
    {:ok, assign(socket, :my_stuff, stuff)}
  end
end

No special work for end-users or drab, and just works. Users can apply their own authentication as well on top of drab if needed, combine into a single connect, or whatever else they need.

4 Likes

Thanks @chrismccord for an idea, I will strongly consider it.

But in this case, there is an additional work for end-users. They’ll have to create a Socket.

My original idea was to keep Drab easy to install/configure. That’s why the only requirement on the server-side is to

use Drab.Endpoint

in the user’s endpoint. Thus, your application doesn’t really care about sockets, channels - you just write an Elixir code in the Drab Commander. The idea of additional channels is rather an exception, and in fact it is against a Drab philosophy for keeping everything on the server side.

1 Like

All phoenix projects have a user_socket.ex generated. 99% of users will have one :), and it will be mounted int their endpoint already so they wouldn’t even have to use Drab.

.[quote=“grych, post:5, topic:3887”]
The idea of additional channels is rather an exception, and in fact it is against a Drab philosophy for keeping everything on the server side.
[/quote]

For the “additional channels” case, my solution is definitely the way to go vs compile time injection based off config. I would go further and make this explicit channel + connect approach the way to use drab as it’s clearer, and nearly the same effort for end-users. They edit one file, and add 4 LOC.

4 Likes

You are absolutely right. There is no point for me to create my own, Drab, socket. I need only a channel for that! Thanks a lot, appreciated.

2 Likes

Exactly this, this is what I thought I mentioned in the github issue but I may not have been clear. ^.^

No biggie, even the default templates give you one to start with, and there is a template generator for making more too, it is no issue. :slight_smile:

2 Likes

Yep, now I got it, I was stuck in a different approach. Discussion may solve may issues!

2 Likes

@chrismccord, just for a curiosity and for a future: is it possible to track multiple files with @external_resource?

1 Like

yup, external_resource is an accumulated attribute so you can register as many files as you’d like

2 Likes

Back to the original question, I plan on spawning little mini live auctions on the fly. I thought I would create a supervisor for each seller as they take an auction live, and have it watch dynamically created auctions(channels) as they come to be.

Does this fit the original scenario? Is it a valid approach in Phoenix/Elixir? Thanks, I’m new to the Elixir world, if that helps .

My early take is that phx pub/sub will handle the messaging and I will have to build a supervision tree to manage shared state? …many miles before I sleep.