I’m trying to create a (very) basic GenStage for Elixir 1.7.4 and GenStage 0.14.
I wan to have 3 steps: P -> PC -> C
This is for a very important app (eventually), so I don’t want to use “hello world” type code, but all the examples I’m finding use Supervisor.Spec which is deprecated, and I’m not sure how to do the alternative.
Here is my code so far (lizard_station.exs):
defmodule LizardStation do
def start(_type, _args) do
import Supervisor.Spec
children = [
worker(FCM.Producer, [0])
# worker(FCM.ProducerConsumer, []),
# worker(FCM.Consumer, [])
]
Supervisor.start_link(children,
strategy: :one_for_one,
name: FCM.Supervisor
)
end
end
and mix.exs
defmodule LizardStation.MixProject do
use Mix.Project
def project do
[
app: :lizard_station,
version: "0.1.0",
elixir: "~> 1.7",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
def application do
[
extra_applications: [:logger]
]
end
defp deps do
[
{:gen_stage, "~> 0.14"}
]
end
end
defmodule LizardStation do
use Application
def start(_type, _args) do
children = [
{FCM.Producer, [0]},
FCM.ProducerConsumer,
FCM.Consumer
]
Supervisor.start_link(children, strategy: :one_for_one, name: FCM.Supervisor)
end
end
might work. That is, if you only needed to get rid of worker/2.
Ok, that seems to work… is it possible to explain in a nutshell what the difference was with Supervisor.Spec and whatever this new way is? Everything I read is a bit confusing. Maybe a 2000 foot view would help?
It’s my understanding that when my app boots, its creating a supervisor to monitor my 3 genstage servers, and also passing in their initial states. correct? What is the old way vs new way?
Nothing has changed in the Supervisor itself. What has changed [in Elixir syntax] is how supervisor initializes the children. There is no need to tell the supervisor what kind of child is it—that info might be easily borrowed from the child itself. That said, instead if calling worker—yes, Elixir is open source—we might just provide a child spec which already defines that this chils is a worker (or supervisor.)
Do I have to call start() myself somewhere? I don’t think it’s running because its not showing up in :observer
Btw I’m using iex -S mix
EDIT: I fixed this problem… i needed to just generate a new project iwth --sup option and examine the new application.ex file and folder structure. I get it now
Follow up question, it says the tuple has to have 3 arguments @idi527 … i assume I can just put [] but I want to write idiomatic elixir so im not sure the proper way to handle that.
children = [
{Consumer, []}, # doesnt need any args, but it forces me to put it
...
]
Basically it’s always going to force something in there… so I can see passing it [] but then I have to make ‘fake’ arguments and whatnot in the function itself… something like this I guess? It doesnt seem ‘right’ though
def start_link(_) do
GenStage.start_link(FeedAggregator, [], name: FeedAggregator)
end
I can see this happening, where a callback requires an argument but you dont want to give it one, and you need a placeholder…so i want the ‘best’ way to do it?
As a sidenote, what is the idiomatic elixir way to write a piece of code that doesnt matter when its used in an expression, like here? I tried using _ but it says I cant use that. i think :state_does_not_matter is stupid
well nothign is broken, Im just wondering “the elixir way” to deal with a callback that has no argument but you’re required to supply one. In tutorials people write :this_doesnt_matter which is stupid
This is how I currently have it which I dont like: (the _state and the [])
def start_link(_state) do
GenStage.start_link(__MODULE__, [], name: __MODULE__)
end
and:
children = [
{Producer, 0},
{ProducerConsumer, []},
{Consumer, []}
]
I could also imagine doing it like…
:ok and :ok
_state and []
[] and []
etc etc… I just dont know which convention people choose for clean code
If state does not matter, the most idiomatic way I’m aware of, is to simply use nil or the empty list ([]) or the empty map (%{}).
The important thing is, that you still bind and pass the state around in your handlers, as if it were an actual thing but that you do not touch. That way its much easier to make it a thing later on.
Well… I installed the romeo XMPP library package, and I can see how Elixir autostarted it.
But how is this different from how I started Redis under my own application supervisor by putting it in children = []
Doesn’t this mean romeo is not supervised, and could crash and not restart? How do I know when to put stuff in children vs just letting elixir auto-load it
Each application has its own suppervision tree. So romeo ist supervised as well, at the same level as your application.
Some libraries are created in a way, to get (partially) integrated into your own supervision tree, as it makes configuring them easier, or having multiple instances with a different set of configuration.